OrderService.php 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. <?php
  2. namespace addons\QiJuhe\common\service;
  3. use addons\QiJuhe\common\enums\OrderEnum;
  4. use addons\QiJuhe\common\enums\QiJuheEnum;
  5. use addons\QiJuhe\common\models\ExpressModel;
  6. use addons\QiJuhe\common\models\GoodsModel;
  7. use addons\QiJuhe\common\models\GoodsSkuModel;
  8. use addons\QiJuhe\common\models\MallGoodsModel;
  9. use addons\QiJuhe\common\models\MallOrderModel;
  10. use addons\QiJuhe\common\models\OrderModel;
  11. use addons\QiJuhe\common\models\SupplysModel;
  12. use common\enums\OrderStatusEnum;
  13. use common\enums\ShippingTypeEnum;
  14. use common\enums\StatusEnum;
  15. use common\enums\AddonsEnum;
  16. use common\logic\order\BaseOrderForm;
  17. use common\models\order\OrderDetail;
  18. use common\models\order\OrderExpress;
  19. use common\models\user\UserAddress;
  20. use addons\CardCoupons\common\models\CardCouponsGift;
  21. use common\models\goods\Goods;
  22. use Exception;
  23. use Yii;
  24. use yii\base\Model;
  25. use yii\data\Pagination;
  26. use yii\helpers\Json;
  27. class OrderService extends Model
  28. {
  29. /**
  30. * 收货人名称最低长度
  31. */
  32. public const NAME_MIN_LENGTH = 2;
  33. /**
  34. * 商城ID
  35. *
  36. * @var integer
  37. */
  38. public $mall_id;
  39. public $form;
  40. public function __construct($config)
  41. {
  42. parent::__construct($config);
  43. }
  44. /**
  45. * 获取列表
  46. *
  47. * @param array $params
  48. * @return void
  49. */
  50. public function getOrderList($params)
  51. {
  52. $limit = $params['limit'] ?? 20;
  53. $page = $params['page'] ?? 1;
  54. $model = OrderModel::find();
  55. $model->with(['mallOrder' => function ($query) {
  56. return $query->select('id, receiver_name as consignee, mobile, goods_price, pay_time, pay_money, status as order_status, order_source');
  57. }, 'mallOrderDetail' => function ($query) {
  58. return $query->select('order_id, num, goods_name, pic_url, goods_attr_name');
  59. }]);
  60. $model->andWhere(['mall_id' => $this->mall_id]);
  61. if (!empty($params['order_no'])) {
  62. $ids = MallOrderModel::find()->where(['mall_id' => $this->mall_id])->andWhere(['like', 'order_no', $params['order_no']])->select('id')->column();
  63. $model->andWhere(['in', 'order_id', $ids]);
  64. }
  65. if (!empty($params['user_id'])) {
  66. $ids = MallOrderModel::find()->where(['mall_id' => $this->mall_id])->andWhere(['=', 'user_id', $params['user_id']])->select('id')->column();
  67. $model->andWhere(['in', 'order_id', $ids]);
  68. }
  69. if (!empty($params['search_words'])) {
  70. $ids = OrderDetail::find()->where(['mall_id' => $this->mall_id])->andWhere(['like', 'goods_name', $params['search_words']])->select('order_id')->column();
  71. $model->andWhere(['in', 'order_id', $ids]);
  72. }
  73. if (!empty($params['push_status']) && $params['push_status'] > 0) {
  74. $model->andWhere(['=', 'status', $params['push_status']]);
  75. }
  76. if (!empty($params['order_status']) && $params['order_status'] > 0) {
  77. $ids = MallOrderModel::find()->where(['mall_id' => $this->mall_id])->andWhere(['=', 'status', $params['order_status']])->select('id')->column();
  78. $model->andWhere(['in', 'order_id', $ids]);
  79. }
  80. if (!empty($params['source']) && $params['source'] > 0) {
  81. $model->andWhere(['=', 'source', $params['source']]);
  82. }
  83. if (!empty($params['name'])) {
  84. $ids = MallOrderModel::find()->where(['mall_id' => $this->mall_id])->andWhere(['like', 'receiver_name', $params['name']])->select('id')->column();
  85. $model->andWhere(['in', 'order_id', $ids]);
  86. }
  87. if (!empty($params['supply_id'])) {
  88. $model->andWhere(['supply_id'=>$params['supply_id']]);
  89. }
  90. $pagination = new Pagination(['totalCount' => $model->count(), 'pageSize' => $limit]);
  91. $model->limit($pagination->limit)->offset($pagination->offset);
  92. $list = $model->orderBy('created_at desc')
  93. ->asArray()
  94. ->all();
  95. $model = SupplysModel::find()->select('id,app_name')->where(['mall_id' => $this->mall_id]);
  96. $supply_list = $model->indexBy('id')->asArray()->all();
  97. foreach ($list as &$item) {
  98. if(!empty($item['order_detail_id'])){
  99. $item['mallOrderDetail'] = OrderDetail::find()->where(['id'=>$item['order_detail_id']])->select('order_id, num, goods_name, pic_url, goods_attr_name')->asArray()->one();
  100. }
  101. // 推送状态
  102. $item['status_text'] = OrderEnum::getValue($item['status']) ?? '未知';
  103. $item['supply_name'] = isset($supply_list[$item['supply_id']]) ? $supply_list[$item['supply_id']]['app_name'] : "";
  104. }
  105. $data['list'] = $list;
  106. $data['page_count'] = $pagination->pageCount;
  107. $data['current_page'] = $page + 1;
  108. return $data;
  109. }
  110. /**
  111. * 聚合供应链生成订单前置检测
  112. *
  113. * @param BaseOrderForm $form 商城订单数据
  114. * @param booleam $is_new_record 是否创建订单
  115. * @return void
  116. */
  117. public function createBeforeCheck($form, $is_new_record = false)
  118. {
  119. $total_price = 0;
  120. $order_goods = $form->order_goods;
  121. // 获取购买的AttrId
  122. foreach ($order_goods as $goods) {
  123. $goods_id = $goods['goods_id'];
  124. $attr_id = $goods['goods_attr_id'];
  125. // 数量
  126. $num = $goods['num'];
  127. // 总价
  128. $total_price += $goods['goods_price'];
  129. }
  130. $sku = GoodsSkuModel::getSku($goods_id, $attr_id);
  131. $sku_id = $sku->sku_id;
  132. if (empty($sku_id)) {
  133. throw new Exception('下单商品SKU不存在,请重新下单');
  134. }
  135. // 获取用户收货地址信息
  136. if (empty($extra['address'])) {
  137. $address = UserAddress::getOne([['id' => $form['address_id']], ['user_id' => $form['user_id']]], true);
  138. $extra['address'] = $address;
  139. } else {
  140. $address = $extra['address'];
  141. }
  142. // 订单加上聚合供应链标识
  143. $group_order_goods = $form->group_order_goods;
  144. // 未选择地址不进行计算运费
  145. if (!empty($address)) {
  146. // 特殊地址处理
  147. $address = static::formatAddress($address);
  148. // 提交的地址
  149. $sub_address = [
  150. 'consignee' => $address['name'],
  151. 'phone' => $address['mobile'],
  152. 'province' => $address['province'],
  153. 'city' => $address['city'],
  154. 'area' => $address['district'],
  155. 'street' => $address['town'],
  156. 'description' => $address['detail'],
  157. ];
  158. // 获取运费请求参数
  159. $data = [
  160. 'spu' => [
  161. [
  162. 'sku' => $sku_id,
  163. 'goods_id' => $goods_id,
  164. 'shop_goods_id' => $sku->supply_goods_id,
  165. 'number' => intval($num),
  166. ]
  167. ],
  168. 'address' => $sub_address,
  169. ];
  170. // 收货人名称过短
  171. if (mb_strlen($address['name']) < static::NAME_MIN_LENGTH && $is_new_record) {
  172. throw new Exception('收货人名称过短');
  173. }
  174. $api = ApiService::getConnect($this->mall_id, $sku->supply_id);
  175. $is_valid = $api->availableCheck($data);
  176. if (isset($is_valid['ban']) && $is_new_record) {
  177. throw new Exception('下单失败,此商品不支持在当前地区销售');
  178. }
  179. // 邮费
  180. $freight = $api->orderBeforeCheck($data);
  181. if ($is_new_record && $freight === false) {
  182. throw new Exception('下单失败:' . $api->error);
  183. }
  184. if(!empty($freight['desc'])){
  185. throw new Exception('下单失败:'.$freight['desc']);
  186. }
  187. $freight_money = $freight['freight'] ?? 0;
  188. // 风控检测
  189. $setting = new SettingService(['mall_id' => $this->mall_id]);
  190. $result = $setting->checkDanger($total_price, $sku->sku_id, $num, $sku->supply_goods_id, $sku->supply_id);
  191. if(!$result) {
  192. MallGoodsModel::updateDown($this->mall_id, $goods_id);
  193. throw new Exception($setting->error);
  194. }
  195. // 计算邮费
  196. $freight = $this->getFreight($freight_money);
  197. $form->marketing['deduct']['shipping_fee'] = [
  198. 'name' => '运费',
  199. 'money' => $freight,
  200. ];
  201. foreach ($group_order_goods as $k => &$v) {
  202. if ($v['mch_order_info']) {
  203. // 标识
  204. $v['mch_order_info']['order_source'] = QiJuheEnum::PLUGIN_SIGN;
  205. // 邮费
  206. $v['mch_order_info']['shipping_type'] = ShippingTypeEnum::LOGISTICS; // 默认物流配送
  207. $v['mch_order_info']['shipping_money'] = $freight;
  208. // 收货地址
  209. $v['mch_order_info']['province'] = $address['province_id'];
  210. $v['mch_order_info']['city'] = $address['city_id'];
  211. $v['mch_order_info']['area'] = $address['district_id'];
  212. $v['mch_order_info']['town'] = $address['town_id'];
  213. $v['mch_order_info']['region_name'] = $address['province'] .' '. $address['city'] .' '. $address['district'].' '.$address['town'];
  214. $v['mch_order_info']['address'] = $address['detail'];
  215. $v['mch_order_info']['receiver_name'] = $address['name'];
  216. $v['mch_order_info']['mobile'] = $address['mobile'];
  217. }
  218. }
  219. // 计算总额
  220. $form['total_pay_money'] = bcadd($form['total_pay_money'], $freight, 2);
  221. }
  222. $form->group_order_goods = $group_order_goods;
  223. return $form;
  224. }
  225. public function validForm(BaseOrderForm $form, $is_new_record = false)
  226. {
  227. if ($form->type == 'buy_now') {
  228. $order_goods = $form->order_goods;
  229. } else {
  230. $order_goods = $form->group_order_goods[QiJuheEnum::PLUGIN_SIGN]['goods'];
  231. }
  232. if (empty($order_goods)) return $form;
  233. // 获取用户收货地址信息
  234. if (empty($extra['address'])) {
  235. $address = UserAddress::getOne([['id' => $form['address_id']], ['user_id' => $form['user_id']]], true);
  236. $extra['address'] = $address;
  237. } else {
  238. $address = $extra['address'];
  239. }
  240. $spu_list = [];
  241. $total_prices = [];
  242. $total_nums = [];
  243. $total_sku_nums = [];
  244. // 获取购买的AttrId
  245. foreach ($order_goods as $goods) {
  246. if ($form->type == 'buy_now') {
  247. $this->valid($goods['goods_id'], $goods['goods_attr_id'], $goods['num'], $goods, $is_new_record,
  248. $spu_list, $total_prices, $total_nums, $total_sku_nums);
  249. } else {
  250. foreach ($goods['goods_details'] as $goods_detail) {
  251. $this->valid($goods_detail['goods_id'], $goods_detail['goods_attr_id'], $goods_detail['num'], $goods_detail,
  252. $is_new_record, $spu_list, $total_prices, $total_nums, $total_sku_nums);
  253. }
  254. }
  255. }
  256. if (empty($spu_list)) return $form;
  257. // 订单加上聚合供应链标识
  258. $group_order_goods = $form->group_order_goods;
  259. $free_shopping_total_amount = 0;
  260. $free_shopping_goods_ids = [];
  261. // 未选择地址不进行计算运费
  262. if (!empty($address)) {
  263. $config = (new SettingService(['mall_id' => $form->mall_id]))->getSetting();
  264. $rate = !empty($sell) ? $sell['sell_agreement'] : 1.5;
  265. $rate = 1;
  266. $shopping_cart_free_amount = !empty($config['shopping_cart_free_amount']) ? $config['shopping_cart_free_amount'] : 59;
  267. $shopping_cart_free_amount = bcmul($shopping_cart_free_amount, $rate, 2);
  268. $freight_amount = 0;
  269. $freight_amount_list = [];
  270. // 特殊地址处理
  271. $address = static::formatAddress($address);
  272. // 提交的地址
  273. $sub_address = [
  274. 'consignee' => $address['name'],
  275. 'phone' => $address['mobile'],
  276. 'province' => $address['province'],
  277. 'city' => $address['city'],
  278. 'area' => $address['district'],
  279. 'street' => $address['town'],
  280. 'description' => $address['detail'],
  281. ];
  282. $sell = null;
  283. $source_goods = [];
  284. foreach ($spu_list as $supply_id => $i_spu_list) {
  285. // 库存检测
  286. self::validStock($form->mall_id, $supply_id, $total_sku_nums[$supply_id]);
  287. foreach ($i_spu_list as $source => $spus) {
  288. // 获取运费请求参数
  289. $data = [
  290. 'spu' => $spus,
  291. 'address' => $sub_address,
  292. ];
  293. // 收货人名称过短
  294. if (mb_strlen($address['name']) < static::NAME_MIN_LENGTH && $is_new_record) {
  295. throw new Exception('收货人名称过短');
  296. }
  297. $api = ApiService::getConnect($this->mall_id, $supply_id);
  298. $is_valid = $api->availableCheck($data);
  299. if (isset($is_valid['ban']) && $is_new_record) {
  300. throw new Exception('下单失败,此商品不支持在当前地区销售');
  301. }
  302. // 邮费
  303. $freight = $api->orderBeforeCheck($data);
  304. if ($freight === false) {
  305. throw new Exception('下单失败:' . $api->error);
  306. }
  307. if(!empty($freight['desc'])){
  308. throw new Exception('下单失败:'.$freight['desc']);
  309. }
  310. $freight_money = $freight['freight'] ?? 0;
  311. // 风控检测
  312. $setting = new SettingService(['mall_id' => $this->mall_id]);
  313. foreach ($spus as $spu) {
  314. $result = $setting->checkDanger($total_prices[$supply_id][$source], $spu['sku'], $spu['number'], $spu['shop_goods_id'], $supply_id);
  315. if(!$result) {
  316. MallGoodsModel::updateDown($this->mall_id, $spu['goods_id']);
  317. throw new Exception($setting->error);
  318. }
  319. // 供应链商品
  320. $s_goods = GoodsModel::findOne(['goods_id' => $spu['shop_goods_id'], 'mall_id' => $form->mall_id]);
  321. // 最低购买量
  322. if ($total_nums[$supply_id][$source][$spu['shop_goods_id']] < $s_goods->min_buy_qty) {
  323. $l_goods = Goods::findOne(['id' => $spu['goods_id']]);
  324. throw new Exception("商品 {$l_goods->goods_name} 起订量 $s_goods->min_buy_qty 件起");
  325. }
  326. if ($source == QiJuheEnum::FREE_SHIPPING_SOURCE && $s_goods->source_name == QiJuheEnum::FREE_SHIPPING_SOURCE_NAME) { // 优选供应链的
  327. // if ($source == QiJuheEnum::FREE_SHIPPING_SOURCE) { // 优选供应链的
  328. // $goodsInfo = $api->goodsInfo([$spu['shop_goods_id']]);
  329. // if (!empty($goodsInfo[0])) {
  330. $free_shopping_total_amount += $total_prices[$supply_id][$source]; // 取协议价
  331. // $free_shopping_goods_ids[] = $spu['goods_id'];
  332. // }
  333. if (empty($sell)) {
  334. $supply = SupplysModel::findOne(['id' => $supply_id]);
  335. $sell = Json::decode(!empty($supply->sell) ? $supply->sell : '[]');
  336. }
  337. }
  338. // 属于哪个供应链
  339. if (empty($source_goods[$source]) || !in_array($spu['goods_id'], $source_goods[$source])) {
  340. $source_goods[$source][] = $spu['goods_id'];
  341. }
  342. }
  343. if (!empty($config['is_enable_shopping_cart_settle']) && !empty($free_shopping_total_amount) &&
  344. $free_shopping_total_amount >= $shopping_cart_free_amount) {
  345. $freight_money = 0; // 强制免邮
  346. array_push($free_shopping_goods_ids, ...array_column($spus, 'goods_id'));
  347. }
  348. $freight_amount_list[QiJuheEnum::PLUGIN_SIGN . $source] = $freight_money ? $this->getFreight($freight_money) : 0;
  349. // 计算邮费
  350. $freight_amount += $freight_money ? $this->getFreight($freight_money) : 0;
  351. $free_shopping_total_amount = 0;
  352. }
  353. }
  354. // $free_shopping_total_amount = bcmul($free_shopping_total_amount, $rate, 2);
  355. $form->marketing['deduct']['shipping_fee'] = [
  356. 'name' => '运费',
  357. 'money' => $freight_amount,
  358. ];
  359. if ($is_new_record) { // 请求云众的时候需要将子供应链进行拆分
  360. // 从这里拆单
  361. $t_group_order_goods = [];
  362. foreach ($group_order_goods as $k => $tv) {
  363. if (strpos($k, QiJuheEnum::PLUGIN_SIGN) === 0) {
  364. foreach ($tv['goods'] as $goods_id => $good) {
  365. foreach ($source_goods as $source => $source_good) {
  366. if (in_array($goods_id, $source_good)) {
  367. $t_k = "{$k}{$source}";
  368. if (empty($t_group_order_goods[$t_k])) { // 如果有优惠,那么需要怎么处理?
  369. $tv['mch_order_info']['pay_money'] = 0;
  370. $tv['mch_order_info']['goods_price'] = 0;
  371. $tv['mch_order_info']['original_goods_price'] = 0;
  372. $t_group_order_goods[$t_k] = [
  373. 'mch_order_info' => $tv['mch_order_info'],
  374. 'goods' => []
  375. ];
  376. }
  377. foreach ($good['goods_details'] as $goods_detail) {
  378. $t_group_order_goods[$t_k]['mch_order_info']['pay_money'] += (float) $goods_detail['goods_price'];
  379. $t_group_order_goods[$t_k]['mch_order_info']['goods_price'] += (float) $goods_detail['goods_price'];
  380. $t_group_order_goods[$t_k]['mch_order_info']['original_goods_price']+= $goods_detail['original_goods_price'];
  381. }
  382. $t_group_order_goods[$t_k]['goods'][$goods_id] = $good;
  383. }
  384. }
  385. }
  386. } else {
  387. $t_group_order_goods[$k] = $tv;
  388. }
  389. }
  390. $group_order_goods = $t_group_order_goods;
  391. }
  392. foreach ($group_order_goods as $k => &$v) { // 如果是创建,那么就将所有商品合为一个订单
  393. if (($v['mch_order_info'] && strpos($k, QiJuheEnum::PLUGIN_SIGN) === 0) || $form->type == 'buy_now') {
  394. // 标识
  395. $v['mch_order_info']['order_source'] = QiJuheEnum::PLUGIN_SIGN;
  396. // 邮费
  397. $v['mch_order_info']['shipping_type'] = ShippingTypeEnum::LOGISTICS; // 默认物流配送
  398. $v['mch_order_info']['shipping_money'] = $form->type == 'buy_now' ? $freight_amount : ($freight_amount_list[$k] ?? 0); // 不同运费
  399. // 收货地址
  400. $v['mch_order_info']['province'] = $address['province_id'];
  401. $v['mch_order_info']['city'] = $address['city_id'];
  402. $v['mch_order_info']['area'] = $address['district_id'];
  403. $v['mch_order_info']['town'] = $address['town_id'];
  404. $v['mch_order_info']['region_name'] = $address['province'] .' '. $address['city'] .' '. $address['district'].' '.$address['town'];
  405. $v['mch_order_info']['address'] = $address['detail'];
  406. $v['mch_order_info']['receiver_name'] = $address['name'];
  407. $v['mch_order_info']['mobile'] = $address['mobile'];
  408. if (!empty($config['is_enable_shopping_cart_settle'])) {
  409. $v['mch_order_info']['extra_info'] = Json::encode([
  410. 'mall_info' => [
  411. 'mall_name' => $config['shopping_cart_mall_name'],
  412. 'mall_logo' => $config['shopping_cart_mall_logo'],
  413. ]
  414. ]);
  415. foreach ($v['goods'] as $goods_id => $good) {
  416. // 判断是否为满包邮
  417. foreach ($good['goods_details'] as $dk => $detail) {
  418. // 判断是否需要包邮
  419. if (in_array($goods_id, $free_shopping_goods_ids)) {
  420. $free_shipping_deduct_info = ['amount' => 0, 'money' => 0, 'tips' => "满{$shopping_cart_free_amount}包邮",
  421. 'name' => "满{$shopping_cart_free_amount}包邮", 'ids' => $free_shopping_goods_ids];//积分抵扣信息
  422. if (empty($detail['marketing']['deduct_currency'])) {
  423. $deduct_currency = ['free_shipping' => $free_shipping_deduct_info];
  424. } else {
  425. $deduct_currency = Json::decode($detail['marketing']['deduct_currency'], true);
  426. $deduct_currency['free_shipping'] = $free_shipping_deduct_info;
  427. }
  428. $v['goods'][$goods_id]['goods_details'][$dk]['marketing']['deduct_currency'] = Json::encode($deduct_currency);
  429. }
  430. }
  431. }
  432. }
  433. }
  434. }
  435. // 计算总额
  436. $form['total_pay_money'] = bcadd($form['total_pay_money'], $freight_amount, 2);
  437. }
  438. $form = $form->toArray();
  439. $form['group_order_goods'] = array_values($group_order_goods);
  440. return $form;
  441. }
  442. protected function valid($goods_id, $attr_id, $num, $goods, $is_new_record, &$spu_list, &$total_prices, &$total_nums, &$total_sku_nums)
  443. {
  444. $sku = GoodsSkuModel::getSku($goods_id, $attr_id);
  445. if (empty($sku)) {
  446. throw new Exception('下单商品SKU不存在,请重新下单: ' . $attr_id);
  447. }
  448. $t_goods = GoodsModel::findOne(['goods_id' => $sku->supply_goods_id, 'mall_id' => $sku->mall_id]);
  449. // 总价
  450. if (empty($total_prices[$sku->supply_id][$t_goods->source])) $total_prices[$sku->supply_id][$t_goods->source] = 0;
  451. $total_prices[$sku->supply_id][$t_goods->source] += $goods['goods_price'];
  452. if (empty($spu_list[$sku->supply_id][$t_goods->source])) $spu_list[$sku->supply_id][$t_goods->source] = [];
  453. $spu_list[$sku->supply_id][$t_goods->source][] = [
  454. 'sku' => $sku->sku_id,
  455. 'goods_id' => $goods_id,
  456. 'shop_goods_id' => $sku->supply_goods_id,
  457. 'number' => intval($num),
  458. ];
  459. // 累加数量
  460. if (empty($total_nums[$sku->supply_id][$t_goods->source][$sku->supply_goods_id]))
  461. $total_nums[$sku->supply_id][$t_goods->source][$sku->supply_goods_id] = 0;
  462. $total_nums[$sku->supply_id][$t_goods->source][$sku->supply_goods_id] += intval($num);
  463. $total_sku_nums[$sku->supply_id][$sku->supply_goods_id][$sku->sku_id] = intval($num);
  464. }
  465. protected static function validStock(int $mall_id, int $supply_id, array $third_goods_ids)
  466. {
  467. $ids = array_keys($third_goods_ids);
  468. $api = ApiService::getConnect($mall_id, $supply_id);
  469. $resp = $api->goodsInfo($ids);
  470. if (!empty($api->error)) throw new Exception($api->error);
  471. if (empty($resp) || count($resp) < count($ids)) throw new Exception('商品不存在');
  472. foreach ($resp as $item) {
  473. foreach ($item['skus'] as $sku) {
  474. if ($third_goods_ids[$item['id']][$sku['id']] > $sku['stock']) {
  475. throw new Exception("{$item['title']} - {$sku['title']} 库存不足");
  476. }
  477. }
  478. }
  479. }
  480. public static function createOrderV2($order_id)
  481. {
  482. // 订单详情
  483. $order = MallOrderModel::getOrder($order_id);
  484. if ($order && $order->order_source != QiJuheEnum::PLUGIN_SIGN && $order->order_source != AddonsEnum::ADDONS_NAME_CARD_COUPONS) return false;
  485. if (!$order->pay_status == StatusEnum::ENABLED) return false;
  486. if (empty($order->detail)) return false;
  487. foreach ($order->detail as $detail) {
  488. if($order->order_source == AddonsEnum::ADDONS_NAME_CARD_COUPONS) {
  489. $goods = Goods::findOne($detail->goods_id);
  490. if(!$goods || $goods['goods_source'] != QiJuheEnum::PLUGIN_SIGN) continue;
  491. }
  492. // skuId
  493. $sku = GoodsSkuModel::getSku($detail->goods_id, $detail->goods_attr_id);
  494. if (empty($sku)) return false;
  495. // 聚合商品信息
  496. $supply_goods = GoodsModel::find()->where(['goods_id' => $sku->supply_goods_id])->select('goods_id, supply_id, source')->one();
  497. if (empty($supply_goods)) return false;
  498. // 以平台分类
  499. $supply_orders[$sku->supply_id][] = [
  500. 'sku' => $sku->sku_id,
  501. 'number' => intval($detail->num),
  502. 'goods_id' => $supply_goods->goods_id,
  503. 'source' => $supply_goods->source,
  504. 'order_detail_id' => $detail['id'],
  505. ];
  506. }
  507. if (empty($supply_orders)) return false;
  508. $sku_arr = [];
  509. $order_model_arr = [];
  510. foreach ($supply_orders as $supply_id => $skus) {
  511. // 接口
  512. $api = ApiService::getConnect($order->mall_id, $supply_id);
  513. foreach ($skus as $sku) {
  514. // 创建订单
  515. $order_model = new OrderModel();
  516. $order_model->supply_id = $supply_id;
  517. $order_model->user_id = $order->user_id;
  518. $order_model->order_no = $order->order_no;
  519. $order_model->order_id = $order_id;
  520. $order_model->order_detail_id = $sku['order_detail_id'];
  521. $order_model->goods_id = $sku['goods_id'];
  522. $order_model->source = strval($sku['source']);
  523. $order_model->mall_id = $order->mall_id;
  524. $order_model->sku_id = $sku['sku'];
  525. $order_model->created_at = $order_model->updated_at = time();
  526. if (!$order_model->save()) {
  527. throw new Exception("聚合供应链订单:{$order->order_no}生成失败");
  528. return false;
  529. }
  530. $order_model_arr[] = $order_model;
  531. // spu
  532. $sku_arr[] = [
  533. 'sku' => $sku['sku'],
  534. 'number' => intval($sku['number']),
  535. ];
  536. }
  537. }
  538. // 地址
  539. $region = explode(' ', $order->region_name);
  540. $address = [
  541. 'province' => $region[0],
  542. 'city' => $region[1],
  543. 'district' => $region[2],
  544. 'town' => $region[3],
  545. ];
  546. // 格式化地址
  547. $address = static::formatAddress($address);
  548. // 提交的地址
  549. $sub_address = [
  550. 'consignee' => $order->receiver_name,
  551. 'phone' => $order->mobile,
  552. 'province' => $address['province'],
  553. 'city' => $address['city'],
  554. 'area' => $address['district'],
  555. 'street' => $address['town'],
  556. 'description' => $order->address,
  557. ];
  558. $data = [
  559. 'order_sn' => $order->order_no,
  560. 'spu' => $sku_arr,
  561. 'address' => $sub_address,
  562. 'remark' => !empty($order->remark) ? $order->remark : '',
  563. ];
  564. // 收货人名称过短
  565. if (mb_strlen($order->receiver_name) < static::NAME_MIN_LENGTH) {
  566. throw new Exception('收货人名称过短');
  567. }
  568. // 保留api提交数据
  569. OrderModel::updateAll(['order_json'=>Json::encode($data)],['order_no'=>$order->order_no]);
  570. // 推送订单
  571. $result = $api->createOreder($data);
  572. if (!$result) {
  573. // 修改状态
  574. foreach($order_model_arr as $order_model){
  575. $order_model->pushFail($api->error);
  576. }
  577. // 队列执行
  578. // Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
  579. // 'mall_id' => $order->mall_id,
  580. // ], OrderFailListener::class, 'async_handle');
  581. throw new Exception("聚合供应链订单:{$order->order_no}推送失败");
  582. }
  583. // 推送成功修改状态
  584. // $order_data = $api->getOrderInfo($order->order_no);
  585. foreach($order_model_arr as $order_model){
  586. $res = $order_model->payOk('', $result);
  587. if (!$res) {
  588. throw new Exception("聚合供应链订单:{$order->order_no}修改成功状态失败");
  589. }
  590. }
  591. }
  592. /**
  593. * 创建聚合订单
  594. *
  595. * @param integer $order_id 订单ID
  596. * @param string $order_sn 订单编号
  597. * @return void
  598. */
  599. public static function createOrder($order_id/* , $order_no */)
  600. {
  601. // 订单详情
  602. $order = MallOrderModel::getOrder($order_id);
  603. if ($order && $order->order_source != QiJuheEnum::PLUGIN_SIGN && $order->order_source != AddonsEnum::ADDONS_NAME_CARD_COUPONS) return false;
  604. if (!$order->pay_status == StatusEnum::ENABLED) return false;
  605. if (empty($order->detail)) return false;
  606. foreach ($order->detail as $detail) {
  607. if($order->order_source == AddonsEnum::ADDONS_NAME_CARD_COUPONS) {
  608. $goods = Goods::findOne($detail->goods_id);
  609. if(!$goods || $goods['goods_source'] != QiJuheEnum::PLUGIN_SIGN) continue;
  610. }
  611. // skuId
  612. $sku = GoodsSkuModel::getSku($detail->goods_id, $detail->goods_attr_id);
  613. if (empty($sku)) return false;
  614. // 聚合商品信息
  615. $supply_goods = GoodsModel::find()->where(['goods_id' => $sku->supply_goods_id])->select('goods_id, supply_id, source')->one();
  616. if (empty($supply_goods)) return false;
  617. // 以平台分类
  618. $supply_orders[$sku->supply_id][] = [
  619. 'sku' => $sku->sku_id,
  620. 'number' => intval($detail->num),
  621. 'goods_id' => $supply_goods->goods_id,
  622. 'source' => $supply_goods->source,
  623. 'order_detail_id' => $detail['id'],
  624. ];
  625. }
  626. if (empty($supply_orders)) return false;
  627. foreach ($supply_orders as $supply_id => $skus) {
  628. // 接口
  629. $api = ApiService::getConnect($order->mall_id, $supply_id);
  630. $order_model_arr = [];
  631. $sku_arr = [];
  632. foreach ($skus as $sku) {
  633. // 创建订单
  634. $order_model = new OrderModel();
  635. $order_model->supply_id = $supply_id;
  636. $order_model->user_id = $order->user_id;
  637. $order_model->order_no = $order->order_no;
  638. $order_model->order_id = $order_id;
  639. $order_model->order_detail_id = $sku['order_detail_id'];
  640. $order_model->goods_id = $sku['goods_id'];
  641. $order_model->source = strval($sku['source']);
  642. $order_model->mall_id = $order->mall_id;
  643. $order_model->sku_id = $sku['sku'];
  644. $order_model->created_at = $order_model->updated_at = time();
  645. if (!$order_model->save()) {
  646. throw new Exception("聚合供应链订单:{$order->order_no}生成失败");
  647. return false;
  648. }
  649. $order_model_arr[] = $order_model;
  650. // spu
  651. $sku_arr[] = [
  652. 'sku' => $sku['sku'],
  653. 'number' => intval($sku['number']),
  654. ];
  655. }
  656. // 地址
  657. $region = explode(' ', $order->region_name);
  658. $address = [
  659. 'province' => $region[0],
  660. 'city' => $region[1],
  661. 'district' => $region[2],
  662. 'town' => $region[3],
  663. ];
  664. // 格式化地址
  665. $address = static::formatAddress($address);
  666. // 提交的地址
  667. $sub_address = [
  668. 'consignee' => $order->receiver_name,
  669. 'phone' => $order->mobile,
  670. 'province' => $address['province'],
  671. 'city' => $address['city'],
  672. 'area' => $address['district'],
  673. 'street' => $address['town'],
  674. 'description' => $order->address,
  675. ];
  676. $data = [
  677. 'order_sn' => $order->order_no,
  678. 'spu' => $sku_arr,
  679. 'address' => $sub_address,
  680. ];
  681. // 收货人名称过短
  682. if (mb_strlen($order->receiver_name) < static::NAME_MIN_LENGTH) {
  683. throw new Exception('收货人名称过短');
  684. }
  685. // 保留api提交数据
  686. OrderModel::updateAll(['order_json'=>Json::encode($data)],['order_no'=>$order->order_no]);
  687. // 推送订单
  688. $result = $api->createOreder($data);
  689. if (!$result) {
  690. // 修改状态
  691. foreach($order_model_arr as $order_model){
  692. $order_model->pushFail($api->error);
  693. }
  694. // 队列执行
  695. // Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
  696. // 'mall_id' => $order->mall_id,
  697. // ], OrderFailListener::class, 'async_handle');
  698. throw new Exception("聚合供应链订单:{$order->order_no}推送失败");
  699. }
  700. // 推送成功修改状态
  701. // $order_data = $api->getOrderInfo($order->order_no);
  702. foreach($order_model_arr as $order_model){
  703. $res = $order_model->payOk('', $result);
  704. if (!$res) {
  705. throw new Exception("聚合供应链订单:{$order->order_no}修改成功状态失败");
  706. }
  707. }
  708. }
  709. }
  710. /**
  711. * 聚合平台交易完成(淘宝 jd确认收货 订单完成了)
  712. * 目前不需要修改系统订单状态,使用系统自带的收货完成
  713. *
  714. * @param string $order_no
  715. * @return void
  716. */
  717. public function orderSuccess($order_no)
  718. {
  719. $juhe_order = OrderModel::findOne(['order_no' => $order_no, 'mall_id' => $this->mall_id]);
  720. if(empty($juhe_order)) {
  721. }
  722. $mall_order = MallOrderModel::findOne(['order_no' => $order_no, 'mall_id' => $this->mall_id]);
  723. if(empty($sys_order)) {
  724. }
  725. }
  726. /**
  727. * 用户确认收货
  728. *
  729. * @param string $order_no
  730. * @return void
  731. */
  732. public static function orderDelivered($order_id)
  733. {
  734. $juhe_order = OrderModel::find()->where(['order_id' => $order_id])->select('id, mall_id, supply_id, order_data')->one();
  735. if (empty($juhe_order)) {
  736. return false;
  737. }
  738. $data = Json::decode($juhe_order->order_data);
  739. if (empty($data['Orders'])) {
  740. return false;
  741. }
  742. $api = ApiService::getConnect($juhe_order->mall_id, $juhe_order->supply_id);
  743. foreach ($data['Orders'] as $order) {
  744. $res = $api->orderReceive($order['id']);
  745. }
  746. }
  747. /**
  748. * 商家发货
  749. *
  750. * @param string $order_no
  751. * @param integer $sku
  752. * @return void
  753. */
  754. public function orderDelivery($order_no, $sku)
  755. {
  756. $juhe_order = OrderModel::findOne(['order_no' => $order_no, 'mall_id' => $this->mall_id]);
  757. if(empty($juhe_order)) {
  758. throw new Exception("聚合供应链订单{$order_no}不存在");
  759. }
  760. $mall_order = MallOrderModel::find()->where(['order_no' => $order_no, 'mall_id' => $this->mall_id])->select('id')->one();
  761. if(empty($mall_order)) {
  762. throw new Exception("商城订单{$order_no}不存在");
  763. }
  764. $res = MallOrderModel::delivery(['mall_id' => $this->mall_id, 'id' => $mall_order->id]);
  765. if ($res === false) {
  766. throw new Exception("商城订单商家发货{$order_no}" . MallOrderModel::$static_error);
  767. }
  768. }
  769. /**
  770. * 订单转换成普通订单
  771. *
  772. * @param integer $id
  773. * @return boolean
  774. */
  775. public function changeOrder(int $id)
  776. {
  777. $order = OrderModel::findOne(['id' => $id, 'mall_id' => $this->mall_id]);
  778. if (empty($order)) return $this->error('订单不存在');
  779. /**
  780. * @var MallOrderModel|null
  781. */
  782. $mall_order = $order->mallOrder;
  783. if (empty($mall_order)) return $this->error('商城订单不存在');
  784. return $mall_order->changeOrder();
  785. }
  786. /**
  787. * 批量补单
  788. *
  789. * @param array $ids
  790. * @return void
  791. */
  792. public function patchOrder(array $ids)
  793. {
  794. $error = [];
  795. $success = [];
  796. try {
  797. $juhe_order = OrderModel::find()->with('mallOrder')->where(['id' => $ids])->all();
  798. if (empty($juhe_order)) {
  799. throw new Exception('订单不存在');
  800. }
  801. foreach ($juhe_order as $order) {
  802. // 订单状态
  803. if (!in_array($order->status, [OrderEnum::PUSH_NOT, OrderEnum::PUSH_FAIL])) {
  804. $error[] = [
  805. 'order_no' => $order->order_no,
  806. 'msg' => '订单状态不正常',
  807. ];
  808. continue;
  809. }
  810. // 不是待发货状态,不允许补单
  811. if ($order->mallOrder->status != OrderStatusEnum::PAY || $order->mallOrder->is_feedback != OrderStatusEnum::NOT_FEEDBACK) {
  812. $error[] = [
  813. 'order_no' => $order->order_no,
  814. 'msg' => '订单未付款或者正在售后~',
  815. ];
  816. continue;
  817. }
  818. $api = ApiService::getConnect($order->mall_id, $order->supply_id);
  819. // 接口没有批量补单
  820. $result = $api->createOreder(Json::decode($order->order_json));
  821. if (!$result) {
  822. $order->pushFail($api->error);
  823. $error[] = [
  824. 'order_no' => $order->order_no,
  825. 'msg' => $order->order_no.'【'.$api->error.'】',
  826. ];
  827. continue;
  828. }
  829. // 补单成功
  830. $order->payOk('', $result);
  831. $success[] = [
  832. 'order_no' => $order->order_no,
  833. 'msg' => '成功',
  834. ];
  835. }
  836. } catch (Exception $e) {
  837. $error[] = $e->getMessage();
  838. }
  839. return compact('success', 'error');
  840. }
  841. /**
  842. * 计算聚合供应链邮费
  843. *
  844. * @param array $juhe_money 聚合供应链返回邮费数据
  845. * @return mixed
  846. */
  847. protected function getFreight($money)
  848. {
  849. if($money <= 0) return 0;
  850. return bcdiv($money, 100, 2);
  851. }
  852. /**
  853. * 中台发货,更新物流信息
  854. * @param $order_sn
  855. */
  856. public function updateLogistic($order_sn)
  857. {
  858. $orders = OrderModel::find()
  859. ->where(['order_no'=>$order_sn])
  860. // ->with(['mallOrder' => function ($q) {
  861. // return $q->select('id')->with(['detail' => function ($q) {
  862. // return $q->select('id, order_id,goods_id');
  863. // }]);
  864. // }])
  865. ->select('id, mall_id, order_no, order_id, supply_id,goods_id,order_detail_id')
  866. ->all();
  867. if(empty($orders)) return true;
  868. $order_list = [];
  869. foreach($orders as $order_item){
  870. $order_list[$order_item['supply_id']][] = $order_item;
  871. }
  872. foreach($order_list as $supply_id => $orders){
  873. $this->mall_id = $orders[0]['mall_id'];
  874. // 获取物流信息
  875. $api = ApiService::getConnect(['mall_id' => $this->mall_id, 'supply_id' => $supply_id]);
  876. $logistic = $api->orderLogistic($order_sn);
  877. try {
  878. if(empty($logistic)) throw new \Exception("获取物流信息异常");
  879. foreach($logistic as $express_order){
  880. $params = $this->getExpressParams($orders, $express_order);
  881. \Yii::$app->custom->logs('物流信息: ' . json_encode($params));
  882. $res = OrderExpress::delivery($params);
  883. if ($res === false) {
  884. // 这里选择不抛出错误(因为如果有多个发货,假设第一个已经发货就会进入来这里,抛出错误后导致其他的也发不了)
  885. $errorMsg = '商家已发货:'
  886. . 'OrderExpress::delivery订单发货失败: ' . OrderExpress::$static_error
  887. . ', 位于File:' . __FILE__
  888. . ', 位于Line:' . __LINE__
  889. . ', 参数:' . json_encode($params ?? [])
  890. . $order_sn;
  891. Yii::error($errorMsg);
  892. // throw new Exception('OrderExpress::delivery订单发货失败: ' . OrderExpress::$static_error);
  893. }
  894. }
  895. return true;
  896. } catch (Exception $e) {
  897. $errorMsg = '商家已发货:'
  898. . $e->getMessage()
  899. . ', 位于File:' . $e->getFile()
  900. . ', 位于Line:' . $e->getLine()
  901. . ', 参数:' . json_encode($params ?? [])
  902. . $order_sn;
  903. Yii::error($errorMsg);
  904. return false;
  905. }
  906. }
  907. }
  908. /**
  909. * 获取物流信息
  910. *
  911. * @param OrderModel $order
  912. * @param array $logistic
  913. * @return array
  914. */
  915. private function getExpressParams($orders, $express_order)
  916. {
  917. $params = [];
  918. $code = $express_order['company_code'];
  919. $express = ExpressModel::getExpress($code);
  920. if (!empty($express_order['order_items'])) {
  921. $params['id'] = $orders[0]['order_id'];
  922. foreach ($orders as $order) {
  923. foreach($express_order['order_items'] as $order_item){
  924. if($order_item['product_id'] == $order->goods_id){
  925. $params['order_detail_ids'][] = $order->order_detail_id;
  926. }
  927. }
  928. }
  929. $params['order_detail_ids'] = array_unique($params['order_detail_ids']);
  930. $params['shipping_type'] = 0;
  931. if (!empty($express)) {
  932. $params['shipping_type'] = 1;
  933. $params['express_id'] = $express->express_id;
  934. $params['express_name'] = $express->name;
  935. // 判断是否是顺丰物流,顺丰物流返回格式=SF1632145481624:5419,需要做切割处理
  936. if (strpos($express_order['express_no'], ':') !== false) {
  937. $arr_express_no = explode(':', $express_order['express_no']);
  938. $express_order['express_no'] = current($arr_express_no);
  939. }
  940. $params['express_no'] = $express_order['express_no'];
  941. }
  942. $params['mall_id'] = $this->mall_id;
  943. $params['mch_id'] = 0;
  944. $params['operator_id'] = 0;
  945. $params['operator_name'] = '';
  946. }
  947. return $params;
  948. }
  949. /**
  950. * 格式化部分区域地址
  951. *
  952. * @param array $address
  953. * @return array
  954. */
  955. protected static function formatAddress(array $address)
  956. {
  957. if ($address['city'] == '东莞市') {
  958. $address['town'] = $address['district'];
  959. $address['district'] = $address['city'];
  960. }
  961. if ($address['city'] == '中山市') {
  962. $address['town'] = $address['district'];
  963. $address['district'] = $address['city'];
  964. }
  965. if (($address['city'] == '长沙市') && ($address['district'] == '宁乡县')) {
  966. $address['district'] = '宁乡市';
  967. }
  968. return $address;
  969. }
  970. }