ExpressFeeService.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <?php
  2. namespace addons\TinyShop\services\common;
  3. use common\helpers\BcHelper;
  4. use Yii;
  5. use yii\web\UnprocessableEntityHttpException;
  6. use common\components\Service;
  7. use common\enums\StatusEnum;
  8. use common\helpers\ArrayHelper;
  9. use common\models\member\Address;
  10. use common\helpers\StringHelper;
  11. use addons\TinyShop\common\models\common\ExpressFee;
  12. use addons\TinyShop\common\enums\ProductShippingTypeEnum;
  13. /**
  14. * Class ExpressFeeService
  15. * @package addons\TinyShop\services\common
  16. * @author jianyan74 <751393839@qq.com>
  17. */
  18. class ExpressFeeService extends Service
  19. {
  20. /**
  21. * 获取运费
  22. *
  23. * @param $defaultProducts
  24. * @param array $fullProductIds 包邮id数组
  25. * @param $company_id
  26. * @param Address $address
  27. * @return float|int
  28. * @throws UnprocessableEntityHttpException
  29. */
  30. public function getPrice($defaultProducts,array $fullProductIds, $company_id, $merchant_id, Address $address, $is_logistics)
  31. {
  32. // 查询用户是否选择物流
  33. if (!empty($is_logistics)) {
  34. return $this->getSameExpressSkuListFee($defaultProducts, $fullProductIds, $company_id, $address);
  35. }
  36. // 根据商品获取物流公司组别
  37. if (!empty($companies = $this->getSkuExpressGroup($defaultProducts, $merchant_id))) {
  38. $fee = 0;
  39. foreach ($companies as $company) {
  40. if (!empty($company['defaultProducts'])) {
  41. $same_fee = $this->getSameExpressSkuListFee($company['defaultProducts'], $fullProductIds, $company['id'], $address);
  42. if ($same_fee >= 0) {
  43. $fee += $same_fee;
  44. } else {
  45. throw new UnprocessableEntityHttpException('找不到物流');
  46. }
  47. }
  48. }
  49. return $fee;
  50. }
  51. throw new UnprocessableEntityHttpException('未设置物流');
  52. }
  53. /**
  54. * 获取相同运费模板运费情况
  55. *
  56. * @param $defaultProducts
  57. * @param array $fullProductIds 包邮id数组
  58. * @param $company_id
  59. * @param $address
  60. * @return float|int
  61. * @throws UnprocessableEntityHttpException
  62. */
  63. public function getSameExpressSkuListFee($defaultProducts, $fullProductIds, $company_id, $address)
  64. {
  65. $fee = 0;
  66. if (empty($defaultProducts)) {
  67. return $fee;
  68. }
  69. list ($weightProducts, $volumeProducts, $byNumProducts, $unifyProducts, $freeExpressProducts) = $this->getSkuGroup($defaultProducts, $fullProductIds);
  70. if (count($freeExpressProducts) === count($defaultProducts)) {
  71. return $fee;
  72. }
  73. if (!empty($weightProducts) || !empty($volumeProducts) || !empty($byNumProducts)) {
  74. $template = $this->getTemplate($company_id, $address);
  75. // 计算称重方式运费
  76. $weightProductsFee = $this->getWeightFee($template, $weightProducts);
  77. $fee += $weightProductsFee;
  78. // 计算体积方式运费
  79. $volumeProductsFee = $this->getVolumeFee($template, $volumeProducts);
  80. $fee += $volumeProductsFee;
  81. // 计件方式计算运费
  82. $byNumProductsFee = $this->getByNumFee($template, $byNumProducts);
  83. $fee += $byNumProductsFee;
  84. }
  85. // 统一计算邮费
  86. $unifyProductsFee = $this->getUnifyFee($unifyProducts);
  87. $fee += $unifyProductsFee;
  88. return $fee;
  89. }
  90. /**
  91. * @param array $defaultProducts
  92. * @param array $fullProductIds 包邮id数组
  93. * @return array
  94. */
  95. public function getSkuGroup(array $defaultProducts, $fullProductIds = [])
  96. {
  97. $weightProducts = [];
  98. $volumeProducts = [];
  99. $byNumProducts = [];
  100. $unifyProducts = [];
  101. $freeExpressProducts = [];
  102. foreach ($defaultProducts as $defaultProduct) {
  103. // 包邮商品
  104. if (in_array($defaultProduct['id'], $fullProductIds)) {
  105. $freeExpressProducts[] = $defaultProduct;
  106. continue;
  107. }
  108. // 包邮
  109. if ($defaultProduct['shipping_type'] == ProductShippingTypeEnum::FULL_MAIL) {
  110. $freeExpressProducts[] = $defaultProduct;
  111. continue;
  112. }
  113. // 买家承担运费
  114. if ($defaultProduct['shipping_type'] == ProductShippingTypeEnum::USER_PAY) {
  115. switch ($defaultProduct['shipping_fee_type']) {
  116. case 1; // 计件
  117. $byNumProducts[] = $defaultProduct;
  118. break;
  119. case 2; // 体积
  120. $volumeProducts[] = $defaultProduct;
  121. break;
  122. case 3; // 重量
  123. $weightProducts[] = $defaultProduct;
  124. break;
  125. }
  126. continue;
  127. }
  128. // 统一运费(买家承担运费)
  129. if ($defaultProduct['shipping_type'] == ProductShippingTypeEnum::FIXATION) {
  130. $unifyProducts[] = $defaultProduct;
  131. }
  132. }
  133. return [
  134. $weightProducts,
  135. $volumeProducts,
  136. $byNumProducts,
  137. $unifyProducts,
  138. $freeExpressProducts,
  139. ];
  140. }
  141. /**
  142. * 根据地址获取运费模板
  143. *
  144. * @param $company_id
  145. * @param $address
  146. * @return array|mixed|\yii\db\ActiveRecord
  147. * @throws UnprocessableEntityHttpException
  148. */
  149. private function getTemplate($company_id, $address)
  150. {
  151. // 所有运费信息
  152. $fees = $this->findByCompanyId($company_id);
  153. // 检测城市是否有区概念
  154. $count = Yii::$app->services->provinces->getCountByPid($address['city_id']);
  155. $temp = [];
  156. $default = [];
  157. foreach ($fees as $v) {
  158. if ($v['is_default'] == StatusEnum::ENABLED) {
  159. $default = $v;
  160. }
  161. if ($count == 0) {
  162. if (!empty($v['city_ids'])) {
  163. $cityIds = explode(',', $v['city_ids']);
  164. in_array($address['city_id'], $cityIds) && $temp = $v;
  165. }
  166. } else {
  167. $areaIds = explode(',', $v['area_ids']);
  168. in_array($address['area_id'], $areaIds) && $temp = $v;
  169. }
  170. }
  171. // 如果模板为空,找到默认模板
  172. if (!empty($temp)) {
  173. return $temp;
  174. }
  175. if (!empty($default)) {
  176. $temp = $default;
  177. return $temp;
  178. }
  179. throw new UnprocessableEntityHttpException('该地址不支持配送');
  180. }
  181. /**
  182. * 商品邮费的sku分组
  183. *
  184. * @param $defaultProducts
  185. * @return array|string|\yii\db\ActiveRecord[]
  186. */
  187. public function getSkuExpressGroup($defaultProducts, $merchant_id)
  188. {
  189. // 获取所有物流公司
  190. if (empty($companies = Yii::$app->tinyShopService->expressCompany->getList($merchant_id))) {
  191. return '';
  192. }
  193. // 获取默认物流公司
  194. if (empty($defaultCompany = Yii::$app->tinyShopService->expressCompany->getDefault($merchant_id))) {
  195. $defaultCompany = $companies[0];
  196. }
  197. // 兼容统一运费
  198. $count = count($companies);
  199. $companies[$count] = [
  200. 'id' => 0
  201. ];
  202. foreach ($companies as $key => $company) {
  203. $companies[$key]['defaultProducts'] = [];
  204. foreach ($defaultProducts as $k => $value) {
  205. // 买家承担运费
  206. if ($value['shipping_type'] == ProductShippingTypeEnum::USER_PAY) {
  207. // 商品未设置物流公司
  208. if ($value['shipping_fee_id'] == 0) {
  209. $company['id'] == $defaultCompany['id'] && $companies[$key]['defaultProducts'][] = $value;
  210. } else {
  211. $company['id'] == $value['shipping_fee_id'] && $companies[$key]['defaultProducts'][] = $value;
  212. }
  213. } elseif ($value['shipping_type'] == ProductShippingTypeEnum::FIXATION) {
  214. $companies[$count]['defaultProducts'][] = $value;
  215. }
  216. }
  217. }
  218. return $companies;
  219. }
  220. /**
  221. * 计算称重方式运费总和
  222. *
  223. * @param $template
  224. * @param $products
  225. * @return float|int
  226. * @throws UnprocessableEntityHttpException
  227. */
  228. private function getWeightFee($template, $products)
  229. {
  230. $weight = 0;
  231. if (empty($products)) {
  232. return $weight;
  233. }
  234. // 不支持配送
  235. if ($template['weight_is_use'] == 0) {
  236. throw new UnprocessableEntityHttpException('不支持配送');
  237. }
  238. // 计算总重量
  239. foreach ($products as $v) {
  240. $weight += $v['product_weight'] * $v['number'];
  241. }
  242. if ($weight <= 0) {
  243. return 0;
  244. }
  245. if ($weight <= $template['weight_snum']) {
  246. return $template['weight_sprice'];
  247. }
  248. // 开始计算
  249. $ext_weight = $weight - $template['weight_snum'];
  250. if ($template['weight_xnum'] == 0) {
  251. $template['weight_xnum'] = 1;
  252. }
  253. if (($ext_weight * 100) % ($template['weight_xnum'] * 100) == 0) {
  254. $ext_data = $ext_weight / $template['weight_xnum'];
  255. } else {
  256. $ext_data = floor($ext_weight / $template['weight_xnum']) + 1;
  257. }
  258. return $template['weight_sprice'] + $ext_data * $template['weight_xprice'];
  259. }
  260. /**
  261. * 计算体积方式运费总和
  262. *
  263. * @param $template
  264. * @param $products
  265. * @return float|int
  266. * @throws UnprocessableEntityHttpException
  267. */
  268. private function getVolumeFee($template, $products)
  269. {
  270. $volume = 0;
  271. if (empty($products)) {
  272. return 0;
  273. }
  274. if ($template['volume_is_use'] == 0) {
  275. throw new UnprocessableEntityHttpException('不支持配送');
  276. }
  277. foreach ($products as $k => $v) {
  278. // 计算总重量
  279. $volume += $v['product_volume'] * $v['number'];
  280. }
  281. if ($volume <= 0) {
  282. return 0;
  283. }
  284. if ($volume <= $template['volume_snum']) {
  285. return $template['volume_sprice'];
  286. } else {
  287. $ext_volume = $volume - $template['volume_snum'];
  288. if ($template['volume_xnum'] == 0) {
  289. $template['volume_xnum'] = 1;
  290. }
  291. if (($ext_volume * 100) % ($template['volume_xnum'] * 100) == 0) {
  292. $ext_data = $ext_volume / $template['volume_xnum'];
  293. } else {
  294. $ext_data = floor($ext_volume / $template['weight_xnum']) + 1;
  295. }
  296. return $template['volume_sprice'] + $ext_data * $template['volume_xprice'];
  297. }
  298. }
  299. /**
  300. * 计算计件方式运费总和
  301. *
  302. * @param $template
  303. * @param $products
  304. * @return float|int
  305. * @throws UnprocessableEntityHttpException
  306. */
  307. private function getByNumFee($template, $products)
  308. {
  309. $num = 0;
  310. if (empty($products)) {
  311. return 0;
  312. }
  313. if ($template['bynum_is_use'] == 0) {
  314. throw new UnprocessableEntityHttpException('不支持配送');
  315. }
  316. foreach ($products as $k => $v) {
  317. // 计算总数量
  318. $num += $v['number'];
  319. }
  320. if ($num <= 0) {
  321. return 0;
  322. }
  323. if ($num <= $template['bynum_snum']) {
  324. return $template['bynum_sprice'];
  325. } else {
  326. $ext_num = $num - $template['bynum_snum'];
  327. if ($template['bynum_xnum'] == 0) {
  328. $template['bynum_xnum'] = 1;
  329. }
  330. if ($ext_num % $template['bynum_xnum'] == 0) {
  331. $ext_data = $ext_num / $template['bynum_xnum'];
  332. } else {
  333. $ext_data = floor($ext_num / $template['bynum_xnum']) + 1;
  334. }
  335. return $template['bynum_sprice'] + $ext_data * $template['bynum_xprice'];
  336. }
  337. }
  338. /**
  339. * 统一邮费
  340. *
  341. * @param $products
  342. * @return int|void
  343. */
  344. private function getUnifyFee($products)
  345. {
  346. $num = 0;
  347. if (empty($products)) {
  348. return 0;
  349. }
  350. foreach ($products as $v) {
  351. $num = BcHelper::add($num, $v['shipping_fee']);
  352. }
  353. if ($num <= 0) {
  354. return 0;
  355. }
  356. return $num;
  357. }
  358. /**
  359. * 获取不可选数据
  360. *
  361. * @param $company_id
  362. * @return array
  363. */
  364. public function getNotChoose($company_id)
  365. {
  366. $models = ExpressFee::find()
  367. ->where(['status' => StatusEnum::ENABLED, 'is_default' => 0, 'company_id' => $company_id])
  368. ->select(['province_ids', 'city_ids', 'area_ids'])
  369. ->asArray()
  370. ->all();
  371. $allProvinceIds = $allCityIds = $allAreaIds = [];
  372. foreach ($models as $model) {
  373. if (!empty($province = StringHelper::parseAttr($model['province_ids']))) {
  374. $allProvinceIds = ArrayHelper::merge($allProvinceIds, $province);
  375. }
  376. if (!empty($city = StringHelper::parseAttr($model['city_ids']))) {
  377. $allCityIds = ArrayHelper::merge($allCityIds, $city);
  378. }
  379. if (!empty($area = StringHelper::parseAttr($model['area_ids']))) {
  380. $allAreaIds = ArrayHelper::merge($allAreaIds, $area);
  381. }
  382. }
  383. return [$allProvinceIds, $allCityIds, $allAreaIds];
  384. }
  385. /**
  386. * 判断是否默认的物流
  387. *
  388. * @param $company_id
  389. * @return array|null|\yii\db\ActiveRecord
  390. */
  391. public function findDefaultFee($company_id)
  392. {
  393. return ExpressFee::find()
  394. ->where(['status' => StatusEnum::ENABLED, 'is_default' => true])
  395. ->andWhere(['company_id' => $company_id])
  396. ->one();
  397. }
  398. /**
  399. * 获取列表
  400. *
  401. * @return array|\yii\db\ActiveRecord[]
  402. */
  403. public function findByCompanyId($company_id)
  404. {
  405. return ExpressFee::find()
  406. ->where(['company_id' => $company_id, 'status' => StatusEnum::ENABLED])
  407. ->asArray()
  408. ->all();
  409. }
  410. }
粤ICP备19079148号