client = new WxTradeClient(); } public function syncOrderShipping($orderId, $triggerItemId = 0) { if (!Bridge::isEnabled()) { return false; } $order = Order::with('item')->where('id', $orderId)->find(); if (!$order || !Bridge::shouldHandleOrder($order)) { return false; } $items = []; if (!empty($order->item)) { foreach ($order->item as $item) { $items[] = is_object($item) && method_exists($item, 'toArray') ? $item->toArray() : (array) $item; } } $triggerItem = null; if ($triggerItemId) { foreach ($items as $item) { $itemArr = is_object($item) ? $item->toArray() : $item; if (intval($itemArr['id']) === intval($triggerItemId)) { $triggerItem = $itemArr; break; } } } $built = WxShippingPayload::buildUploadPayload($order, $items, $triggerItem); $payload = $built['payload']; $logId = SyncLogger::create($order, 'shipping', $payload, $triggerItemId, $built['meta']['delivery_mode']); try { $response = $this->client->postJson('/wxa/sec/order/upload_shipping_info', $payload); $this->client->callOrThrow($response, '上传发货信息'); SyncLogger::markSuccess($logId, $response, $payload); return true; } catch (\Exception $e) { $response = isset($response) && is_array($response) ? $response : []; SyncLogger::markFailed($logId, $e, $response, $payload); Log::error('[shopro_wechat_trade] shipping sync fail order#' . $orderId . ': ' . $e->getMessage()); throw $e; } } public function syncOrderConfirm($orderId) { if (!Bridge::isEnabled()) { return false; } if (SyncLogger::hasSuccessfulConfirm($orderId)) { return true; } $order = Order::with('item')->where('id', $orderId)->find(); if (!$order || !Bridge::shouldHandleOrder($order)) { return false; } $pending = OrderItem::where('order_id', $orderId) ->where('dispatch_status', '<>', OrderItem::DISPATCH_STATUS_GETED) ->count(); if ($pending > 0) { return false; } $payload = WxShippingPayload::buildConfirmPayload($order); $logId = SyncLogger::create($order, 'confirm', $payload); try { $response = $this->client->postJson('/wxa/sec/order/notify_confirm_receive', $payload); $this->client->callOrThrow($response, '同步确认收货'); SyncLogger::markSuccess($logId, $response, $payload); return true; } catch (\Exception $e) { $response = isset($response) && is_array($response) ? $response : []; SyncLogger::markFailed($logId, $e, $response, $payload); Log::error('[shopro_wechat_trade] confirm sync fail order#' . $orderId . ': ' . $e->getMessage()); throw $e; } } public function backfillShippedOrders($limit = 50) { $rows = Db::name('shopro_order') ->alias('o') ->join('shopro_order_item oi', 'oi.order_id = o.id') ->where('o.platform', 'wxMiniProgram') ->where('o.pay_type', 'in', ['wechat', 'huifu_wechat']) ->where('o.status', '>', 0) ->where('oi.dispatch_status', '>=', OrderItem::DISPATCH_STATUS_SENDED) ->group('o.id') ->order('o.id', 'desc') ->limit($limit) ->column('o.id'); $count = 0; foreach ($rows as $orderId) { $hasSuccess = Db::name('shopro_wechat_trade_sync') ->where('order_id', $orderId) ->where('sync_type', 'shipping') ->where('status', 'success') ->find(); if ($hasSuccess) { continue; } try { $this->syncOrderShipping($orderId); $count++; } catch (\Exception $e) { // logged } } return $count; } public function retryFailed($limit = 50) { $now = time(); $rows = Db::name('shopro_wechat_trade_sync') ->where('status', 'pending') ->where('retry_count', '>', 0) ->where('next_retry_time', '<=', $now) ->order('id', 'asc') ->limit($limit) ->select(); $count = 0; foreach ($rows as $row) { try { if ($row['sync_type'] === 'confirm') { $this->syncOrderConfirm($row['order_id']); } else { $this->syncOrderShipping($row['order_id'], $row['order_item_id']); } $count++; } catch (\Exception $e) { // logged } } return $count; } }