ExpressTemplateService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. namespace addons\WechatVideoShop\common\service;
  3. use addons\WechatVideoShop\common\expand\sdk\weixin\src\model\express\ExpressAllFreightCalcMethodModel;
  4. use addons\WechatVideoShop\common\expand\sdk\weixin\src\model\express\ExpressConditionFreeDetailModel;
  5. use addons\WechatVideoShop\common\expand\sdk\weixin\src\model\express\ExpressFreightCalcMethodModel;
  6. use addons\WechatVideoShop\common\expand\sdk\weixin\src\model\express\ExpressNotSendAreaModel;
  7. use addons\WechatVideoShop\common\expand\sdk\weixin\src\model\express\ExpressTemplateModel;
  8. use addons\WechatVideoShop\common\expand\sdk\weixin\src\model\express\FreightTemplateModel;
  9. use addons\WechatVideoShop\common\expand\sdk\weixin\src\model\user\AddressInfoModel;
  10. use addons\WechatVideoShop\common\expand\sdk\weixin\src\request\express\ExpressAddRequest;
  11. use addons\WechatVideoShop\common\expand\sdk\weixin\src\request\express\ExpressUpdateRequest;
  12. use addons\WechatVideoShop\common\helper\WechatRequestHelper;
  13. use addons\WechatVideoShop\common\models\WechatVideoShopExpressTemplate;
  14. use addons\WechatVideoShop\common\models\WechatVideoShopRegion;
  15. use common\enums\StatusEnum;
  16. use common\models\goods\FreightRules;
  17. use common\models\goods\GoodsAttr;
  18. use yii\helpers\Json;
  19. class ExpressTemplateService
  20. {
  21. /**
  22. * @param int $mall_id
  23. * @param int $is_free
  24. * @param array $goods
  25. * @param array $config
  26. * @param int $shop_id
  27. * @param null $third_template_id
  28. * @return string|WechatVideoShopExpressTemplate
  29. */
  30. public function create(int $mall_id, int $is_free, array $goods, array $config, int $shop_id, $third_template_id = null)
  31. {
  32. try {
  33. $templateModel = new FreightTemplateModel();
  34. // 计费类型,PIECE:按件数,WEIGHT:按重量
  35. $templateModel->valuation_type = FreightTemplateModel::VALIDATION_TYPE_PRICE;
  36. $templateModel->name = $is_free == StatusEnum::ENABLED ? '包邮模板' : "商品 {$goods['id']} 运费模板" . date('Y-m-d H:i:s', time()); // 模板名称
  37. $templateModel->send_time = FreightTemplateModel::SEND_TIME_FOUTYEIGHT_HOUR; // 发货时间期限
  38. $templateModel->delivery_type = FreightTemplateModel::DELIVERY_TYPE_EXPRESS; // 计费方式:FREE:包邮CONDITION_FREE:条件包邮NO_FREE:不包邮
  39. // 发货地址
  40. $templateModel->address_info = $this->getShipAddressInfo($mall_id, $config);
  41. // 不发货区域
  42. if (!empty($goods['is_area_limit']) && !empty($goods['area_limit'])) {
  43. $templateModel->not_send_area = $this->getNotSendArea($goods['area_limit']);
  44. }
  45. if ($is_free) {
  46. $templateModel->shipping_method = FreightTemplateModel::SHIPPING_METHOD_FREE; // 运输方式,EXPRESS:快递
  47. } else {
  48. // 条件包邮详情 暂时没有条件包邮
  49. // $templateModel->all_condition_free_detail = '';
  50. // 具体计费方法,默认运费,指定地区运费等 只有含有运费模板级使用运费模板的时候才需要
  51. if (!empty($goods['freight_id']) && $goods['freight_rules_type'] == StatusEnum::ENABLED) {
  52. $templateModel->shipping_method = FreightTemplateModel::SHIPPING_METHOD_CONDITION_FREE;
  53. $freight = FreightRules::getOne([
  54. ['mall_id' => $goods['mall_id']],
  55. ['id' => $goods['freight_id']]
  56. ]);
  57. // 计费类型,PIECE:按件数,WEIGHT:按重量
  58. $templateModel->valuation_type = $freight->type == StatusEnum::ENABLED ?
  59. FreightTemplateModel::VALIDATION_TYPE_WEIGHT : FreightTemplateModel::VALIDATION_TYPE_PRICE;
  60. // 是否默认模板
  61. $templateModel->is_default = $freight->is_default == StatusEnum::ENABLED;
  62. $templateModel->all_freight_calc_method = $this->getAllFreightCalcMethod($freight);
  63. } else {
  64. // 自定义运费 - 所有地区不包邮
  65. $templateModel->is_default = false;
  66. $templateModel->shipping_method = FreightTemplateModel::SHIPPING_METHOD_CONDITION_FREE;
  67. $templateModel->all_condition_free_detail = $this->conditionFreeDetail($goods);
  68. $templateModel->all_freight_calc_method = $this->fixedExpressPrice($goods);
  69. }
  70. }
  71. $model = new ExpressTemplateModel();
  72. if (empty($third_template_id)) { // 新建
  73. $req = new ExpressAddRequest($config);
  74. } else {
  75. $req = new ExpressUpdateRequest($config);
  76. $templateModel->template_id = $third_template_id;
  77. }
  78. $model->freight_template = $templateModel;
  79. // dd($model->toArray());
  80. $t_hash = md5(json_encode($model->toArray()));
  81. // 查询一下
  82. $localExpressModel = WechatVideoShopExpressTemplate::findOne(['mall_id' => $mall_id, 't_hash' => $t_hash]);
  83. if (!empty($localExpressModel)) return $localExpressModel;
  84. $resp = WechatRequestHelper::accessTokenAndToArray($req, $model);
  85. if (empty($third_template_id) && empty($resp['template_id'])) {
  86. throw new \Exception('响应模板ID为空');
  87. }
  88. if (empty($third_template_id)) {
  89. $localExpressModel = new WechatVideoShopExpressTemplate();
  90. } else {
  91. $localExpressModel = WechatVideoShopExpressTemplate::getOne([
  92. ['mall_id' => $mall_id],
  93. ['third_template_id' => $third_template_id]
  94. ]);
  95. if (empty($localExpressModel)) throw new \Exception("微信运费模板 {$third_template_id} 不存在");
  96. }
  97. $localExpressModel->mall_id = $mall_id;
  98. $localExpressModel->template_id = $goods['freight_id'];
  99. $localExpressModel->third_template_id = $resp['template_id'] ?? $third_template_id;
  100. $localExpressModel->content = $model->toArray();
  101. $localExpressModel->is_free = $is_free;
  102. $localExpressModel->shop_id = $shop_id;
  103. $localExpressModel->goods_id = $goods['id'];
  104. $localExpressModel->t_hash = $t_hash;
  105. if (!$localExpressModel->save()) throw new \Exception($localExpressModel->getErrorMessage());
  106. return $localExpressModel;
  107. } catch (\Exception $e) {
  108. return "运费模板:{$e->getMessage()}";
  109. }
  110. }
  111. /**
  112. * 获取发货地址 - 可以在基础设置那边设置这一项
  113. * @return AddressInfoModel
  114. * @throws \Exception
  115. */
  116. protected function getShipAddressInfo(int $mall_id, array $config)
  117. {
  118. $model = new AddressInfoModel();
  119. $model->user_name = $config['contact_person'];
  120. $model->province_name = $config['province']; // 省份名称
  121. $model->city_name = $config['city']; // 城市名称
  122. $model->county_name = $config['district']; // 区县名称
  123. $model->detail_info = $config['address'];
  124. $model->tel_number = $config['phone'];
  125. return $model;
  126. }
  127. protected function getFreightDetail()
  128. {
  129. // $model = new Freight
  130. }
  131. /**
  132. * 获取不发货的区域
  133. * @param string $areas
  134. * @return ExpressNotSendAreaModel
  135. */
  136. protected function getNotSendArea(string $areas)
  137. {
  138. $model = new ExpressNotSendAreaModel();
  139. $areas = Json::decode($areas);
  140. $regions = $this->getAreaIds($areas);
  141. $list = [];
  142. if (!empty($regions)) {
  143. foreach ($regions as $region) {
  144. $tmp = $this->packAddrInfo($region);
  145. if ($tmp) $list[] = $tmp;
  146. }
  147. $model->address_infos = $list;
  148. }
  149. return $model;
  150. }
  151. protected function getAllConditionFreeDetail()
  152. {
  153. }
  154. /**
  155. * 具体计费方法,默认运费,指定地区运费等
  156. * @param FreightRules $freight
  157. * @return ExpressAllFreightCalcMethodModel
  158. * @throws \Exception
  159. */
  160. protected function getAllFreightCalcMethod(FreightRules $freight)
  161. {
  162. $model = new ExpressAllFreightCalcMethodModel();
  163. $list = [];
  164. if (empty($freight)) throw new \Exception("运费模板不存在");
  165. if (empty($freight->rule)) throw new \Exception("运费模板规则为空");
  166. $rules = Json::decode($freight->rule);
  167. if (empty($rules)) throw new \Exception("运费模板规则为空.");
  168. $firstPrice = 0;
  169. $secondPrice = 0;
  170. $first = 1;
  171. $second = 1;
  172. foreach ($rules as $key => $rule) {
  173. // if (empty($rule['list'])) continue;
  174. $regions = $this->getAreaIds($rule['list']);
  175. $addr_list = [];
  176. foreach ($regions as $region) {
  177. $tmp = $this->packAddrInfo($region);
  178. if ($tmp) $addr_list[] = $tmp;
  179. }
  180. if (!empty($addr_list)) {
  181. $expressFreightCalcMethodModel = new ExpressFreightCalcMethodModel();
  182. $expressFreightCalcMethodModel->first_val_amount = (float) $rule['first']; // 首重计算
  183. $expressFreightCalcMethodModel->first_price = (int) bcmul($rule['firstPrice'], 100); // 首重价格
  184. $expressFreightCalcMethodModel->second_val_amount = (float) $rule['second'];// 续重计算
  185. $expressFreightCalcMethodModel->second_price = (int) bcmul($rule['secondPrice'], 100);// 续重价格
  186. // if ($key == 0) $expressFreightCalcMethodModel->is_default = true; // 默认运费不能指定地区
  187. if ($rule['firstPrice'] > $firstPrice) {
  188. $firstPrice = $rule['firstPrice'];
  189. $secondPrice = $rule['secondPrice'];
  190. $first = $rule['first'];
  191. $second = $rule['second'];
  192. }
  193. $expressFreightCalcMethodModel->address_infos = $addr_list;
  194. $list[] = $expressFreightCalcMethodModel;
  195. }
  196. }
  197. // 设置一个默认运费 默认运费不能指定地区
  198. $expressFreightCalcMethodModel = new ExpressFreightCalcMethodModel();
  199. $expressFreightCalcMethodModel->first_val_amount = (float) $first; // 首重计算
  200. $expressFreightCalcMethodModel->first_price = (int) bcmul($firstPrice, 100); // 首重价格
  201. $expressFreightCalcMethodModel->second_val_amount = (float) $second;;// 续重计算
  202. $expressFreightCalcMethodModel->second_price = (int) bcmul($secondPrice, 100);// 续重价格
  203. $expressFreightCalcMethodModel->is_default = true;// 续重价格
  204. $list[] = $expressFreightCalcMethodModel;
  205. $model->freight_calc_method_list = $list;
  206. return $model;
  207. }
  208. /**
  209. * 统一运费
  210. * @param array $goods
  211. * @return ExpressAllFreightCalcMethodModel
  212. */
  213. protected function fixedExpressPrice(array $goods)
  214. {
  215. $model = new ExpressAllFreightCalcMethodModel();
  216. $expressFreightCalcMethodModel = new ExpressFreightCalcMethodModel();
  217. if (!empty($goods['shipping_fee'])) {
  218. $expressFreightCalcMethodModel->first_val_amount = 1; // 首重计算
  219. $expressFreightCalcMethodModel->first_price = (int) bcmul($goods['shipping_fee'], 100); // 首重价格
  220. $expressFreightCalcMethodModel->second_val_amount = 1;// 续重计算
  221. $expressFreightCalcMethodModel->second_price = 0;// 续重价格
  222. $expressFreightCalcMethodModel->is_default = true;// 续重价格
  223. // $expressFreightCalcMethodModel->delivery_id = "AJ";
  224. // $expressFreightCalcMethodModel->address_infos = [];
  225. }
  226. $model->freight_calc_method_list = [$expressFreightCalcMethodModel];
  227. return $model;
  228. }
  229. /**
  230. * @param array $goods
  231. * @return ExpressConditionFreeDetailModel
  232. */
  233. protected function conditionFreeDetail(array $goods)
  234. {
  235. $model = new ExpressConditionFreeDetailModel();
  236. $weight = bcmul(GoodsAttr::find()->where(['goods_id' => $goods['id']])
  237. ->andWhere(['status' => StatusEnum::ENABLED])
  238. ->orderBy('weight desc')->select('weight')->scalar(), 100) * $goods['free_shipping_num'];
  239. // free_shipping_num = 满件包邮
  240. // free_shipping_money = 满额包邮
  241. // shipping_fee = 运费金额
  242. // 如果为0 则不支持
  243. $model->min_weight = (float) (!empty($weight) ? $weight / 1000 : 20000000); // 最低重量KG
  244. $model->min_piece = (float) (!empty($goods['free_shipping_num']) ? $goods['free_shipping_num'] : 1000000); // 最低件数
  245. $model->min_amount = (int) !empty($goods['free_shipping_money']) ? bcmul($goods['free_shipping_money'], 100) : 1000000000; // 最低金额,单位(分)
  246. if (empty($model->min_amount)) $model->min_amount = 999999999;
  247. $model->valuation_flag = 1; // 计费方式对应的选项是否已设置
  248. $model->amount_flag = 1; // 金额是否设置
  249. $addr_list = [];
  250. $regions = WechatVideoShopRegion::lists([
  251. 'where' => [
  252. ['level' => 1],
  253. ['status' => 1]
  254. ]
  255. ]);
  256. foreach ($regions as $region) {
  257. $tmp = $this->packAddrInfo($region);
  258. if ($tmp) $addr_list[] = $tmp;
  259. }
  260. $model->address_infos = $addr_list; // 支持的地址列表
  261. return $model;
  262. }
  263. /**
  264. * 获取本地地区ID数组
  265. * @param array $areas
  266. * @return array|false|mixed
  267. */
  268. protected function getAreaIds(array $areas)
  269. {
  270. $regions = [];
  271. if (!empty($areas)) {
  272. $region_ids = array_column($areas, 'id');
  273. $regions = WechatVideoShopRegion::lists([
  274. 'where' => [
  275. ['local_id' => $region_ids],
  276. ['level' => 2],
  277. ['status' => StatusEnum::ENABLED]
  278. ]
  279. ]);
  280. }
  281. return $regions;
  282. }
  283. /**
  284. * 拼凑地址信息
  285. * @param array $region
  286. * @return AddressInfoModel
  287. */
  288. protected function packAddrInfo(array $region)
  289. {
  290. $addrModel = new AddressInfoModel();
  291. if ($region['level_two_name'] == '省直辖县级行政区划') return null;
  292. switch ($region['level']) {
  293. case 3:
  294. $addrModel->province_name = $region['level_one_name'];
  295. if ($region['level_one_name'] == $region['level_two_name']) { // 如果是直辖市
  296. $addrModel->city_name = $region['name'];
  297. } else {
  298. $addrModel->city_name = $region['level_two_name'];
  299. $addrModel->county_name = $region['name'];
  300. }
  301. break;
  302. case 2:
  303. $addrModel->city_name = $region['name'];
  304. $addrModel->province_name = $region['level_one_name'];
  305. break;
  306. case 1:
  307. $addrModel->province_name = $region['name'];
  308. break;
  309. }
  310. return $addrModel;
  311. }
  312. }