OrderDispatch.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <?php
  2. namespace addons\shopro\service\order;
  3. use app\admin\model\shopro\order\Order;
  4. use app\admin\model\shopro\order\OrderItem;
  5. use app\admin\model\shopro\order\Action as OrderAction;
  6. use app\admin\model\shopro\order\Express as OrderExpress;
  7. use app\admin\model\shopro\activity\Groupon as ActivityGroupon;
  8. use app\admin\model\shopro\activity\GrouponLog as ActivityGrouponLog;
  9. use app\admin\model\shopro\dispatch\Dispatch as DispatchModel;
  10. use app\admin\model\shopro\dispatch\DispatchAutosend;
  11. use addons\shopro\library\express\Express as ExpressLib;
  12. use think\Db;
  13. class OrderDispatch
  14. {
  15. public $order = null;
  16. public function __construct($params = [])
  17. {
  18. if (isset($params['order_id']) && !empty($params['order_id'])) {
  19. $this->order = Order::find($params['order_id']);
  20. if (!$this->order) {
  21. error_stop('订单不存在');
  22. }
  23. }
  24. }
  25. /**
  26. * 执行发货
  27. *
  28. * @return object
  29. */
  30. public function confirm($params)
  31. {
  32. $admin = auth_admin();
  33. $method = $params['method'] ?? '';
  34. if (!in_array($method, ['input', 'api', 'upload'])) {
  35. error_stop('请使用正确的发货方式');
  36. }
  37. if ($this->order->status !== Order::STATUS_PAID && !$this->order->isOffline($this->order)) {
  38. error_stop("该订单{$this->order->status_text},不能发货");
  39. }
  40. if ($this->order->apply_refund_status === Order::APPLY_REFUND_STATUS_APPLY) {
  41. error_stop("该订单已申请退款,暂不能发货");
  42. }
  43. switch ($method) {
  44. case 'api':
  45. list($orderItems, $express) = $this->doByApi($params);
  46. break;
  47. case 'input':
  48. list($orderItems, $express) = $this->doByInput($params);
  49. break;
  50. case 'upload':
  51. list($orderItems, $express) = $this->doByUpload($params);
  52. break;
  53. }
  54. // 添加包裹信息
  55. $orderExpress = OrderExpress::create([
  56. 'user_id' => $this->order->user_id,
  57. 'order_id' => $this->order->id,
  58. 'express_name' => $express['name'],
  59. 'express_code' => $express['code'],
  60. 'express_no' => $express['no'],
  61. 'method' => $method,
  62. 'driver' => $express['driver'] ?? null,
  63. 'ext' => $express['ext'] ?? null
  64. ]);
  65. // 修改订单商品发货状态
  66. foreach ($orderItems as $orderItem) {
  67. $orderItem->order_express_id = $orderExpress->id;
  68. $orderItem->dispatch_status = OrderItem::DISPATCH_STATUS_SENDED;
  69. $orderItem->ext = array_merge($orderItem->ext, ['send_time' => time()]); // item 发货时间
  70. $orderItem->save();
  71. OrderAction::add($this->order, $orderItem, $admin, 'admin', "商品{$orderItem->goods_title}已发货");
  72. }
  73. //找不到未发货的了,也就是全都发货了
  74. $check_nosend = Db::name('shopro_order_item')->where([
  75. 'order_id' => $this->order->id,
  76. 'dispatch_status' => OrderItem::DISPATCH_STATUS_NOSEND,
  77. 'order_express_id' => 0,
  78. ])->find();
  79. if(empty($check_nosend)){
  80. //商城,发货,冗余到bill表
  81. Db::name('bill')->where([
  82. 'table_name' => 'shopro_order',
  83. 'table_id' => $this->order->id,
  84. ])->update([
  85. 'status' => 2, //待收货
  86. ]);
  87. }
  88. $this->subscribeExpressInfo($orderExpress);
  89. // 订单发货后
  90. $data = [
  91. 'order' => $this->order,
  92. 'items' => $orderItems,
  93. 'express' => $orderExpress,
  94. 'dispatch_type' => 'express',
  95. ];
  96. \think\Hook::listen('order_dispatch_after', $data);
  97. return $express;
  98. }
  99. // 手动发货
  100. private function doByInput($params)
  101. {
  102. $orderItems = $this->getDispatchOrderItems($params, 'express');
  103. $express = $params['express'] ?? null;
  104. if (empty($express['name']) || empty($express['code']) || empty($express['no']) || strpos($express['no'], '=') !== false) {
  105. error_stop('请输入正确的快递信息');
  106. }
  107. return [$orderItems, $express];
  108. }
  109. // API发货
  110. private function doByApi($params)
  111. {
  112. $orderItems = $this->getDispatchOrderItems($params, 'express');
  113. $sender = $params['sender'] ?? null;
  114. $expressLib = new ExpressLib();
  115. $data = [
  116. 'order' => $this->order,
  117. 'sender' => $sender,
  118. 'consignee' => $this->order->address
  119. ];
  120. $express = $expressLib->eOrder($data, $orderItems);
  121. return [$orderItems, $express];
  122. }
  123. // 上传发货模板发货 TODO: 如果发货单比较多,循环更新可能会比较慢,考虑解析完模版信息以后,把数据返回前端,再次执行批量发货流程
  124. private function doByUpload($params)
  125. {
  126. $orderItems = $this->getDispatchOrderItems($params, 'express');
  127. $express = $params['express'] ?? null;
  128. if (empty($express['name']) || empty($express['code']) || empty($express['no'])) {
  129. error_stop('请输入正确的快递信息');
  130. }
  131. return [$orderItems, $express];
  132. }
  133. // 获取可发货的订单商品
  134. public function getDispatchOrderItems($params = null, $dispatch_type = 'express')
  135. {
  136. $orderItemIds = $params['order_item_ids'] ?? [];
  137. $whereCanDispatch['order_id'] = $this->order->id;
  138. $whereCanDispatch['dispatch_status'] = OrderItem::DISPATCH_STATUS_NOSEND;
  139. $whereCanDispatch['aftersale_status'] = ['<>', OrderItem::AFTERSALE_STATUS_ING];
  140. $whereCanDispatch['refund_status'] = OrderItem::REFUND_STATUS_NOREFUND;
  141. $whereCanDispatch['dispatch_type'] = $dispatch_type;
  142. if (empty($orderItemIds)) {
  143. $orderItems = OrderItem::where($whereCanDispatch)->select();
  144. } else {
  145. $orderItems = OrderItem::where('id', 'in', $orderItemIds)->where($whereCanDispatch)->select();
  146. if (count($orderItems) !== count($orderItemIds)) {
  147. error_stop('选中商品暂不能发货');
  148. }
  149. }
  150. if (!$orderItems) {
  151. error_stop('该订单无可发货商品');
  152. }
  153. return $orderItems;
  154. }
  155. /**
  156. * 取消发货
  157. *
  158. */
  159. public function cancel($params)
  160. {
  161. $admin = auth_user();
  162. $order_express_id = $params['order_express_id'] ?? 0;
  163. $orderExpress = OrderExpress::where('id', $order_express_id)->find();
  164. if (!$orderExpress) {
  165. error_stop('未找到发货单');
  166. }
  167. // 1.检测是不是用api发的 有些快递不支持取消接口 所以不判断了,统一手动取消
  168. // if ($orderExpress->method === 'api') {
  169. // // TODO: 走取消运单接口
  170. // $expressLib = new ExpressLib();
  171. // $data = [
  172. // 'express_no' => $orderExpress['express_no'],
  173. // 'express_code' => $orderExpress['express_code'],
  174. // 'order_code' => $orderExpress['ext']['Order']['OrderCode']
  175. // ];
  176. // $express = $expressLib->cancel($data);
  177. // }
  178. // 2. 变更发货状态
  179. $orderItems = OrderItem::where([
  180. 'order_id' => $this->order->id,
  181. 'order_express_id' => $orderExpress->id
  182. ])->where('dispatch_type', 'express')->select();
  183. foreach ($orderItems as $orderItem) {
  184. $orderItem->order_express_id = 0;
  185. $orderItem->dispatch_status = OrderItem::DISPATCH_STATUS_NOSEND;
  186. $orderItem->save();
  187. OrderAction::add($this->order, null, $admin, 'admin', "已取消发货");
  188. }
  189. // 删除发货单
  190. $orderExpress->delete();
  191. //找到未发货的了,上面刚刚操作完,其实不需要查询了
  192. $check_nosend = Db::name('shopro_order_item')->where([
  193. 'order_id' => $this->order->id,
  194. 'dispatch_status' => OrderItem::DISPATCH_STATUS_NOSEND,
  195. 'order_express_id' => 0,
  196. ])->find();
  197. if($check_nosend){
  198. //商城,取消发货,冗余到bill表
  199. Db::name('bill')->where([
  200. 'table_name' => 'shopro_order',
  201. 'table_id' => $this->order->id,
  202. ])->update([
  203. 'status' => 1, //待发货
  204. ]);
  205. }
  206. return true;
  207. }
  208. /**
  209. * 修改发货信息
  210. *
  211. */
  212. public function change($params)
  213. {
  214. $admin = auth_user();
  215. $order_express_id = $params['order_express_id'] ?? 0;
  216. $orderExpress = OrderExpress::where('id', $order_express_id)->find();
  217. if (!$orderExpress) {
  218. error_stop('未找到发货单');
  219. }
  220. // 1.1 检测是不是用api发的 如果是则提醒取消运单再重新走发货流程 此时什么都不用做
  221. if ($orderExpress->method === 'api') {
  222. error_stop('该发货单已被推送第三方平台,请取消后重新发货');
  223. }
  224. // 1.2 如果不是则手动变更运单信息(快递公司、运单号)
  225. $express = $params['express'] ?? null;
  226. if (empty($express['name']) || empty($express['code']) || empty($express['no']) || strpos($express['no'], '=') !== false) {
  227. error_stop('请输入正确的快递信息');
  228. }
  229. $orderExpress->save([
  230. 'express_name' => $express['name'],
  231. 'express_code' => $express['code'],
  232. 'express_no' => $express['no'],
  233. 'method' => 'input'
  234. ]);
  235. OrderAction::add($this->order, null, $admin, 'admin', "变更发货信息");
  236. $this->subscribeExpressInfo($orderExpress);
  237. // 修改发货信息
  238. $data = [
  239. 'order' => $this->order,
  240. 'express' => $orderExpress,
  241. 'dispatch_type' => 'express',
  242. ];
  243. \think\Hook::listen('order_dispatch_change', $data);
  244. return $express;
  245. }
  246. // 解析批量发货信息,筛选出能发货的订单
  247. public function multiple($params)
  248. {
  249. error_stop('暂不支持');
  250. // 上传发货模板
  251. if (!empty($params['file'])) {
  252. $express = $params['express'];
  253. $file = $params['file']->getPathname();
  254. $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
  255. $PHPExcel = $reader->load($file);
  256. $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表
  257. $allRow = $currentSheet->getHighestRow(); //取得一共有多少行
  258. if ($allRow <= 2) {
  259. error_stop('您的发货列表为空');
  260. }
  261. $orderExpressMap = [];
  262. $orderId = 0;
  263. $orderSn = "";
  264. for ($currentRow = 2; $currentRow <= $allRow - 1; $currentRow++) {
  265. $orderId = $currentSheet->getCellByColumnAndRow(1, $currentRow)->getValue() ?? $orderId;
  266. $orderSn = $currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue() ?? $orderSn;
  267. $orderItemId = $currentSheet->getCellByColumnAndRow(8, $currentRow)->getValue();
  268. if (empty($orderItemId)) {
  269. error_stop("发货单格式不正确");
  270. }
  271. $orderExpressNo = $currentSheet->getCellByColumnAndRow(15, $currentRow)->getValue();
  272. if (empty($orderExpressNo)) {
  273. error_stop("请填写 订单ID-{$orderId} 的运单号");
  274. }
  275. $orderExpressMap["$orderId.$orderSn"][$orderExpressNo][] = $orderItemId;
  276. }
  277. $list = [];
  278. foreach ($orderExpressMap as $orderFlag => $orderExpress) {
  279. foreach ($orderExpress as $expressNo => $orderItemIds) {
  280. $order = explode('.', $orderFlag);
  281. $list[] = [
  282. 'order_id' => $order[0],
  283. 'order_sn' => $order[1],
  284. 'order_item_ids' => $orderItemIds,
  285. 'express' => [
  286. 'name' => $express['name'],
  287. 'code' => $express['code'],
  288. 'no' => $expressNo
  289. ]
  290. ];
  291. }
  292. }
  293. return $list;
  294. } else {
  295. $list = [];
  296. $orderIds = $params['order_ids'] ?? [];
  297. if (empty($orderIds)) {
  298. error_stop('请选择发货订单');
  299. }
  300. foreach ($orderIds as $orderId) {
  301. $list[] = [
  302. 'order_id' => $orderId,
  303. 'order_sn' => Order::where('id', $orderId)->value('order_sn'),
  304. 'order_item_ids' => $this->getDispatchOrderItemIds($orderId)
  305. ];
  306. }
  307. return $list;
  308. }
  309. }
  310. // 获取可发货的订单商品
  311. private function getDispatchOrderItemIds($orderId)
  312. {
  313. $whereCanDispatch = [
  314. 'order_id' => $orderId,
  315. 'dispatch_status' => OrderItem::DISPATCH_STATUS_NOSEND,
  316. 'aftersale_status' => ['neq', OrderItem::AFTERSALE_STATUS_ING],
  317. 'refund_status' => OrderItem::REFUND_STATUS_NOREFUND,
  318. 'dispatch_type' => 'express'
  319. ];
  320. $orderItems = OrderItem::where($whereCanDispatch)->column('id');
  321. return $orderItems;
  322. }
  323. // 订阅物流追踪
  324. private function subscribeExpressInfo($orderExpress)
  325. {
  326. try {
  327. $expressLib = new ExpressLib();
  328. $a = $expressLib->subscribe([
  329. 'express_code' => $orderExpress['express_code'],
  330. 'express_no' => $orderExpress['express_no']
  331. ]);
  332. } catch (\Exception $e) {
  333. // Nothing TODO
  334. return;
  335. }
  336. }
  337. /**
  338. * 手动发货
  339. *
  340. * @param array $params
  341. * @return void
  342. */
  343. public function customDispatch($params)
  344. {
  345. $admin = auth_admin();
  346. $custom_type = $params['custom_type'] ?? 'text';
  347. $custom_content = $params['custom_content'] ?? ($custom_type == 'text' ? '' : []);
  348. if ($this->order->status !== Order::STATUS_PAID && !$this->order->isOffline($this->order)) {
  349. error_stop("该订单{$this->order->status_text},不能发货");
  350. }
  351. if ($this->order->apply_refund_status === Order::APPLY_REFUND_STATUS_APPLY) {
  352. error_stop("该订单已申请退款,暂不能发货");
  353. }
  354. // 获取手动发货的 items
  355. $orderItems = $this->getDispatchOrderItems($params, 'custom');
  356. $customExt = [ // 手动发货信息
  357. 'dispatch_content_type' => $custom_type,
  358. 'dispatch_content' => $custom_content
  359. ];
  360. // 修改订单商品发货状态
  361. foreach ($orderItems as $orderItem) {
  362. $orderItem->dispatch_status = OrderItem::DISPATCH_STATUS_SENDED;
  363. $orderItem->ext = array_merge($orderItem->ext, $customExt, ['send_time' => time()]); // item 发货时间
  364. $orderItem->save();
  365. OrderAction::add($this->order, $orderItem, $admin, 'admin', "商品{$orderItem->goods_title}已发货");
  366. }
  367. // 订单发货后
  368. $data = [
  369. 'order' => $this->order,
  370. 'items' => $orderItems,
  371. 'express' => null,
  372. 'dispatch_type' => 'custom',
  373. ];
  374. \think\Hook::listen('order_dispatch_after', $data);
  375. }
  376. /**
  377. * 拼团完成时触发检测自动发货
  378. *
  379. * @return bool
  380. */
  381. public function grouponCheckDispatchAndSend()
  382. {
  383. $this->systemCheckAutoSend();
  384. return true;
  385. }
  386. /**
  387. * 普通商品自动发货
  388. *
  389. * @return bool
  390. */
  391. public function checkDispatchAndSend()
  392. {
  393. // 拼团不自动发货,等成团完成才发货
  394. $orderExt = $this->order['ext'];
  395. $buy_type = ($orderExt && isset($orderExt['buy_type'])) ? $orderExt['buy_type'] : '';
  396. if ($this->order['activity_type'] && strpos($this->order['activity_type'], 'groupon') !== false && $buy_type == 'groupon') {
  397. return true; // 这里不对拼团的订单进行自动发货,等拼团成功在检测
  398. }
  399. // 检测需要自动发货的 item
  400. $this->systemCheckAutoSend();
  401. return true;
  402. }
  403. /**
  404. * 系统检测自动发货
  405. */
  406. private function systemCheckAutoSend()
  407. {
  408. $autosendItems = [];
  409. // 判断订单是否有需要发货的商品,并进行自动发货(autosend)
  410. foreach ($this->order->items as $key => $item) {
  411. // 判断不是未发货状态,或者退款完成,continue
  412. if (
  413. $item['dispatch_status'] == OrderItem::DISPATCH_STATUS_NOSEND
  414. && $item['aftersale_status'] != OrderItem::AFTERSALE_STATUS_ING
  415. && $item['refund_status'] == OrderItem::REFUND_STATUS_NOREFUND
  416. ) {
  417. // 订单可以发货
  418. switch ($item['dispatch_type']) {
  419. case 'autosend':
  420. // 自动发货
  421. $autosendItems[] = $item;
  422. }
  423. }
  424. }
  425. if ($autosendItems) {
  426. $this->autoSendItems($autosendItems, ['oper_type' => 'system']);
  427. }
  428. }
  429. /**
  430. * 当前订单需要自动发货的所有商品
  431. *
  432. * @param object|array $items
  433. * @return void
  434. */
  435. private function autoSendItems($items)
  436. {
  437. foreach ($items as $item) {
  438. $autosendExt = $this->getAutosendContent($item);
  439. $item->dispatch_status = OrderItem::DISPATCH_STATUS_SENDED;
  440. $item->ext = array_merge($item->ext, $autosendExt, ['send_time' => time()]); // item 发货时间
  441. $item->save();
  442. OrderAction::add($this->order, $item, null, 'system', "商品{$item->goods_title}已发货");
  443. }
  444. $data = [
  445. 'order' => $this->order,
  446. 'items' => $items,
  447. 'express' => null,
  448. 'dispatch_type' => 'custom',
  449. ];
  450. // 发货后事件,消息通知
  451. \think\Hook::listen('order_dispatch_after', $data);
  452. }
  453. /**
  454. * 获取商品的自动发货模板数据
  455. *
  456. * @param object|array $item
  457. * @return array
  458. */
  459. private function getAutosendContent($item)
  460. {
  461. // 获取配送模板
  462. $result = [];
  463. $dispatch = DispatchModel::with([$item['dispatch_type']])->show()->where('type', $item['dispatch_type'])->where('id', $item['dispatch_id'])->find();
  464. if ($dispatch && $dispatch->autosend) {
  465. $autosend = $dispatch->autosend;
  466. if (in_array($autosend['type'], ['text', 'params'])) {
  467. $result['dispatch_content_type'] = $autosend['type'];
  468. $result['dispatch_content'] = $autosend['content'];
  469. }
  470. }
  471. return $result;
  472. }
  473. }