OrderPackageReceivedSyncService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <?php
  2. namespace App\Services;
  3. use App\Jobs\LogisticAliJiSuSync;
  4. use App\Jobs\LogisticSFSync;
  5. use App\Jobs\LogisticYDSync;
  6. use App\Jobs\LogisticYTOSync;
  7. use App\Jobs\LogisticZopSync;
  8. use App\Order;
  9. use App\OrderPackage;
  10. use App\OrderPackageExpressRoute;
  11. use Carbon\Carbon;
  12. use Exception;
  13. use Illuminate\Database\Eloquent\Collection;
  14. use Illuminate\Support\Facades\Log;
  15. use Illuminate\Support\Str;
  16. class OrderPackageReceivedSyncService
  17. {
  18. protected $logisticSFService;
  19. protected $logisticZopService;
  20. use \App\Traits\LogisticSyncTrait;
  21. /**
  22. * 同步快递信息
  23. * @param bool $is_to_init 是否为初始化数据状态 初始化同步一个月的 正常执行为15天
  24. * @param array $logistic_numbers 指定更新的单号
  25. */
  26. public function syncLogisticRoute($is_to_init = false, $logistic_numbers = [])
  27. {
  28. ini_set('max_execution_time', 2 * 60 * 60);
  29. ini_set('memory_limit', '1024M');
  30. //没有指定单号
  31. if (empty($logistic_numbers)) {
  32. $query = OrderPackage::query()
  33. ->select(['logistic_number', 'order_id', 'id'])
  34. ->whereIn('status', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, null])
  35. ->whereIn('exception_status', [0, 1, 2, 3, 4, 5, 6, 7, null])
  36. ->with(['order' => function ($query) {
  37. return $query->select(['id', 'logistic_id'])->with('logistic:id,name,code');
  38. }]);
  39. if ($is_to_init) {//当前时间小于等于初始化时间
  40. $initDate = Carbon::parse(config('api_logistic.init_date'));
  41. //初始化查询一个月的数据
  42. $query = $query->where('created_at', '>=', $initDate)
  43. ->whereNull('received_at');
  44. } else {//查询20天以内的数据
  45. $query = $query->where('created_at', '>=', now()->subDays(config('api_logistic.querying_days'))->startOfDay())
  46. ->whereNull('received_at');
  47. }
  48. } else {//指定了单号
  49. $query = OrderPackage::query()
  50. ->select(['logistic_number', 'order_id', 'id'])
  51. ->with(['order' => function ($query) {
  52. return $query->select(['id', 'logistic_id'])->with('logistic:id,name,code');
  53. }])
  54. ->whereIn('logistic_number', $logistic_numbers);
  55. }
  56. //分块执行
  57. $query->chunkById(1000, function ($orderPackages) {
  58. //按照承运商分组
  59. $logisticNumbers = $this->buildData($orderPackages);
  60. //更新顺丰系列
  61. if (array_key_exists('SF', $logisticNumbers)) {
  62. $SFLogisticNumbers = $logisticNumbers['SF'];
  63. foreach ($SFLogisticNumbers as $logisticNumber) {
  64. LogisticSFSync::dispatch($logisticNumber);
  65. }
  66. }
  67. //更新中通
  68. if (array_key_exists('ZTO', $logisticNumbers)) {
  69. $ZTOLogisticNumbers = $logisticNumbers['ZTO'];
  70. foreach ($ZTOLogisticNumbers as $logisticNumber) {
  71. LogisticZopSync::dispatch($logisticNumber);
  72. }
  73. }
  74. //更新韵达
  75. if (array_key_exists('YUNDA', $logisticNumbers)) {
  76. $YDLogisticNumbers = $logisticNumbers['YUNDA'];
  77. foreach ($YDLogisticNumbers as $logistic_number) {
  78. LogisticYDSync::dispatch($logistic_number);
  79. }
  80. }
  81. //更新圆通
  82. if (array_key_exists('YTO', $logisticNumbers)) {
  83. $YTOLogisticNumbers = $logisticNumbers['YTO'];
  84. foreach ($YTOLogisticNumbers as $logistic_number) {
  85. if ($logistic_number) LogisticYTOSync::dispatch($logistic_number);
  86. }
  87. }
  88. });
  89. }
  90. /**
  91. * 阿里云同步快递信息
  92. * @param array $logistic_numbers 指定单号同步
  93. */
  94. public function syncLogisticRouteByAliJiSu(array $logistic_numbers = [])
  95. {
  96. //没有指定单号
  97. if (empty($logistic_numbers)) {
  98. ini_set('max_execution_time', 2 * 60 * 60);
  99. $query = OrderPackage::query()
  100. ->select(['logistic_number', 'order_id', 'id'])
  101. ->whereIn('status', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, null])
  102. ->whereIn('exception_status', [0, 1, 2, 3, 4, 5, 6, 7, null])
  103. ->whereIn('order_id', function ($query) {
  104. $query->from('orders')->selectRaw('id')->whereIn('logistic_id', function ($builder) {
  105. $builder->from('logistics')->selectRaw('id')->where('type', '=', '快递')->whereNotIn('belong_company', ['顺丰', '中通', '韵达', '圆通', '京东']);
  106. });
  107. });
  108. $query = $query->where('created_at', '>=', now()->subDays(config('api_logistic.querying_days')))
  109. ->whereNull('received_at');
  110. } else {//指定了单号
  111. $query = OrderPackage::query()
  112. ->select(['logistic_number', 'order_id', 'id'])
  113. ->whereIn('logistic_number', $logistic_numbers);
  114. }
  115. $query->chunkById(200, function ($orderPackages) {
  116. foreach ($orderPackages as $orderPackage) {
  117. if ($orderPackage && $orderPackage->logistic_number) LogisticAliJiSuSync::dispatch($orderPackage->logistic_number);
  118. }
  119. });
  120. }
  121. public function syncLogisticRouteJD()
  122. {
  123. ini_set('max_execution_time', 60);
  124. $query = OrderPackage::query()
  125. ->select(['logistic_number', 'order_id', 'id'])
  126. ->whereIn('order_id', function ($query) {
  127. $query->from('orders')->selectRaw('id')->whereIn('logistic_id', function ($builder) {
  128. $builder->from('logistics')->selectRaw('id')->where('type', '!=', '物流')->where('belong_company', '京东');
  129. });
  130. });
  131. $query = $query->where('created_at', '>=', now()->subDays(config('api_logistic.querying_days')))
  132. ->whereNull('received_at')->where('logistic_number', 'like', 'JD%');
  133. $query->chunkById(200, function ($orderPackages) {
  134. foreach ($orderPackages as $orderPackage) {
  135. if ($orderPackage && $orderPackage->logistic_number) LogisticAliJiSuSync::dispatch($orderPackage->logistic_number);
  136. }
  137. });
  138. }
  139. /**
  140. * 根据快递单号更新状态
  141. * @param array $logisticResponses
  142. */
  143. public function update(array $logisticResponses)
  144. {
  145. foreach ($logisticResponses as $logisticResponse) {
  146. if (empty($logisticResponse)) continue;
  147. $orderPackage = OrderPackage::query()->where('logistic_number', $logisticResponse['logistic_number'])->first();
  148. if (empty($orderPackage)) continue;
  149. //如果已经收货,状态改为已签收
  150. if ($logisticResponse['received_at'] ?? false) {
  151. $logisticResponse['status'] = '已签收';
  152. }
  153. //设置异常信息
  154. $logisticResponse = $this->setExceptionStatus($logisticResponse);
  155. //标记为手动更新的 status不更新
  156. if ($orderPackage->is_manual_update) {
  157. //异常状态不更新
  158. if (isset($logisticResponse['exception_status'])) unset($logisticResponse['exception_status']);
  159. //状态允许向后更新
  160. if (OrderPackage::switchStatus($orderPackage->status) > OrderPackage::switchStatus($logisticResponse['status'] ?? '生成订单')) {
  161. unset($logisticResponse['status']);
  162. }
  163. }
  164. //标记为单号异常
  165. if (Str::contains($orderPackage->logistic_number, ['SO', '#', '-'])) {
  166. $logisticResponse['exception_status'] = '单号异常';
  167. }
  168. //没有状态的数据赋予初始状态
  169. if (((!isset($logisticResponse['status'])) || ($logisticResponse['status'] === '')) &&
  170. $orderPackage->status === '') {
  171. $logisticResponse['status'] = '生成订单';
  172. if (!empty($orderPackage->sent_at)) {
  173. $logisticResponse['status'] = '已复核';
  174. }
  175. if (!empty($orderPackage->weighed_at)) {
  176. $logisticResponse['status'] = '已称重';
  177. }
  178. }
  179. if (isset($logisticResponse['exception_status'])) $logisticResponse['exception_status'] = OrderPackage::switchExceptionStatus($logisticResponse['exception_status']);
  180. if (isset($logisticResponse['status'])) $logisticResponse['status'] = OrderPackage::switchStatus($logisticResponse['status']);
  181. if (isset($logisticResponse['routes_length'])) unset($logisticResponse['routes_length']);
  182. if (empty($logisticResponse['transfer_status'])) unset($logisticResponse['transfer_status']);
  183. if (isset($logisticResponse['transfer_status'])) {
  184. $logisticResponse['route_length'] = count($logisticResponse['transfer_status']);
  185. } else {
  186. $logisticResponse['route_length'] = 0;
  187. }
  188. /** @var OrderPackage $orderPackage */
  189. $orderPackage = OrderPackage::query()->where('logistic_number', $logisticResponse['logistic_number'])->first();
  190. if (isset($logisticResponse['transfer_status']) && isset($logisticResponse['logistic_number'])) {
  191. $orderPackageExpressRouteArr = [
  192. 'order_package_express_routes' => $logisticResponse['transfer_status'],
  193. 'logistics_number' => $logisticResponse['logistic_number'],
  194. ];
  195. try {
  196. $issue_time = $orderPackage->order->created_at ?? null;
  197. $check_time = $orderPackage->sent_at ?? null;
  198. $pickup_time = $orderPackageExpressRouteArr['order_package_express_routes'][0]['accept_time'] ?? null;
  199. $array_filter = array_filter($orderPackageExpressRouteArr['order_package_express_routes'], function ($item) {
  200. return (
  201. str_contains($item['remark'], '已发出')//中通
  202. || str_contains($item['accept_address'], '已发出')//中通
  203. || str_contains($item['remark'], '转运')//中通
  204. || str_contains($item['remark'], '完成分拣')//顺丰
  205. || str_contains($item['accept_address'], '转运')//圆通
  206. || str_contains($item['remark'], '已发往')//中通
  207. || str_contains($item['accept_address'], '已发往')//中通
  208. || str_contains($item['remark'], '中心')
  209. || str_contains($item['accept_address'], '中心'));
  210. }) ?? null;
  211. $transfer_time = null;
  212. if ($array_filter != null) {
  213. $transfer_time = $array_filter[array_key_first($array_filter)]['accept_time'] ?? null;
  214. }
  215. $receive_time = $logisticResponse['received_at'] ?? null;
  216. $orderPackageExpressRouteArr['issue_time'] = $issue_time;
  217. $orderPackageExpressRouteArr['check_time'] = $check_time;
  218. $orderPackageExpressRouteArr['pickup_time'] = $pickup_time;
  219. $orderPackageExpressRouteArr['transfer_time'] = $transfer_time;
  220. $orderPackageExpressRouteArr['receive_time'] = $receive_time;
  221. } catch (Exception $e) {
  222. Log::error("快递同步异常", ["msg" => $e->getMessage(), "json" => $orderPackageExpressRouteArr]);
  223. }
  224. $orderPackageExpressRoute = OrderPackageExpressRoute::query()->updateOrCreate(
  225. [
  226. 'logistics_number' => $logisticResponse['logistic_number'],
  227. ],
  228. $orderPackageExpressRouteArr);
  229. $orderPackage->order_package_express_route_id = $orderPackageExpressRoute->id;
  230. $orderPackage->save();
  231. }
  232. unset($logisticResponse['transfer_status']);
  233. OrderPackage::query()->where('logistic_number', $logisticResponse['logistic_number'])->update($logisticResponse);
  234. }
  235. }
  236. /**
  237. * 将orderPackage集合分类并摘取指定数据
  238. * @param Collection $orderPackages
  239. * @return array
  240. */
  241. private function buildData(Collection $orderPackages): array
  242. {
  243. $data = [];
  244. foreach ($orderPackages as $orderPackage) {
  245. try {
  246. $logisticCode = $orderPackage->order->logistic->code;
  247. } catch (Exception $e) {
  248. continue;
  249. }
  250. $key = config('api_logistic.logistic.' . $logisticCode);
  251. if (!isset($data[$key])) {
  252. $data[$key] = [];
  253. }
  254. $data[$key][] = $orderPackage->logistic_number;
  255. }
  256. return $data;
  257. }
  258. }