input('keyword', '')); $status = trim((string)$request->input('status', '')); $serviceProvider = trim((string)$request->input('service_provider', '')); $sourceChannel = $this->normalizeOrderSourceChannel((string)$request->input('source_channel', '')); $paginationEnabled = $request->input('page', null) !== null || $request->input('page_size', null) !== null; $page = max(1, (int)$request->input('page', 1)); $pageSize = max(1, min(100, (int)$request->input('page_size', 20))); $query = Db::name('orders') ->alias('o') ->leftJoin('order_products p', 'p.order_id = o.id') ->field([ 'o.id', 'o.order_no', 'o.appraisal_no', 'o.service_provider', 'o.order_status', 'o.display_status', 'o.estimated_finish_time', 'o.source_channel', 'o.source_customer_id', 'o.pay_amount', 'o.created_at', 'p.product_name', 'p.category_name', 'p.brand_name', ]) ->order('o.id', 'desc'); if ($keyword !== '') { $query->where(function ($builder) use ($keyword) { $builder->whereRaw( '(o.order_no LIKE :keyword_order OR o.appraisal_no LIKE :keyword_appraisal OR p.product_name LIKE :keyword_product)', [ 'keyword_order' => "%{$keyword}%", 'keyword_appraisal' => "%{$keyword}%", 'keyword_product' => "%{$keyword}%", ] ); }); } $warehouseStatusFilters = [ 'warehouse_active', 'warehouse_pending_inbound', 'warehouse_in_transit', 'warehouse_received', 'warehouse_pending_return', ]; $specialStatusFilters = array_merge(['returning', 'completed_signed'], $warehouseStatusFilters); if ($status !== '' && !in_array($status, $specialStatusFilters, true)) { $query->where('o.order_status', $status); } if (in_array($status, $warehouseStatusFilters, true)) { $warehouseActiveStatuses = [ 'pending_shipping', 'received', 'in_first_review', 'pending_supplement', 'in_final_review', 'generating_report', 'report_published', ]; if ($status === 'warehouse_in_transit') { $query->where('o.order_status', 'pending_shipping'); } elseif ($status === 'warehouse_pending_inbound') { $query->where('o.order_status', 'pending_shipping') ->where('o.source_channel', self::MANUAL_ENTRY_SOURCE); } elseif ($status === 'warehouse_received') { $query->whereIn('o.order_status', array_values(array_diff($warehouseActiveStatuses, ['pending_shipping', 'report_published']))); } elseif ($status === 'warehouse_pending_return') { $query->where('o.order_status', 'report_published'); } else { $query->whereIn('o.order_status', $warehouseActiveStatuses); } } if ($serviceProvider !== '') { $query->where('o.service_provider', $serviceProvider); } if ($sourceChannel !== '') { $query->where('o.source_channel', $sourceChannel); } $rows = $query->select()->toArray(); $orderIds = array_map('intval', array_column($rows, 'id')); $sendTrackingMap = $this->latestLogisticsMap($orderIds, 'send_to_center'); $returnTrackingMap = $this->latestLogisticsMap($orderIds, 'return_to_user'); $transferFlowMap = $this->latestTransferFlowMap($orderIds); $list = array_map(function (array $item) use ($sendTrackingMap, $returnTrackingMap, $transferFlowMap) { $orderId = (int)$item['id']; $sendTrackingNo = $sendTrackingMap[$orderId]['tracking_no'] ?? ''; $sendTrackingStatus = $sendTrackingMap[$orderId]['tracking_status'] ?? ''; $warehouseBucket = $this->warehouseOrderBucket( (string)$item['order_status'], $sendTrackingNo, $sendTrackingStatus, (string)($item['display_status'] ?? ''), (string)($item['source_channel'] ?? '') ); return [ 'id' => $orderId, 'order_no' => $item['order_no'], 'appraisal_no' => $item['appraisal_no'], 'product_name' => $item['product_name'] ?: '待完善物品信息', 'category_name' => $item['category_name'] ?: '', 'brand_name' => $item['brand_name'] ?: '', 'service_provider' => $item['service_provider'], 'service_provider_text' => $item['service_provider'] === 'zhongjian' ? '中检鉴定' : '实物鉴定', 'source_channel' => $this->normalizeOrderSourceChannel((string)($item['source_channel'] ?? '')), 'source_channel_text' => $this->sourceChannelText((string)($item['source_channel'] ?? '')), 'source_customer_id' => (string)($item['source_customer_id'] ?? ''), 'order_status' => $item['order_status'], 'display_status' => $this->displayStatus( (string)$item['order_status'], (string)$item['display_status'], $returnTrackingMap[$orderId]['tracking_no'] ?? '', $returnTrackingMap[$orderId]['tracking_status'] ?? '', ), 'internal_tag_no' => $transferFlowMap[$orderId]['internal_tag_no'] ?? '', 'warehouse_bucket' => $warehouseBucket, 'warehouse_bucket_text' => $this->warehouseOrderBucketText($warehouseBucket), 'estimated_finish_time' => $item['estimated_finish_time'], 'pay_amount' => (float)$item['pay_amount'], 'created_at' => $item['created_at'], ]; }, $rows); if ($status === 'returning') { $list = array_values(array_filter($list, function (array $item) { return $item['order_status'] === 'completed' && $item['display_status'] === '物品已寄回'; })); } if ($status === 'completed_signed') { $list = array_values(array_filter($list, function (array $item) { return $item['order_status'] === 'completed' && $item['display_status'] === '已完成'; })); } if (in_array($status, $warehouseStatusFilters, true)) { $list = array_values(array_filter($list, function (array $item) use ($status) { if ($status === 'warehouse_active') { return in_array($item['warehouse_bucket'], [ 'warehouse_pending_inbound', 'warehouse_in_transit', 'warehouse_received', 'warehouse_pending_return', ], true); } return $item['warehouse_bucket'] === $status; })); } $total = count($list); if ($paginationEnabled) { $offset = ($page - 1) * $pageSize; $list = array_slice($list, $offset, $pageSize); return api_success([ 'list' => $list, 'total' => $total, 'page' => $page, 'page_size' => $pageSize, ]); } return api_success([ 'list' => $list, ]); } public function detail(Request $request) { $id = (int)$request->input('id', 0); if (!$id) { return api_error('订单 ID 不能为空', 422); } $order = Db::name('orders')->where('id', $id)->find(); if (!$order) { return api_error('订单不存在', 404); } $product = Db::name('order_products')->where('order_id', $id)->find(); $extra = Db::name('order_extras')->where('order_id', $id)->find(); $sendLogistics = Db::name('order_logistics') ->where('order_id', $id) ->where('logistics_type', 'send_to_center') ->order('id', 'desc') ->find(); $returnLogistics = Db::name('order_logistics') ->where('order_id', $id) ->where('logistics_type', 'return_to_user') ->order('id', 'desc') ->find(); $transferFlow = Db::name('order_transfer_flows') ->where('order_id', $id) ->order('id', 'desc') ->find(); $timeline = Db::name('order_timelines') ->where('order_id', $id) ->order('occurred_at', 'asc') ->select() ->toArray(); $timeline = array_map(fn (array $item) => [ 'node_text' => $item['node_text'], 'node_desc' => $item['node_desc'], 'occurred_at' => $item['occurred_at'], ], $timeline); $supplement = Db::name('order_supplement_tasks')->where('order_id', $id)->order('id', 'desc')->find(); $supplementItems = []; if ($supplement) { $supplementItems = Db::name('order_supplement_task_items') ->where('task_id', $supplement['id']) ->select() ->toArray(); $supplementItems = array_map(fn (array $item) => [ 'item_name' => $item['item_name'], 'guide_text' => $item['guide_text'], ], $supplementItems); } $report = Db::name('reports')->where('order_id', $id)->order('id', 'desc')->find(); $hasPublishedOrderReport = $report && ($report['report_status'] ?? '') === 'published'; $canAttemptReturnLogistics = in_array($order['order_status'], ['report_published', 'completed'], true) && (($returnLogistics['tracking_status'] ?? '') !== 'received'); $shippingTarget = Db::name('order_shipping_targets')->where('order_id', $id)->find(); $returnAddress = Db::name('order_return_addresses')->where('order_id', $id)->find(); if (!$returnAddress) { $returnAddress = Db::name('user_addresses') ->where('user_id', (int)$order['user_id']) ->where('is_default', 1) ->order('id', 'desc') ->find() ?: Db::name('user_addresses') ->where('user_id', (int)$order['user_id']) ->order('id', 'desc') ->find(); if ($returnAddress) { $returnAddress = [ 'user_address_id' => (int)$returnAddress['id'], 'consignee' => $returnAddress['consignee'], 'mobile' => $returnAddress['mobile'], 'province' => $returnAddress['province'], 'city' => $returnAddress['city'], 'district' => $returnAddress['district'], 'detail_address' => $returnAddress['detail_address'], ]; } } $logisticsNodes = []; if ($sendLogistics) { $logisticsNodes = Db::name('order_logistics_nodes') ->where('logistics_id', $sendLogistics['id']) ->order('node_time', 'desc') ->select() ->toArray(); } $inboundAttachments = $this->inboundAttachments($id, $request); $returnLogisticsNodes = []; if ($returnLogistics) { $returnLogisticsNodes = Db::name('order_logistics_nodes') ->where('logistics_id', $returnLogistics['id']) ->order('node_time', 'desc') ->select() ->toArray(); } return api_success([ 'order_info' => [ 'id' => (int)$order['id'], 'order_no' => $order['order_no'], 'appraisal_no' => $order['appraisal_no'], 'service_provider' => $order['service_provider'], 'service_provider_text' => $order['service_provider'] === 'zhongjian' ? '中检鉴定' : '实物鉴定', 'source_channel' => $this->normalizeOrderSourceChannel((string)($order['source_channel'] ?? '')), 'source_channel_text' => $this->sourceChannelText((string)($order['source_channel'] ?? '')), 'source_customer_id' => (string)($order['source_customer_id'] ?? ''), 'order_status' => $order['order_status'], 'display_status' => $this->displayStatus( (string)$order['order_status'], (string)$order['display_status'], $returnLogistics['tracking_no'] ?? '', $returnLogistics['tracking_status'] ?? '', ), 'pay_amount' => (float)$order['pay_amount'], 'estimated_finish_time' => $order['estimated_finish_time'], 'created_at' => $order['created_at'], 'can_reassign_warehouse' => $order['order_status'] === 'pending_shipping' && empty($sendLogistics['tracking_no']), 'can_mark_received' => $order['order_status'] === 'pending_shipping' && (!empty($sendLogistics['tracking_no']) || ($order['source_channel'] ?? '') === 'enterprise_push'), 'can_submit_return_logistics' => $hasPublishedOrderReport && $canAttemptReturnLogistics, 'return_logistics_block_reason' => (!$hasPublishedOrderReport && $canAttemptReturnLogistics) ? '订单报告未发布前,物品不允许寄回' : '', 'can_mark_return_received' => $order['order_status'] === 'completed' && !empty($returnLogistics['tracking_no']) && ($returnLogistics['tracking_status'] ?? '') !== 'received', ], 'product_info' => [ 'product_name' => $product['product_name'] ?? '', 'category_id' => (int)($product['category_id'] ?? 0), 'category_name' => $product['category_name'] ?? '', 'brand_id' => (int)($product['brand_id'] ?? 0), 'brand_name' => $product['brand_name'] ?? '', 'color' => $product['color'] ?? '', 'size_spec' => $product['size_spec'] ?? '', 'serial_no' => $product['serial_no'] ?? '', ], 'extra_info' => [ 'purchase_channel' => $extra['purchase_channel'] ?? '', 'purchase_price' => (float)($extra['purchase_price'] ?? 0), 'usage_status' => $extra['usage_status'] ?? '', 'condition_desc' => $extra['condition_desc'] ?? '', 'remark' => $extra['remark'] ?? '', ], 'shipping_target' => $shippingTarget ? [ 'warehouse_id' => (int)($shippingTarget['warehouse_id'] ?? 0), 'warehouse_name' => $shippingTarget['warehouse_name'], 'warehouse_code' => $shippingTarget['warehouse_code'], 'receiver_name' => $shippingTarget['receiver_name'], 'receiver_mobile' => $shippingTarget['receiver_mobile'], 'full_address' => trim(sprintf( '%s%s%s%s', $shippingTarget['province'] ?? '', $shippingTarget['city'] ?? '', $shippingTarget['district'] ?? '', $shippingTarget['detail_address'] ?? '' )), 'service_time' => $shippingTarget['service_time'], 'notice' => $shippingTarget['notice'], ] : null, 'return_address' => $returnAddress ? [ 'user_address_id' => (int)($returnAddress['user_address_id'] ?? 0), 'consignee' => $returnAddress['consignee'], 'mobile' => $returnAddress['mobile'], 'full_address' => trim(sprintf( '%s%s%s%s', $returnAddress['province'] ?? '', $returnAddress['city'] ?? '', $returnAddress['district'] ?? '', $returnAddress['detail_address'] ?? '' )), ] : null, 'timeline' => $timeline, 'transfer_flow' => $transferFlow ? [ 'internal_tag_no' => (string)($transferFlow['internal_tag_no'] ?? ''), ] : null, 'logistics_info' => $sendLogistics ? [ 'express_company' => $sendLogistics['express_company'], 'tracking_no' => $sendLogistics['tracking_no'], 'tracking_status' => $sendLogistics['tracking_status'], 'tracking_status_text' => $this->trackingStatusText($sendLogistics['tracking_status'], 'send_to_center'), 'latest_desc' => $this->formatAdminLogisticsDesc( 'send_to_center', $sendLogistics['tracking_status'], $sendLogistics['express_company'], $sendLogistics['tracking_no'], $sendLogistics['latest_desc'] ), 'latest_time' => $sendLogistics['latest_time'], 'nodes' => array_map(fn (array $item) => [ 'node_time' => $item['node_time'], 'node_desc' => $this->formatAdminLogisticsDesc( 'send_to_center', $sendLogistics['tracking_status'], $sendLogistics['express_company'], $sendLogistics['tracking_no'], $item['node_desc'] ), 'node_location' => $item['node_location'], ], $logisticsNodes), ] : null, 'inbound_attachments' => $inboundAttachments, 'return_logistics' => $returnLogistics ? [ 'express_company' => $returnLogistics['express_company'], 'tracking_no' => $returnLogistics['tracking_no'], 'tracking_status' => $returnLogistics['tracking_status'], 'tracking_status_text' => $this->trackingStatusText($returnLogistics['tracking_status'], 'return_to_user'), 'latest_desc' => $this->formatAdminLogisticsDesc( 'return_to_user', $returnLogistics['tracking_status'], $returnLogistics['express_company'], $returnLogistics['tracking_no'], $returnLogistics['latest_desc'] ), 'latest_time' => $returnLogistics['latest_time'], 'nodes' => array_map(fn (array $item) => [ 'node_time' => $item['node_time'], 'node_desc' => $this->formatAdminLogisticsDesc( 'return_to_user', $returnLogistics['tracking_status'], $returnLogistics['express_company'], $returnLogistics['tracking_no'], $item['node_desc'] ), 'node_location' => $item['node_location'], ], $returnLogisticsNodes), ] : null, 'supplement_task' => $supplement ? [ 'reason' => $supplement['reason'], 'deadline' => $supplement['deadline'], 'status' => $supplement['status'], 'items' => $supplementItems, ] : null, 'report_summary' => $report ? [ 'id' => (int)$report['id'], 'report_no' => $report['report_no'], 'report_title' => $report['report_title'], 'report_status' => $report['report_status'], 'report_status_text' => $this->reportStatusText((string)$report['report_status']), 'publish_time' => $report['publish_time'], ] : null, ]); } public function warehouseOptions(Request $request) { $id = (int)$request->input('id', 0); if ($id <= 0) { return api_error('订单 ID 不能为空', 422); } $order = Db::name('orders')->where('id', $id)->find(); if (!$order) { return api_error('订单不存在', 404); } $product = Db::name('order_products')->where('order_id', $id)->find(); $options = (new WarehouseService())->optionsForOrder( (string)($order['service_provider'] ?? 'anxinyan'), !empty($product['category_id']) ? (int)$product['category_id'] : null ); return api_success([ 'list' => $options, ]); } public function reassignWarehouse(Request $request) { $id = (int)$request->input('id', 0); $warehouseId = (int)$request->input('warehouse_id', 0); if ($id <= 0 || $warehouseId <= 0) { return api_error('订单 ID 和仓库 ID 不能为空', 422); } $order = Db::name('orders')->where('id', $id)->find(); if (!$order) { return api_error('订单不存在', 404); } $logistics = Db::name('order_logistics') ->where('order_id', $id) ->where('logistics_type', 'send_to_center') ->order('id', 'desc') ->find(); if ($order['order_status'] !== 'pending_shipping' || !empty($logistics['tracking_no'])) { return api_error('当前订单已进入寄送流程,暂不支持改派仓库', 422); } $warehouse = Db::name('shipping_warehouses') ->where('id', $warehouseId) ->where('status', 'enabled') ->find(); if (!$warehouse) { return api_error('目标仓库不存在或已停用', 404); } $product = Db::name('order_products')->where('order_id', $id)->find(); $categoryId = !empty($product['category_id']) ? (int)$product['category_id'] : null; $allowedWarehouses = (new WarehouseService())->optionsForOrder((string)$order['service_provider'], $categoryId); $allowedIds = array_column($allowedWarehouses, 'id'); if (!in_array($warehouseId, $allowedIds, true)) { return api_error('目标仓库不适用于当前订单服务类型或品类', 422); } $currentTarget = Db::name('order_shipping_targets')->where('order_id', $id)->find(); if ($currentTarget && (int)($currentTarget['warehouse_id'] ?? 0) === $warehouseId) { return api_error('当前订单已绑定该仓库,无需重复改派', 422); } $snapshot = [ 'warehouse_id' => (int)$warehouse['id'], 'warehouse_name' => $warehouse['warehouse_name'], 'warehouse_code' => $warehouse['warehouse_code'], 'receiver_name' => $warehouse['receiver_name'], 'receiver_mobile' => $warehouse['receiver_mobile'], 'province' => $warehouse['province'], 'city' => $warehouse['city'], 'district' => $warehouse['district'], 'detail_address' => $warehouse['detail_address'], 'service_time' => $warehouse['service_time'], 'notice' => $warehouse['notice'], ]; $now = date('Y-m-d H:i:s'); Db::startTrans(); try { (new WarehouseService())->bindOrderTarget($id, (string)$order['service_provider'], $categoryId); Db::name('order_shipping_targets')->where('order_id', $id)->update([ 'warehouse_id' => $snapshot['warehouse_id'], 'warehouse_name' => $snapshot['warehouse_name'], 'warehouse_code' => $snapshot['warehouse_code'], 'service_provider' => $order['service_provider'], 'receiver_name' => $snapshot['receiver_name'], 'receiver_mobile' => $snapshot['receiver_mobile'], 'province' => $snapshot['province'], 'city' => $snapshot['city'], 'district' => $snapshot['district'], 'detail_address' => $snapshot['detail_address'], 'service_time' => $snapshot['service_time'], 'notice' => $snapshot['notice'], 'updated_at' => $now, ]); Db::name('order_timelines')->insert([ 'order_id' => $id, 'node_code' => 'warehouse_reassigned', 'node_text' => '仓库已改派', 'node_desc' => sprintf('订单收货仓库已改派至 %s', $snapshot['warehouse_name']), 'operator_type' => 'admin', 'operator_id' => (int)$request->header('x-admin-id', 0) ?: null, 'occurred_at' => $now, 'created_at' => $now, ]); Db::commit(); } catch (\Throwable $e) { Db::rollback(); return api_error('仓库改派失败', 500, [ 'detail' => $e->getMessage(), ]); } return api_success([ 'id' => $id, 'warehouse_id' => $snapshot['warehouse_id'], 'warehouse_name' => $snapshot['warehouse_name'], ], '仓库已改派'); } public function receiveLogistics(Request $request) { $id = (int)$request->input('id', 0); if ($id <= 0) { return api_error('订单 ID 不能为空', 422); } $order = Db::name('orders')->where('id', $id)->find(); if (!$order) { return api_error('订单不存在', 404); } $logistics = Db::name('order_logistics') ->where('order_id', $id) ->where('logistics_type', 'send_to_center') ->order('id', 'desc') ->find(); $allowEnterpriseManualReceive = ($order['source_channel'] ?? '') === 'enterprise_push'; if ((!$logistics || $logistics['tracking_no'] === '') && !$allowEnterpriseManualReceive) { return api_error('当前订单还没有有效运单信息', 422); } if ($order['order_status'] !== 'pending_shipping') { return api_error('当前订单状态不支持标记签收', 422); } $now = date('Y-m-d H:i:s'); $latestDesc = '鉴定中心已签收包裹,等待鉴定师开始处理。'; Db::startTrans(); try { if ($logistics) { Db::name('order_logistics')->where('id', $logistics['id'])->update([ 'tracking_status' => 'received', 'latest_desc' => $latestDesc, 'latest_time' => $now, 'updated_at' => $now, ]); $logisticsId = (int)$logistics['id']; } else { $latestDesc = '大客户推送订单已确认到仓,等待鉴定师开始处理。'; $logisticsId = (int)Db::name('order_logistics')->insertGetId([ 'order_id' => $id, 'logistics_type' => 'send_to_center', 'express_company' => '', 'tracking_no' => '', 'tracking_status' => 'received', '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, ]); Db::name('orders')->where('id', $id)->update([ 'order_status' => 'in_first_review', 'display_status' => '鉴定中', 'updated_at' => $now, ]); $taskUpdate = [ 'status' => 'processing', 'updated_at' => $now, ]; $task = Db::name('appraisal_tasks') ->where('order_id', $id) ->where('task_stage', 'first_review') ->order('id', 'asc') ->find(); if ($task && empty($task['started_at'])) { $taskUpdate['started_at'] = $now; } if ($task) { Db::name('appraisal_tasks')->where('id', (int)$task['id'])->update($taskUpdate); } Db::name('order_timelines')->insert([ 'order_id' => $id, 'node_code' => 'first_review', 'node_text' => '鉴定中', 'node_desc' => $logistics ? '包裹已由鉴定中心签收,订单已进入鉴定流程' : '大客户推送订单已确认到仓,订单已进入鉴定流程', 'operator_type' => 'admin', 'operator_id' => (int)$request->header('x-admin-id', 0) ?: null, 'occurred_at' => $now, 'created_at' => $now, ]); Db::commit(); } catch (\Throwable $e) { Db::rollback(); return api_error('标记签收失败', 500, [ 'detail' => $e->getMessage(), ]); } (new EnterpriseWebhookService())->recordOrderEvent($id, 'inbound_received', [ 'express_company' => (string)($logistics['express_company'] ?? ''), 'tracking_no' => (string)($logistics['tracking_no'] ?? ''), 'received_at' => $now, ]); return api_success(['id' => $id], '已标记鉴定中心签收'); } public function saveReturnLogistics(Request $request) { $id = (int)$request->input('id', 0); $expressCompany = trim((string)$request->input('express_company', '')); $trackingNo = trim((string)$request->input('tracking_no', '')); if ($id <= 0 || $expressCompany === '' || $trackingNo === '') { return api_error('订单、快递公司和运单号不能为空', 422); } $order = Db::name('orders')->where('id', $id)->find(); if (!$order) { return api_error('订单不存在', 404); } if (!in_array($order['order_status'], ['report_published', 'completed'], true)) { return api_error('当前订单状态不支持登记回寄运单', 422); } $report = Db::name('reports')->where('order_id', $id)->order('id', 'desc')->find(); if (!$report || ($report['report_status'] ?? '') !== 'published') { return api_error('订单报告未发布前,物品不允许寄回', 422); } $returnAddress = Db::name('order_return_addresses')->where('order_id', $id)->find(); if (!$returnAddress) { $fallbackAddress = Db::name('user_addresses') ->where('user_id', (int)$order['user_id']) ->where('is_default', 1) ->order('id', 'desc') ->find() ?: Db::name('user_addresses') ->where('user_id', (int)$order['user_id']) ->order('id', 'desc') ->find(); if (!$fallbackAddress) { return api_error('当前订单尚未确认寄回地址,且用户账户下没有可用地址', 422); } $returnAddress = [ 'user_address_id' => (int)$fallbackAddress['id'], 'consignee' => $fallbackAddress['consignee'], 'mobile' => $fallbackAddress['mobile'], 'province' => $fallbackAddress['province'], 'city' => $fallbackAddress['city'], 'district' => $fallbackAddress['district'], 'detail_address' => $fallbackAddress['detail_address'], ]; } $existing = Db::name('order_logistics') ->where('order_id', $id) ->where('logistics_type', 'return_to_user') ->order('id', 'desc') ->find(); $now = date('Y-m-d H:i:s'); $latestDesc = sprintf('平台已通过 %s 回寄商品,运单号 %s。', $expressCompany, $trackingNo); Db::startTrans(); try { $existingReturnAddress = Db::name('order_return_addresses')->where('order_id', $id)->find(); if (!$existingReturnAddress) { Db::name('order_return_addresses')->insert([ 'order_id' => $id, 'user_address_id' => $returnAddress['user_address_id'] ?? null, 'consignee' => $returnAddress['consignee'] ?? '', 'mobile' => $returnAddress['mobile'] ?? '', 'province' => $returnAddress['province'] ?? '', 'city' => $returnAddress['city'] ?? '', 'district' => $returnAddress['district'] ?? '', 'detail_address' => $returnAddress['detail_address'] ?? '', 'created_at' => $now, 'updated_at' => $now, ]); } if ($existing) { Db::name('order_logistics')->where('id', $existing['id'])->update([ 'logistics_type' => 'return_to_user', 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, 'tracking_status' => 'in_transit', 'latest_desc' => $latestDesc, 'latest_time' => $now, 'updated_at' => $now, ]); $logisticsId = (int)$existing['id']; $nodeText = '已更新回寄运单'; $nodeDesc = sprintf('平台更新回寄运单:%s %s', $expressCompany, $trackingNo); } else { $logisticsId = (int)Db::name('order_logistics')->insertGetId([ 'order_id' => $id, 'logistics_type' => 'return_to_user', 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, 'tracking_status' => 'in_transit', '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' => $returnAddress['city'] ?? '用户地址', 'created_at' => $now, ]); Db::name('orders')->where('id', $id)->update([ 'order_status' => 'completed', 'display_status' => '物品已寄回', 'updated_at' => $now, ]); Db::name('order_timelines')->insert([ 'order_id' => $id, 'node_code' => 'return_shipped', 'node_text' => $nodeText, 'node_desc' => $nodeDesc, 'operator_type' => 'admin', 'operator_id' => (int)$request->header('x-admin-id', 0) ?: null, 'occurred_at' => $now, 'created_at' => $now, ]); (new MessageDispatcher())->sendInboxEvent('return_shipped', [ 'user_id' => (int)($order['user_id'] ?? 0), 'biz_type' => 'return_shipped', 'biz_id' => $id, 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, 'fallback_title' => '鉴定物品已寄回', 'fallback_content' => sprintf('平台已通过%s回寄鉴定物品,运单号 %s,可前往订单详情查看物流进度。', $expressCompany, $trackingNo), ]); Db::commit(); } catch (\Throwable $e) { Db::rollback(); return api_error('回寄运单登记失败', 500, [ 'detail' => $e->getMessage(), ]); } (new EnterpriseWebhookService())->recordOrderEvent($id, 'return_shipped', [ 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, 'shipped_at' => $now, ]); return api_success([ 'id' => $id, 'express_company' => $expressCompany, 'tracking_no' => $trackingNo, ], '回寄运单已登记'); } public function receiveReturnLogistics(Request $request) { $id = (int)$request->input('id', 0); if ($id <= 0) { return api_error('订单 ID 不能为空', 422); } $order = Db::name('orders')->where('id', $id)->find(); if (!$order) { return api_error('订单不存在', 404); } $logistics = Db::name('order_logistics') ->where('order_id', $id) ->where('logistics_type', 'return_to_user') ->order('id', 'desc') ->find(); if (!$logistics || $logistics['tracking_no'] === '') { return api_error('当前订单还没有有效回寄运单', 422); } if (($logistics['tracking_status'] ?? '') === 'received') { return api_error('当前订单已标记用户签收,无需重复操作', 422); } $now = date('Y-m-d H:i:s'); $latestDesc = '用户已签收回寄商品,本次订单已完成。'; Db::startTrans(); try { Db::name('order_logistics')->where('id', $logistics['id'])->update([ 'tracking_status' => 'received', 'latest_desc' => $latestDesc, 'latest_time' => $now, 'updated_at' => $now, ]); Db::name('order_logistics_nodes')->insert([ 'logistics_id' => $logistics['id'], 'node_time' => $now, 'node_desc' => $latestDesc, 'node_location' => '用户地址', 'created_at' => $now, ]); Db::name('orders')->where('id', $id)->update([ 'order_status' => 'completed', 'display_status' => '已完成', 'updated_at' => $now, ]); Db::name('order_timelines')->insert([ 'order_id' => $id, 'node_code' => 'return_received', 'node_text' => '用户已签收', 'node_desc' => '回寄商品已由用户签收,本次订单已完成。', 'operator_type' => 'admin', 'operator_id' => (int)$request->header('x-admin-id', 0) ?: null, 'occurred_at' => $now, 'created_at' => $now, ]); (new MessageDispatcher())->sendInboxEvent('return_received', [ 'user_id' => (int)($order['user_id'] ?? 0), 'biz_type' => 'return_received', 'biz_id' => $id, 'fallback_title' => '回寄商品已签收', 'fallback_content' => '系统已确认您签收回寄商品,本次鉴定订单已完成。', ]); Db::commit(); } catch (\Throwable $e) { Db::rollback(); return api_error('标记用户签收失败', 500, [ 'detail' => $e->getMessage(), ]); } (new EnterpriseWebhookService())->recordOrderEvent($id, 'completed', [ 'express_company' => (string)($logistics['express_company'] ?? ''), 'tracking_no' => (string)($logistics['tracking_no'] ?? ''), 'completed_at' => $now, ]); return api_success(['id' => $id], '已标记用户签收'); } public function createManualOrder(Request $request) { $serviceProvider = $this->normalizeServiceProvider((string)$request->input('service_provider', 'anxinyan')); $productInput = $this->requestArray($request, 'product_info'); $extraInput = $this->requestArray($request, 'extra_info'); $returnAddressInput = $this->requestArray($request, 'return_address'); $materialsInput = $request->input('materials', []); $materials = is_array($materialsInput) ? $materialsInput : []; $categoryId = (int)($productInput['category_id'] ?? 0); $brandId = (int)($productInput['brand_id'] ?? 0); $productName = trim((string)($productInput['product_name'] ?? '')); $consignee = trim((string)($returnAddressInput['consignee'] ?? '')); $mobile = trim((string)($returnAddressInput['mobile'] ?? '')); $province = trim((string)($returnAddressInput['province'] ?? '')); $city = trim((string)($returnAddressInput['city'] ?? '')); $district = trim((string)($returnAddressInput['district'] ?? '')); $detailAddress = trim((string)($returnAddressInput['detail_address'] ?? '')); if ($serviceProvider === '') { return api_error('服务类型不正确', 422); } if ($categoryId <= 0 || $brandId <= 0 || $productName === '') { return api_error('请完整填写品类、品牌和商品名称', 422); } if ($consignee === '' || $mobile === '' || $province === '' || $city === '' || $district === '' || $detailAddress === '') { return api_error('请完整填写寄回收件信息', 422); } $category = Db::name('catalog_categories')->where('id', $categoryId)->find(); if (!$category) { return api_error('品类不存在', 422); } $brand = Db::name('catalog_brands')->where('id', $brandId)->find(); if (!$brand) { return api_error('品牌不存在', 422); } $now = date('Y-m-d H:i:s'); $serviceConfig = $this->serviceConfig($serviceProvider); $orderNo = $this->generateOrderNo(); $appraisalNo = $this->generateAppraisalNo(); $estimated = date('Y-m-d H:i:s', strtotime(sprintf('+%d hours', (int)$serviceConfig['sla_hours']))); $operatorId = (int)$request->header('x-admin-id', 0) ?: null; Db::startTrans(); try { $user = $this->resolveManualOrderUser($consignee, $mobile, $now); $addressId = $this->ensureUserAddress((int)$user['id'], [ 'consignee' => $consignee, 'mobile' => $mobile, 'province' => $province, 'city' => $city, 'district' => $district, 'detail_address' => $detailAddress, ], $now); $orderId = (int)Db::name('orders')->insertGetId([ 'order_no' => $orderNo, 'appraisal_no' => $appraisalNo, 'user_id' => (int)$user['id'], 'service_mode' => 'physical', 'service_provider' => $serviceProvider, 'payment_status' => 'paid', 'order_status' => 'pending_shipping', 'display_status' => '待入库', 'estimated_finish_time' => $estimated, 'source_channel' => self::MANUAL_ENTRY_SOURCE, 'source_customer_id' => '', 'pay_amount' => (float)$serviceConfig['price'], 'paid_at' => $now, 'created_at' => $now, 'updated_at' => $now, ]); Db::name('order_products')->insert([ 'order_id' => $orderId, 'category_id' => $categoryId, 'category_name' => (string)$category['name'], 'brand_id' => $brandId, 'brand_name' => (string)$brand['name'], 'color' => trim((string)($productInput['color'] ?? '')), 'size_spec' => trim((string)($productInput['size_spec'] ?? '')), 'serial_no' => trim((string)($productInput['serial_no'] ?? '')), 'product_name' => $productName, 'product_cover' => '', 'created_at' => $now, 'updated_at' => $now, ]); Db::name('order_extras')->insert([ 'order_id' => $orderId, 'purchase_channel' => trim((string)($extraInput['purchase_channel'] ?? '')), 'purchase_price' => (float)($extraInput['purchase_price'] ?? 0), 'purchase_date' => null, 'usage_status' => trim((string)($extraInput['usage_status'] ?? '')), 'condition_desc' => trim((string)($extraInput['condition_desc'] ?? '')), 'has_accessories' => 0, 'accessories_json' => json_encode([], JSON_UNESCAPED_UNICODE), 'remark' => trim((string)($extraInput['remark'] ?? '')), 'created_at' => $now, 'updated_at' => $now, ]); Db::name('order_return_addresses')->insert([ 'order_id' => $orderId, 'user_address_id' => $addressId, 'consignee' => $consignee, 'mobile' => $mobile, 'province' => $province, 'city' => $city, 'district' => $district, 'detail_address' => $detailAddress, 'created_at' => $now, 'updated_at' => $now, ]); $shippingTarget = (new WarehouseService())->bindOrderTarget($orderId, $serviceProvider, $categoryId, [ 'province' => $province, 'city' => $city, 'district' => $district, 'detail_address' => $detailAddress, ]); $this->insertManualOrderMaterials($orderId, $materials, $now); 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, ]); Db::name('order_timelines')->insertAll([ [ 'order_id' => $orderId, 'node_code' => 'manual_created', 'node_text' => '补录订单已创建', 'node_desc' => '后台已补录订单资料,等待仓管入库。', 'operator_type' => 'admin', 'operator_id' => $operatorId, 'occurred_at' => $now, 'created_at' => $now, ], [ 'order_id' => $orderId, 'node_code' => 'pending_inbound', 'node_text' => '待入库', 'node_desc' => sprintf('可使用订单号或鉴定单号匹配入库,目标仓库:%s。', $shippingTarget['warehouse_name'] ?: '鉴定中心'), 'operator_type' => 'system', 'operator_id' => null, 'occurred_at' => $now, 'created_at' => $now, ], ]); Db::commit(); } catch (\Throwable $e) { Db::rollback(); return api_error('补录订单创建失败', 500, ['detail' => $e->getMessage()]); } return api_success([ 'order_id' => $orderId, 'order_no' => $orderNo, 'appraisal_no' => $appraisalNo, 'user_id' => (int)$user['id'], 'next_status' => 'pending_shipping', ], '补录订单已创建'); } public function manualOrderMeta(Request $request) { $categories = Db::name('catalog_categories') ->field(['id', 'name', 'code', 'is_enabled', 'supported_service_types']) ->where('is_enabled', 1) ->order('sort_order', 'asc') ->select() ->toArray(); $brands = Db::name('catalog_brands') ->alias('b') ->leftJoin('catalog_brand_categories cbc', 'cbc.brand_id = b.id') ->field([ 'b.id', 'b.name', 'b.en_name', 'b.code', 'b.is_enabled', 'b.supported_service_types', 'GROUP_CONCAT(DISTINCT cbc.category_id) AS category_ids', ]) ->where('b.is_enabled', 1) ->group('b.id') ->order('b.sort_order', 'asc') ->select() ->toArray(); return api_success([ 'categories' => array_map(fn (array $item) => [ 'id' => (int)$item['id'], 'name' => (string)$item['name'], 'code' => (string)$item['code'], 'supported_service_types' => $this->decodeJsonArray($item['supported_service_types'] ?? null), ], $categories), 'brands' => array_map(fn (array $item) => [ 'id' => (int)$item['id'], 'name' => (string)$item['name'], 'en_name' => (string)($item['en_name'] ?? ''), 'code' => (string)($item['code'] ?? ''), 'category_ids' => $this->decodeIntList($item['category_ids'] ?? ''), 'supported_service_types' => $this->decodeJsonArray($item['supported_service_types'] ?? null), ], $brands), ]); } public function uploadManualOrderFile(Request $request) { try { return api_success((new AppraisalEvidenceService())->upload($request)); } catch (\Throwable $e) { return api_error($e->getMessage(), 422); } } private function inboundAttachments(int $orderId, Request $request): array { $logs = Db::name('order_transfer_flow_logs') ->where('order_id', $orderId) ->where('action_code', 'inbound_received') ->order('id', 'desc') ->select() ->toArray(); $attachments = []; foreach ($logs as $log) { $payload = $this->decodeJsonObject($log['payload_json'] ?? null); foreach ($this->decodeJsonArray($payload['inbound_attachments'] ?? []) as $item) { if (is_array($item)) { $attachments[] = $item; } } } $normalized = (new AppraisalEvidenceService())->normalize($attachments, $request); return array_values(array_filter($normalized, function (array $item) { return in_array((string)($item['file_type'] ?? ''), ['image', 'video'], true); })); } private function decodeJsonArray(mixed $value): array { if (is_array($value)) { return array_values($value); } if (is_string($value) && $value !== '') { $decoded = json_decode($value, true); return is_array($decoded) ? array_values($decoded) : []; } return []; } private function decodeJsonObject(mixed $value): array { if (is_array($value)) { return $value; } if (is_string($value) && $value !== '') { $decoded = json_decode($value, true); return is_array($decoded) ? $decoded : []; } return []; } private function decodeIntList(mixed $value): array { if (is_array($value)) { return array_values(array_filter(array_map('intval', $value), fn (int $item) => $item > 0)); } if (!is_string($value) || trim($value) === '') { return []; } return array_values(array_filter(array_map('intval', explode(',', $value)), fn (int $item) => $item > 0)); } private function requestArray(Request $request, string $key): array { $value = $request->input($key, []); return is_array($value) ? $value : []; } private function normalizeServiceProvider(string $serviceProvider): string { $serviceProvider = trim($serviceProvider); return in_array($serviceProvider, ['anxinyan', 'zhongjian'], true) ? $serviceProvider : ''; } private function serviceConfig(string $serviceProvider): array { $configs = [ 'anxinyan' => ['price' => 99.00, 'sla_hours' => 48], 'zhongjian' => ['price' => 199.00, 'sla_hours' => 72], ]; return $configs[$serviceProvider] ?? $configs['anxinyan']; } private function generateOrderNo(): string { do { $orderNo = 'AXY' . date('YmdHis') . mt_rand(100, 999); } while (Db::name('orders')->where('order_no', $orderNo)->find()); return $orderNo; } private function generateAppraisalNo(): string { do { $appraisalNo = 'AXY-APP-' . date('Ymd') . '-' . mt_rand(1000, 9999); } while (Db::name('orders')->where('appraisal_no', $appraisalNo)->find()); return $appraisalNo; } private function resolveManualOrderUser(string $consignee, string $mobile, string $now): array { $user = Db::name('users') ->where('mobile', $mobile) ->whereNull('deleted_at') ->find(); if ($user) { return $user; } $userId = (int)Db::name('users')->insertGetId([ 'nickname' => $consignee, 'avatar' => '', 'mobile' => $mobile, 'password' => '', 'status' => 'enabled', 'last_login_at' => null, 'created_at' => $now, 'updated_at' => $now, ]); return Db::name('users')->where('id', $userId)->find(); } private function ensureUserAddress(int $userId, array $address, string $now): int { $existing = Db::name('user_addresses') ->where('user_id', $userId) ->where('consignee', (string)$address['consignee']) ->where('mobile', (string)$address['mobile']) ->where('province', (string)$address['province']) ->where('city', (string)$address['city']) ->where('district', (string)$address['district']) ->where('detail_address', (string)$address['detail_address']) ->find(); if ($existing) { return (int)$existing['id']; } $hasDefault = Db::name('user_addresses') ->where('user_id', $userId) ->where('is_default', 1) ->find(); return (int)Db::name('user_addresses')->insertGetId([ 'user_id' => $userId, 'consignee' => (string)$address['consignee'], 'mobile' => (string)$address['mobile'], 'province' => (string)$address['province'], 'city' => (string)$address['city'], 'district' => (string)$address['district'], 'detail_address' => (string)$address['detail_address'], 'is_default' => $hasDefault ? 0 : 1, 'created_at' => $now, 'updated_at' => $now, ]); } private function insertManualOrderMaterials(int $orderId, array $materials, string $now): void { $evidenceService = new AppraisalEvidenceService(); foreach ($materials as $index => $item) { if (!is_array($item)) { continue; } $files = $evidenceService->normalize($item['files'] ?? [], null, true); if (!$files) { continue; } $orderUploadId = (int)Db::name('order_upload_items')->insertGetId([ 'order_id' => $orderId, 'template_id' => null, 'item_code' => trim((string)($item['item_code'] ?? 'manual_material_' . ($index + 1))), 'item_name' => trim((string)($item['item_name'] ?? '补录资料')), 'is_required' => !empty($item['is_required']) ? 1 : 0, 'source_type' => 'initial', 'status' => 'uploaded', 'created_at' => $now, 'updated_at' => $now, ]); foreach ($files as $file) { Db::name('order_upload_files')->insert([ 'order_upload_item_id' => $orderUploadId, 'file_id' => (string)($file['file_id'] ?? ''), 'file_url' => ltrim((string)($file['file_url'] ?? ''), '/'), 'thumbnail_url' => ltrim((string)($file['thumbnail_url'] ?? ''), '/'), 'quality_status' => 'uploaded', 'quality_message' => '', 'uploaded_by_user_id' => null, 'created_at' => $now, 'updated_at' => $now, ]); } } } private function trackingStatusText(string $status, string $logisticsType = 'send_to_center'): string { if ($logisticsType === 'return_to_user') { return match ($status) { 'submitted' => '已登记回寄运单', 'in_transit' => '回寄途中', 'received' => '用户已签收', default => $status === '' ? '待回寄' : $status, }; } return match ($status) { 'submitted' => '用户已提交运单', 'in_transit' => '用户已寄出,运输中', 'received' => '鉴定中心已签收', default => $status === '' ? '待提交' : $status, }; } private function reportStatusText(string $status): string { return match ($status) { 'draft' => '草稿中', 'pending_publish' => '待发布', 'published' => '已发布', 'updated' => '已更新', 'invalid' => '已作废', default => $status, }; } private function displayStatus(string $orderStatus, string $displayStatus, string $returnTrackingNo = '', string $returnTrackingStatus = ''): string { if ($orderStatus === 'report_published') { return '待寄回'; } if ($orderStatus === 'completed') { if ($returnTrackingStatus === 'received') { return '已完成'; } if ($returnTrackingNo !== '') { return '物品已寄回'; } } return $displayStatus; } private function latestLogisticsMap(array $orderIds, string $logisticsType): array { $orderIds = array_values(array_unique(array_filter(array_map('intval', $orderIds)))); if (!$orderIds) { return []; } $rows = Db::name('order_logistics') ->whereIn('order_id', $orderIds) ->where('logistics_type', $logisticsType) ->order('id', 'desc') ->select() ->toArray(); $map = []; foreach ($rows as $row) { $orderId = (int)($row['order_id'] ?? 0); if ($orderId > 0 && !isset($map[$orderId])) { $map[$orderId] = [ 'tracking_no' => (string)($row['tracking_no'] ?? ''), 'tracking_status' => (string)($row['tracking_status'] ?? ''), ]; } } return $map; } private function latestTransferFlowMap(array $orderIds): array { $orderIds = array_values(array_unique(array_filter(array_map('intval', $orderIds)))); if (!$orderIds) { return []; } $rows = Db::name('order_transfer_flows') ->whereIn('order_id', $orderIds) ->order('id', 'desc') ->select() ->toArray(); $map = []; foreach ($rows as $row) { $orderId = (int)($row['order_id'] ?? 0); if ($orderId > 0 && !isset($map[$orderId])) { $map[$orderId] = [ 'internal_tag_no' => (string)($row['internal_tag_no'] ?? ''), ]; } } return $map; } private function warehouseOrderBucket( string $orderStatus, string $sendTrackingNo = '', string $sendTrackingStatus = '', string $displayStatus = '', string $sourceChannel = '' ): string { if ($orderStatus === 'pending_shipping') { if ($sourceChannel === self::MANUAL_ENTRY_SOURCE && $sendTrackingNo === '') { return 'warehouse_pending_inbound'; } $hasSubmittedTracking = $sendTrackingNo !== '' && $sendTrackingStatus !== 'received'; $hasSubmittedDisplayStatus = in_array($displayStatus, ['已提交运单', '用户已提交运单'], true) && $sendTrackingStatus !== 'received'; if ($hasSubmittedTracking || $hasSubmittedDisplayStatus) { return 'warehouse_in_transit'; } } if (in_array($orderStatus, [ 'received', 'in_first_review', 'pending_supplement', 'in_final_review', 'generating_report', ], true)) { return 'warehouse_received'; } if ($orderStatus === 'report_published') { return 'warehouse_pending_return'; } return ''; } private function warehouseOrderBucketText(string $bucket): string { return match ($bucket) { 'warehouse_pending_inbound' => '待入库', 'warehouse_in_transit' => '在途', 'warehouse_received' => '已入仓', 'warehouse_pending_return' => '待寄回', default => '', }; } private function normalizeOrderSourceChannel(string $sourceChannel): string { $sourceChannel = trim($sourceChannel); $aliases = [ 'wechat_mini_program' => 'mini_program', 'weixin_mini_program' => 'mini_program', 'mp_weixin' => 'mini_program', 'miniapp' => 'mini_program', 'user_app' => 'mini_program', 'web_h5' => 'h5', 'enterprise' => 'enterprise_push', 'enterprise_order' => 'enterprise_push', 'customer_push' => 'enterprise_push', 'large_customer_push' => 'enterprise_push', 'manual' => self::MANUAL_ENTRY_SOURCE, 'manual_order' => self::MANUAL_ENTRY_SOURCE, 'manual_entry' => self::MANUAL_ENTRY_SOURCE, ]; $sourceChannel = $aliases[$sourceChannel] ?? $sourceChannel; return in_array($sourceChannel, ['mini_program', 'h5', 'enterprise_push', self::MANUAL_ENTRY_SOURCE], true) ? $sourceChannel : ''; } private function sourceChannelText(string $sourceChannel): string { return match ($this->normalizeOrderSourceChannel($sourceChannel)) { 'mini_program' => '小程序', 'h5' => 'H5', 'enterprise_push' => '大客户推送订单', self::MANUAL_ENTRY_SOURCE => '后台补录订单', default => '未知渠道', }; } private function formatAdminLogisticsDesc(string $logisticsType, string $status, string $expressCompany, string $trackingNo, string $fallback): string { $expressCompany = trim($expressCompany); $trackingNo = trim($trackingNo); if ($logisticsType === 'return_to_user') { if (in_array($status, ['submitted', 'in_transit'], true) && $expressCompany !== '' && $trackingNo !== '') { return sprintf('平台已登记回寄运单:%s %s,商品正在回寄途中。', $expressCompany, $trackingNo); } if ($status === 'received') { return '用户已签收回寄商品,订单已完成。'; } return $fallback; } if ($status === 'submitted' && $expressCompany !== '' && $trackingNo !== '') { return sprintf('用户已提交寄送运单:%s %s,等待鉴定中心签收。', $expressCompany, $trackingNo); } if ($status === 'in_transit' && $expressCompany !== '' && $trackingNo !== '') { return sprintf('用户已寄出商品:%s %s,当前运输中。', $expressCompany, $trackingNo); } if ($status === 'received') { return '鉴定中心已签收包裹,等待鉴定师开始处理。'; } return $fallback; } }