From 137b719187e8575caa0680c01c75768806ea6605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=98=8A=E5=A4=A9?= <442384644@qq.com> Date: Thu, 22 Oct 2026 13:18:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=94=AE=E5=90=8E):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E6=94=B6=E8=B4=A7=E7=8A=B6=E6=80=81=E4=B8=8E?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E6=B5=81=E6=B0=B4=E5=8F=B7=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 退货退款/换货确认收货后按 receive_time 展示待退款/待补发,并切换确认收货、执行退款、补发按钮 - 后台填写的退款流水号写入售后单,退款完成时优先使用,不再被系统退款单号覆盖 - 退款流水号限制 64 个字符 --- .../app/services/order/AfterSaleServices.php | 35 ++++++++++++++++--- .../services/order/RefundEffectServices.php | 10 ++++-- .../services/order/RefundOrderServices.php | 13 ++++++- .../src/pages/order/afterSale/detail.vue | 20 +++++++---- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/backend/app/services/order/AfterSaleServices.php b/backend/app/services/order/AfterSaleServices.php index 7c98c9358..e63c4ad96 100644 --- a/backend/app/services/order/AfterSaleServices.php +++ b/backend/app/services/order/AfterSaleServices.php @@ -696,6 +696,29 @@ class AfterSaleServices extends BaseService 'completed' => '已完成', ]; + /** + * 解析售后单展示状态文案 + * 退货退款/换货在商家确认收货后仍沿用 receiving 状态(退款/补发动作依赖该状态), + * 展示层按 receive_time 细分为“待退款”“待补发”,避免确认收货后仍显示“待收货”。 + * @param array|object $afterSale 售后单数据 + * @return string + */ + protected function resolveStatusText($afterSale): string + { + $data = is_object($afterSale) ? $afterSale->toArray() : (array)$afterSale; + $status = (string)($data['status'] ?? ''); + $type = (string)($data['type'] ?? ''); + if ($status === 'receiving' && (int)($data['receive_time'] ?? 0) > 0) { + if ($type === 'return_refund') { + return '待退款'; + } + if ($type === 'exchange') { + return '待补发'; + } + } + return self::STATUS_TEXT_MAP[$status] ?? $status; + } + /** * 售后状态分组映射 * 用于用户端列表 tab 筛选:processing=处理中,completed=已完成,canceled=已取消 @@ -908,7 +931,7 @@ class AfterSaleServices extends BaseService 'type' => $type, 'type_text' => self::TYPE_TEXT_MAP[$type] ?? $type, 'status' => $status, - 'status_text' => self::STATUS_TEXT_MAP[$status] ?? $status, + 'status_text' => $this->resolveStatusText($afterSale), 'refund_price' => (float)($afterSale['refund_price'] ?? 0), 'actual_refund_price' => (float)($afterSale['actual_refund_price'] ?? 0), // 退款标的物与退还积分:积分订单售后展示退还积分数 @@ -933,6 +956,8 @@ class AfterSaleServices extends BaseService ], 'timeline' => $timeline, 'actions' => $actions, + // 商家确认收货时间:前端据此区分“待收货”与“待退款/待补发” + 'receive_time' => (int)($afterSale['receive_time'] ?? 0), 'add_time' => (int)($afterSale['add_time'] ?? 0), ]; } @@ -1786,7 +1811,7 @@ class AfterSaleServices extends BaseService $specOrders = []; foreach ($list as &$item) { $item['type_text'] = self::TYPE_TEXT_MAP[$item['type'] ?? ''] ?? ($item['type'] ?? ''); - $item['status_text'] = self::STATUS_TEXT_MAP[$item['status'] ?? ''] ?? ($item['status'] ?? ''); + $item['status_text'] = $this->resolveStatusText($item); $item['status_group'] = $this->resolveStatusGroup($item['status'] ?? ''); // 产品类型中文名:动态从注册中心获取,新增产品类型注册即生效 $productType = $item['product_type'] ?? ''; @@ -1945,7 +1970,7 @@ class AfterSaleServices extends BaseService // 附加文案 foreach ($list as &$item) { $item['type_text'] = self::TYPE_TEXT_MAP[$item['type'] ?? ''] ?? ($item['type'] ?? ''); - $item['status_text'] = self::STATUS_TEXT_MAP[$item['status'] ?? ''] ?? ($item['status'] ?? ''); + $item['status_text'] = $this->resolveStatusText($item); } unset($item); @@ -2507,7 +2532,7 @@ class AfterSaleServices extends BaseService $refundStatus = 1; } $item['refund_status'] = $refundStatus; - $item['refund_status_text'] = self::STATUS_TEXT_MAP[$status] ?? $status; + $item['refund_status_text'] = $this->resolveStatusText($item); $item['refund_type_text'] = self::TYPE_TEXT_MAP[$item['type'] ?? ''] ?? ($item['type'] ?? ''); // 旧 _status 结构:-1=申请中 -2=已退款 -3=已拒绝 @@ -2560,7 +2585,7 @@ class AfterSaleServices extends BaseService // 旧结构兼容键 $detail['order_id'] = $detail['after_sale_no'] ?? ''; $detail['store_order_id'] = (int)($detail['order_center_id'] ?? 0); - $detail['refund_status_text'] = self::STATUS_TEXT_MAP[$detail['status'] ?? ''] ?? ($detail['status'] ?? ''); + $detail['refund_status_text'] = $this->resolveStatusText($detail); $detail['cartInfo'] = $detail['cart_info'] = $this->tidyOutItems($detail['product_snapshot'] ?? []); $detail['refunded_price'] = $detail['actual_refund_price'] ?? '0.00'; $detail['_add_time'] = !empty($detail['add_time']) ? date('Y-m-d H:i:s', (int)$detail['add_time']) : ''; diff --git a/backend/app/services/order/RefundEffectServices.php b/backend/app/services/order/RefundEffectServices.php index 7a35cb851..38ddfec1a 100644 --- a/backend/app/services/order/RefundEffectServices.php +++ b/backend/app/services/order/RefundEffectServices.php @@ -174,16 +174,22 @@ class RefundEffectServices extends BaseService implements RefundEffectContextInt $handler->applyRefundEffects($this, $lockedArray, $afterSale, $order, $items); $now = time(); + // 后台手工填写的退款流水号优先;未填写时才回退渠道退款号/系统退款单号 + $refundContext = (array)($lockedArray['context'] ?? []); + $manualTradeNo = trim((string)($refundContext['refund_trade_no'] ?? '')); + $refundTradeNo = $manualTradeNo !== '' + ? $manualTradeNo + : (string)($locked['channel_refund_id'] ?: $locked['refund_no']); $this->afterSaleDao->update((int)$afterSale['id'], [ 'status' => 'completed', 'actual_refund_price' => (string)$locked['amount'], 'refund_type' => (string)$locked['refund_method'], - 'refund_trade_no' => (string)($locked['channel_refund_id'] ?: $locked['refund_no']), + 'refund_trade_no' => $refundTradeNo, 'refund_time' => $now, 'complete_time' => $now, ]); - $handlerContext = (array)($lockedArray['context'] ?? []); + $handlerContext = $refundContext; $handlerContext['order_no'] = (string)$afterSale['order_no']; $handlerContext['refund_no'] = (string)$locked['refund_no']; $handlerContext['total_refund_amount'] = $projectedTotal; diff --git a/backend/app/services/order/RefundOrderServices.php b/backend/app/services/order/RefundOrderServices.php index 447afdce0..86d896270 100644 --- a/backend/app/services/order/RefundOrderServices.php +++ b/backend/app/services/order/RefundOrderServices.php @@ -43,6 +43,12 @@ class RefundOrderServices extends BaseService throw new \RuntimeException('退款金额格式错误'); } $amount = bcadd((string)$amount, '0', 2); + // 后台选填的退款流水号:去空格并限制字段长度(eb_after_sale.refund_trade_no varchar(64)) + $refundTradeNo = trim((string)($context['refund_trade_no'] ?? '')); + if ($refundTradeNo !== '' && mb_strlen($refundTradeNo) > 64) { + throw new \RuntimeException('退款流水号不能超过64个字符'); + } + $context['refund_trade_no'] = $refundTradeNo; $refund = $this->transaction(function () use ($afterSaleId, $amount, $context) { $afterSale = $this->afterSaleDao->getByIdForUpdate($afterSaleId); if (!$afterSale) { @@ -115,7 +121,12 @@ class RefundOrderServices extends BaseService 'add_time' => time(), 'update_time' => time(), ]); - $this->afterSaleDao->update($afterSaleId, ['status' => 'refunding']); + $afterSaleUpdate = ['status' => 'refunding']; + if ((string)($context['refund_trade_no'] ?? '') !== '') { + // 先落库后台填写的流水号,退款处理中即可在详情看到,成功回调也不会被覆盖 + $afterSaleUpdate['refund_trade_no'] = (string)$context['refund_trade_no']; + } + $this->afterSaleDao->update($afterSaleId, $afterSaleUpdate); /** @var OrderCenterServices $centerServices */ $centerServices = $this->app->make(OrderCenterServices::class); $centerServices->syncStatus((string)$afterSale['order_no'], OrderCenter::STATUS_REFUNDING, [ diff --git a/frontend/admin/src/pages/order/afterSale/detail.vue b/frontend/admin/src/pages/order/afterSale/detail.vue index 8f3a44763..07725fb67 100644 --- a/frontend/admin/src/pages/order/afterSale/detail.vue +++ b/frontend/admin/src/pages/order/afterSale/detail.vue @@ -364,7 +364,7 @@ - +