normalizePayloadForHash($payload), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); $existingRef = Db::name('enterprise_customer_order_refs') ->where('customer_id', (int)$customer['id']) ->where('external_order_no', $externalOrderNo) ->find(); if ($existingRef) { if (($existingRef['payload_hash'] ?? '') !== $payloadHash) { throw new \RuntimeException('external_order_no 已存在,但请求内容不一致'); } return [ 'idempotent' => true, 'order' => $this->buildOrderProgress((int)$customer['id'], $existingRef, (string)$customer['customer_code']), ]; } $serviceProvider = trim((string)($payload['service_provider'] ?? 'anxinyan')); if (!in_array($serviceProvider, ['anxinyan', 'zhongjian'], true)) { throw new \InvalidArgumentException('service_provider 无效'); } $pricePackageCode = trim((string)($payload['price_package_code'] ?? '')); try { $servicePackage = $this->pricePackageSnapshot($serviceProvider, $pricePackageCode); } catch (\RuntimeException $e) { throw new \InvalidArgumentException($e->getMessage()); } $product = $this->normalizeProduct((array)($payload['product_info'] ?? [])); $returnAddress = $this->normalizeReturnAddress((array)($payload['return_address'] ?? [])); $materials = $this->normalizeMaterials((array)($payload['materials'] ?? [])); $now = date('Y-m-d H:i:s'); $orderNo = 'AXY' . date('YmdHis') . mt_rand(100, 999); $appraisalNo = 'AXY-APP-' . date('Ymd') . '-' . mt_rand(1000, 9999); $estimated = date('Y-m-d H:i:s', strtotime(sprintf('+%d hours', (int)$servicePackage['sla_hours']))); $userId = (new EnterpriseCustomerService())->ensureVirtualUser($customer); $productName = $this->resolveProductName($product); Db::startTrans(); try { $orderId = (int)Db::name('orders')->insertGetId([ 'order_no' => $orderNo, 'appraisal_no' => $appraisalNo, 'user_id' => $userId, 'service_mode' => 'physical', 'service_provider' => $serviceProvider, 'payment_status' => 'paid', 'order_status' => 'pending_shipping', 'display_status' => '待寄送商品', 'estimated_finish_time' => $estimated, 'source_channel' => 'enterprise_push', 'source_customer_id' => $customer['customer_code'], 'price_package_id' => $servicePackage['price_package_id'], 'price_package_name' => $servicePackage['price_package_name'], 'price_package_code' => $servicePackage['price_package_code'], 'price_package_price' => $servicePackage['price_package_price'], 'pay_amount' => $servicePackage['pay_amount'], 'paid_at' => $now, 'created_at' => $now, 'updated_at' => $now, ]); Db::name('order_products')->insert(array_merge($product, [ 'order_id' => $orderId, 'product_name' => $productName, 'product_cover' => $materials[0]['file_url'] ?? '', 'created_at' => $now, 'updated_at' => $now, ])); $extra = (array)($payload['extra_info'] ?? []); Db::name('order_extras')->insert([ 'order_id' => $orderId, 'purchase_channel' => trim((string)($extra['purchase_channel'] ?? '')), 'purchase_price' => (float)($extra['purchase_price'] ?? 0), 'purchase_date' => $extra['purchase_date'] ?? null, 'usage_status' => trim((string)($extra['usage_status'] ?? '')), 'condition_desc' => trim((string)($extra['condition_desc'] ?? '')), 'has_accessories' => !empty($extra['has_accessories']) ? 1 : 0, 'accessories_json' => json_encode(array_values((array)($extra['accessories'] ?? [])), JSON_UNESCAPED_UNICODE), 'remark' => trim((string)($extra['remark'] ?? '')), 'created_at' => $now, 'updated_at' => $now, ]); if ($returnAddress) { Db::name('order_return_addresses')->insert(array_merge($returnAddress, [ 'order_id' => $orderId, 'user_address_id' => null, 'created_at' => $now, 'updated_at' => $now, ])); } $shippingTarget = (new WarehouseService())->bindOrderTarget( $orderId, $serviceProvider, !empty($product['category_id']) ? (int)$product['category_id'] : null, null ); $this->insertMaterials($orderId, $materials, $now); $this->insertTimelines($orderId, $now, $shippingTarget); $this->insertTask($orderId, $serviceProvider, $estimated, $now); $this->insertInboundLogistics($orderId, $this->normalizeInboundLogistics($payload), $now); Db::name('enterprise_customer_order_refs')->insert([ 'customer_id' => (int)$customer['id'], 'external_order_no' => $externalOrderNo, 'order_id' => $orderId, 'order_no' => $orderNo, 'appraisal_no' => $appraisalNo, 'payload_hash' => $payloadHash, 'created_at' => $now, 'updated_at' => $now, ]); Db::commit(); } catch (\Throwable $e) { Db::rollback(); throw $e; } (new EnterpriseWebhookService())->recordOrderEvent($orderId, 'order_created', [ 'product_name' => $productName, 'price_package_name' => $servicePackage['price_package_name'], 'pay_amount' => (float)$servicePackage['pay_amount'], ]); $ref = Db::name('enterprise_customer_order_refs')->where('order_id', $orderId)->find(); return [ 'idempotent' => false, 'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']), ]; } public function findOrder(array $customer, string $externalOrderNo = '', string $orderNo = ''): array { $query = Db::name('enterprise_customer_order_refs')->where('customer_id', (int)$customer['id']); if ($externalOrderNo !== '') { $query->where('external_order_no', $externalOrderNo); } elseif ($orderNo !== '') { $query->where('order_no', $orderNo); } else { throw new \InvalidArgumentException('external_order_no 或 order_no 不能为空'); } $ref = $query->find(); if (!$ref) { throw new \RuntimeException('订单不存在'); } return $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']); } public function cancelOrder(array $customer, array $payload): array { $externalOrderNo = trim((string)($payload['external_order_no'] ?? '')); if ($externalOrderNo === '') { throw new \InvalidArgumentException('external_order_no 不能为空'); } $cancelReason = trim((string)($payload['cancel_reason'] ?? '')); if (mb_strlen($cancelReason, 'UTF-8') > 255) { throw new \InvalidArgumentException('cancel_reason 不能超过 255 个字符'); } $ref = Db::name('enterprise_customer_order_refs') ->where('customer_id', (int)$customer['id']) ->where('external_order_no', $externalOrderNo) ->find(); if (!$ref) { throw new \RuntimeException('订单不存在'); } $now = date('Y-m-d H:i:s'); $nodeDesc = $cancelReason === '' ? '第三方客户取消订单。' : sprintf('第三方客户取消订单:%s', $cancelReason); $cancelled = true; Db::startTrans(); try { $order = Db::name('orders')->where('id', (int)$ref['order_id'])->lock(true)->find(); if (!$order) { throw new \RuntimeException('订单不存在'); } if ((string)$order['order_status'] === 'cancelled') { $cancelled = false; Db::commit(); } else { if ((string)$order['order_status'] !== 'pending_shipping') { throw new \InvalidArgumentException('当前订单状态不可取消'); } $inboundLogistics = Db::name('order_logistics') ->where('order_id', (int)$order['id']) ->where('logistics_type', 'send_to_center') ->where('tracking_no', '<>', '') ->lock(true) ->find(); if ($inboundLogistics) { throw new \InvalidArgumentException('订单已提交寄入运单,当前不可取消'); } Db::name('orders')->where('id', (int)$order['id'])->update([ 'order_status' => 'cancelled', 'display_status' => '已取消', 'cancelled_at' => $now, 'updated_at' => $now, ]); Db::name('appraisal_tasks') ->where('order_id', (int)$order['id']) ->where('status', 'pending') ->delete(); Db::name('order_timelines')->insert([ 'order_id' => (int)$order['id'], 'node_code' => 'cancelled', 'node_text' => '订单已取消', 'node_desc' => $nodeDesc, 'operator_type' => 'system', 'operator_id' => null, 'occurred_at' => $now, 'created_at' => $now, ]); Db::commit(); } } catch (\Throwable $e) { Db::rollback(); throw $e; } return [ 'cancelled' => $cancelled, 'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']), ]; } public function submitShipping(array $customer, array $payload): array { $externalOrderNo = trim((string)($payload['external_order_no'] ?? '')); $expressCompany = trim((string)($payload['express_company'] ?? '')); $trackingNo = trim((string)($payload['tracking_no'] ?? '')); if ($externalOrderNo === '') { throw new \InvalidArgumentException('external_order_no 不能为空'); } if ($expressCompany === '' || $trackingNo === '') { throw new \InvalidArgumentException('快递公司和运单号不能为空'); } $ref = Db::name('enterprise_customer_order_refs') ->where('customer_id', (int)$customer['id']) ->where('external_order_no', $externalOrderNo) ->find(); if (!$ref) { throw new \RuntimeException('订单不存在'); } $order = Db::name('orders')->where('id', (int)$ref['order_id'])->find(); if (!$order) { throw new \RuntimeException('订单不存在'); } if ((string)$order['order_status'] !== 'pending_shipping') { throw new \InvalidArgumentException('当前订单状态不支持提交运单'); } $existing = Db::name('order_logistics') ->where('order_id', (int)$order['id']) ->where('logistics_type', 'send_to_center') ->order('id', 'desc') ->find(); $sameLogistics = $existing && (string)$existing['express_company'] === $expressCompany && (string)$existing['tracking_no'] === $trackingNo; if ($sameLogistics) { return [ 'idempotent' => true, 'updated' => false, 'logistics' => $this->formatLogistics($existing), 'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']), ]; } $now = date('Y-m-d H:i:s'); $latestDesc = sprintf('客户已提交寄送运单:%s %s,等待鉴定中心签收。', $expressCompany, $trackingNo); $updated = (bool)$existing; $logisticsId = 0; $resetLogisticsSync = false; $idempotentLogistics = null; Db::startTrans(); try { $lockedOrder = Db::name('orders')->where('id', (int)$order['id'])->lock(true)->find(); if (!$lockedOrder) { throw new \RuntimeException('订单不存在'); } if ((string)$lockedOrder['order_status'] !== 'pending_shipping') { throw new \InvalidArgumentException('当前订单状态不支持提交运单'); } $existing = Db::name('order_logistics') ->where('order_id', (int)$order['id']) ->where('logistics_type', 'send_to_center') ->order('id', 'desc') ->lock(true) ->find(); $sameLogistics = $existing && (string)$existing['express_company'] === $expressCompany && (string)$existing['tracking_no'] === $trackingNo; if ($sameLogistics) { $idempotentLogistics = $existing; Db::commit(); } else { $updated = (bool)$existing; if ($existing) { $logisticsId = (int)$existing['id']; $resetLogisticsSync = true; Db::name('order_logistics')->where('id', $logisticsId)->update([ 'logistics_type' => 'send_to_center', 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, 'tracking_status' => 'submitted', 'latest_desc' => $latestDesc, 'latest_time' => $now, 'updated_at' => $now, ]); $nodeText = '已更新运单'; $nodeDesc = sprintf('客户更新了寄送运单:%s %s', $expressCompany, $trackingNo); } else { $logisticsId = (int)Db::name('order_logistics')->insertGetId([ 'order_id' => (int)$order['id'], 'logistics_type' => 'send_to_center', 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, 'tracking_status' => 'submitted', 'latest_desc' => $latestDesc, 'latest_time' => $now, 'created_at' => $now, 'updated_at' => $now, ]); $nodeText = '已提交运单'; $nodeDesc = sprintf('客户已提交寄送运单:%s %s', $expressCompany, $trackingNo); } Db::name('order_logistics_nodes')->insert([ 'logistics_id' => $logisticsId, 'node_time' => $now, 'node_desc' => $latestDesc, 'node_location' => '第三方', 'created_at' => $now, ]); Db::name('orders')->where('id', (int)$order['id'])->update([ 'display_status' => '已提交运单', 'updated_at' => $now, ]); Db::name('order_timelines')->insert([ 'order_id' => (int)$order['id'], 'node_code' => 'tracking_submitted', 'node_text' => $nodeText, 'node_desc' => $nodeDesc, 'operator_type' => 'system', 'operator_id' => null, 'occurred_at' => $now, 'created_at' => $now, ]); Db::commit(); } } catch (\Throwable $e) { Db::rollback(); throw $e; } if ($idempotentLogistics) { return [ 'idempotent' => true, 'updated' => false, 'logistics' => $this->formatLogistics($idempotentLogistics), 'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']), ]; } $syncService = new OrderLogisticsSyncService(); if ($resetLogisticsSync) { Db::name('order_logistics_syncs')->where('logistics_id', $logisticsId)->delete(); } $syncService->subscribeAsync($logisticsId); $logistics = Db::name('order_logistics')->where('id', $logisticsId)->find(); return [ 'idempotent' => false, 'updated' => $updated, 'logistics' => $this->formatLogistics($logistics), 'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']), ]; } public function saveReturnAddress(array $customer, array $payload): array { $externalOrderNo = trim((string)($payload['external_order_no'] ?? '')); if ($externalOrderNo === '') { throw new \InvalidArgumentException('external_order_no 不能为空'); } $returnAddress = $this->normalizeReturnAddress((array)($payload['return_address'] ?? [])); if (!$returnAddress) { throw new \InvalidArgumentException('return_address 不能为空'); } $ref = Db::name('enterprise_customer_order_refs') ->where('customer_id', (int)$customer['id']) ->where('external_order_no', $externalOrderNo) ->find(); if (!$ref) { throw new \RuntimeException('订单不存在'); } $order = Db::name('orders')->where('id', (int)$ref['order_id'])->find(); if (!$order) { throw new \RuntimeException('订单不存在'); } $returnLogistics = Db::name('order_logistics') ->where('order_id', (int)$order['id']) ->where('logistics_type', 'return_to_user') ->order('id', 'desc') ->find(); if (!empty($returnLogistics['tracking_no'])) { throw new \InvalidArgumentException('回寄运单已生成,当前不可再修改寄回地址'); } $existing = Db::name('order_return_addresses')->where('order_id', (int)$order['id'])->find(); $now = date('Y-m-d H:i:s'); $updated = (bool)$existing; $snapshot = array_merge($returnAddress, [ 'user_address_id' => null, ]); Db::startTrans(); try { if ($existing) { Db::name('order_return_addresses')->where('order_id', (int)$order['id'])->update(array_merge($snapshot, [ 'updated_at' => $now, ])); $nodeText = '已更新寄回地址'; } else { Db::name('order_return_addresses')->insert(array_merge($snapshot, [ 'order_id' => (int)$order['id'], 'created_at' => $now, 'updated_at' => $now, ])); $nodeText = '已确认寄回地址'; } Db::name('order_timelines')->insert([ 'order_id' => (int)$order['id'], 'node_code' => 'return_address_selected', 'node_text' => $nodeText, 'node_desc' => sprintf( '大客户已确认寄回地址:%s%s%s%s', $returnAddress['province'], $returnAddress['city'], $returnAddress['district'], $returnAddress['detail_address'] ), 'operator_type' => 'system', 'operator_id' => null, 'occurred_at' => $now, 'created_at' => $now, ]); Db::commit(); } catch (\Throwable $e) { Db::rollback(); throw $e; } return [ 'updated' => $updated, 'return_address' => $this->formatReturnAddress($snapshot), 'order' => $this->buildOrderProgress((int)$customer['id'], $ref, (string)$customer['customer_code']), ]; } public function buildOrderProgress(int $customerId, array $ref, string $customerCode = ''): array { $order = Db::name('orders')->where('id', (int)$ref['order_id'])->find(); if (!$order) { throw new \RuntimeException('订单不存在'); } $timeline = Db::name('order_timelines')->where('order_id', (int)$order['id'])->order('occurred_at', 'asc')->select()->toArray(); $sendLogistics = Db::name('order_logistics')->where('order_id', (int)$order['id'])->where('logistics_type', 'send_to_center')->order('id', 'desc')->find(); $returnLogistics = Db::name('order_logistics')->where('order_id', (int)$order['id'])->where('logistics_type', 'return_to_user')->order('id', 'desc')->find(); $returnAddress = Db::name('order_return_addresses')->where('order_id', (int)$order['id'])->find(); $report = Db::name('reports') ->where('order_id', (int)$order['id']) ->where('report_status', 'published') ->order('id', 'desc') ->find(); $verify = $report ? (Db::name('report_verifies')->where('report_id', (int)$report['id'])->find() ?: null) : null; return [ 'customer_id' => $customerCode !== '' ? $customerCode : (string)$customerId, 'customer_code' => $customerCode !== '' ? $customerCode : (string)$customerId, 'external_order_no' => (string)$ref['external_order_no'], 'order_id' => (int)$order['id'], 'order_no' => (string)$order['order_no'], 'appraisal_no' => (string)$order['appraisal_no'], 'order_status' => (string)$order['order_status'], 'display_status' => (string)$order['display_status'], 'payment_status' => (string)$order['payment_status'], 'price_package_name' => (string)($order['price_package_name'] ?? ''), 'price_package_code' => (string)($order['price_package_code'] ?? ''), 'price_package_price' => (float)($order['price_package_price'] ?? 0), 'pay_amount' => (float)$order['pay_amount'], 'estimated_finish_time' => (string)($order['estimated_finish_time'] ?? ''), 'created_at' => (string)$order['created_at'], 'timeline' => array_map(fn(array $item) => [ 'node_code' => (string)$item['node_code'], 'node_text' => (string)$item['node_text'], 'node_desc' => (string)$item['node_desc'], 'occurred_at' => (string)$item['occurred_at'], ], $timeline), 'inbound_logistics' => $this->formatLogistics($sendLogistics), 'return_address' => $returnAddress ? $this->formatReturnAddress($returnAddress) : null, 'return_logistics' => $this->formatLogistics($returnLogistics), 'report_summary' => $report ? [ 'report_no' => (string)$report['report_no'], 'report_title' => (string)$report['report_title'], 'report_status' => (string)$report['report_status'], 'publish_time' => (string)($report['publish_time'] ?? ''), 'verify_url' => (string)($verify['verify_url'] ?? ''), 'report_page_url' => (string)($verify['verify_qrcode_url'] ?? ''), 'verify_status' => (string)($verify['verify_status'] ?? ''), ] : null, ]; } private function normalizePayloadForHash(array $payload): array { ksort($payload); foreach ($payload as &$value) { if (is_array($value)) { $value = $this->normalizePayloadForHash($value); } } unset($value); return $payload; } private function normalizeProduct(array $product): array { $productName = trim((string)($product['product_name'] ?? '')); return [ 'category_id' => !empty($product['category_id']) ? (int)$product['category_id'] : null, 'category_name' => trim((string)($product['category_name'] ?? '')), 'brand_id' => !empty($product['brand_id']) ? (int)$product['brand_id'] : null, 'brand_name' => trim((string)($product['brand_name'] ?? '')), 'color' => trim((string)($product['color'] ?? '')), 'size_spec' => trim((string)($product['size_spec'] ?? '')), 'serial_no' => trim((string)($product['serial_no'] ?? '')), 'product_name' => $productName, ]; } private function normalizeReturnAddress(array $address): ?array { $requiredKeys = ['consignee', 'mobile', 'province', 'city', 'district', 'detail_address']; $hasAnyValue = false; foreach ($requiredKeys as $key) { if (trim((string)($address[$key] ?? '')) !== '') { $hasAnyValue = true; break; } } if (!$hasAnyValue) { return null; } foreach ($requiredKeys as $key) { if (trim((string)($address[$key] ?? '')) === '') { throw new \InvalidArgumentException("return_address.{$key} 不能为空"); } } return [ 'consignee' => trim((string)$address['consignee']), 'mobile' => trim((string)$address['mobile']), 'province' => trim((string)$address['province']), 'city' => trim((string)$address['city']), 'district' => trim((string)$address['district']), 'detail_address' => trim((string)$address['detail_address']), ]; } private function normalizeMaterials(array $materials): array { $list = []; foreach ($materials as $index => $item) { if (is_string($item)) { $url = trim($item); $itemCode = 'material_' . ($index + 1); $itemName = '鉴定资料'; } elseif (is_array($item)) { $url = trim((string)($item['file_url'] ?? $item['url'] ?? '')); $itemCode = trim((string)($item['item_code'] ?? 'material_' . ($index + 1))); $itemName = trim((string)($item['item_name'] ?? '鉴定资料')); } else { $url = ''; $itemCode = 'material_' . ($index + 1); $itemName = '鉴定资料'; } if ($url === '' || !preg_match('/^https?:\/\//i', $url)) { throw new \InvalidArgumentException('materials 只支持 http/https 图片 URL'); } $list[] = [ 'item_code' => $itemCode, 'item_name' => $itemName, 'file_url' => $url, 'thumbnail_url' => is_array($item) ? trim((string)($item['thumbnail_url'] ?? $url)) : $url, 'is_required' => is_array($item) && !empty($item['is_required']) ? 1 : 0, ]; } return $list; } private function normalizeInboundLogistics(array $payload): array { $logistics = (array)($payload['inbound_logistics'] ?? []); if (!empty($payload['express_company']) || !empty($payload['tracking_no'])) { $logistics = array_merge($logistics, [ 'express_company' => $payload['express_company'] ?? ($logistics['express_company'] ?? ''), 'tracking_no' => $payload['tracking_no'] ?? ($logistics['tracking_no'] ?? ''), ]); } return $logistics; } private function resolveProductName(array $product): string { $productName = trim((string)($product['product_name'] ?? '')); if ($productName !== '') { return $productName; } return trim(($product['brand_name'] ?? '') . ' ' . ($product['category_name'] ?? '')); } private function pricePackageSnapshot(string $serviceProvider, string $packageCode = ''): array { return (new AppraisalServicePricePackageService())->snapshotForOrder($serviceProvider, 0, $packageCode); } private function insertMaterials(int $orderId, array $materials, string $now): void { foreach ($materials as $item) { $uploadItemId = (int)Db::name('order_upload_items')->insertGetId([ 'order_id' => $orderId, 'template_id' => null, 'item_code' => $item['item_code'], 'item_name' => $item['item_name'], 'is_required' => $item['is_required'], 'source_type' => 'initial', 'status' => 'uploaded', 'created_at' => $now, 'updated_at' => $now, ]); Db::name('order_upload_files')->insert([ 'order_upload_item_id' => $uploadItemId, 'file_id' => md5($item['file_url']), 'file_url' => $item['file_url'], 'thumbnail_url' => $item['thumbnail_url'], 'quality_status' => 'uploaded', 'quality_message' => '', 'uploaded_by_user_id' => null, 'created_at' => $now, 'updated_at' => $now, ]); } } private function insertTimelines(int $orderId, string $now, array $shippingTarget): void { Db::name('order_timelines')->insertAll([ [ 'order_id' => $orderId, 'node_code' => 'created', 'node_text' => '下单成功', 'node_desc' => '大客户订单已推送并创建成功', 'operator_type' => 'system', 'operator_id' => null, 'occurred_at' => $now, 'created_at' => $now, ], [ 'order_id' => $orderId, 'node_code' => 'pending_shipping', 'node_text' => '待寄送商品', 'node_desc' => sprintf('请将商品寄送至%s', $shippingTarget['warehouse_name'] ?: '鉴定中心'), 'operator_type' => 'system', 'operator_id' => null, 'occurred_at' => $now, 'created_at' => $now, ], ]); } private function insertTask(int $orderId, string $serviceProvider, string $estimated, string $now): void { Db::name('appraisal_tasks')->insert([ 'order_id' => $orderId, 'task_stage' => 'first_review', 'service_provider' => $serviceProvider, 'status' => 'pending', 'assignee_id' => null, 'assignee_name' => '未分配', 'started_at' => null, 'submitted_at' => null, 'sla_deadline' => $estimated, 'is_overtime' => 0, 'created_at' => $now, 'updated_at' => $now, ]); } private function insertInboundLogistics(int $orderId, array $logistics, string $now): void { $expressCompany = trim((string)($logistics['express_company'] ?? '')); $trackingNo = trim((string)($logistics['tracking_no'] ?? '')); if ($expressCompany === '' || $trackingNo === '') { return; } $latestDesc = sprintf('客户已提交寄送运单:%s %s,等待鉴定中心签收。', $expressCompany, $trackingNo); $logisticsId = (int)Db::name('order_logistics')->insertGetId([ 'order_id' => $orderId, 'logistics_type' => 'send_to_center', 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, 'tracking_status' => 'submitted', 'latest_desc' => $latestDesc, 'latest_time' => $now, 'created_at' => $now, 'updated_at' => $now, ]); Db::name('order_logistics_nodes')->insert([ 'logistics_id' => $logisticsId, 'node_time' => $now, 'node_desc' => $latestDesc, 'node_location' => '', 'created_at' => $now, ]); } private function formatLogistics(?array $logistics): ?array { if (!$logistics) { return null; } return [ 'express_company' => (string)$logistics['express_company'], 'tracking_no' => (string)$logistics['tracking_no'], 'tracking_status' => (string)$logistics['tracking_status'], 'latest_desc' => (string)$logistics['latest_desc'], 'latest_time' => (string)($logistics['latest_time'] ?? ''), ]; } private function formatReturnAddress(array $item): array { return [ 'consignee' => (string)($item['consignee'] ?? ''), 'mobile' => (string)($item['mobile'] ?? ''), 'province' => (string)($item['province'] ?? ''), 'city' => (string)($item['city'] ?? ''), 'district' => (string)($item['district'] ?? ''), 'detail_address' => (string)($item['detail_address'] ?? ''), 'full_address' => trim(sprintf( '%s%s%s%s', $item['province'] ?? '', $item['city'] ?? '', $item['district'] ?? '', $item['detail_address'] ?? '' )), ]; } }