Преглед на файлове

feat: 新增 PARTIAL_REFUND 支付状态,区分部分退款与全额退款

- OrderConstants / PayStatus 枚举新增 PARTIAL_REFUND 状态
- updateOrderRefundStatus 部分退款设置 PARTIAL_REFUND,
  全额退款设置 REFUND
- 退款验证同时接受 PAID 和 PARTIAL_REFUND 状态
- EntityLabelUtils / OrderServiceImpl 新增"部分退款"/"全额退款"标签
- 前端管理后台支付状态列新增部分退款/全额退款显示
- V5 迁移脚本同步更新字典数据

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline преди 2 седмици
родител
ревизия
51d3e71e38

+ 10 - 4
haha-admin-web/src/views/order/utils/hook.tsx

@@ -144,9 +144,10 @@ export function useOrder(tableRef: Ref) {
       minWidth: 85,
       cellRenderer: ({ row }) => {
         const statusMap: Record<string, { label: string; type: string }> = {
-          "UNPAID": { label: "支付", type: "warning" },
+          "UNPAID": { label: "支付", type: "warning" },
           "PAID": { label: "已支付", type: "success" },
-          "REFUND": { label: "已退款", type: "danger" }
+          "PARTIAL_REFUND": { label: "部分退款", type: "warning" },
+          "REFUND": { label: "全额退款", type: "danger" }
         };
         const status = statusMap[row.payStatus] || { label: "未知", type: "info" };
         return <el-tag type={status.type as any}>{status.label}</el-tag>;
@@ -312,9 +313,14 @@ export function useOrder(tableRef: Ref) {
       const order = data;
   
       const payStatusStyle = order.payStatus === "PAID"
-        ? "success" : order.payStatus === "REFUND"
+        ? "success" : order.payStatus === "PARTIAL_REFUND"
+        ? "warning" : order.payStatus === "REFUND"
         ? "danger" : "warning";
-      const payStatusText = order.payStatusLabel || (order.payStatus === "PAID" ? "已支付" : order.payStatus === "REFUND" ? "已退款" : "待支付");
+      const payStatusText = order.payStatusLabel || (
+        order.payStatus === "PAID" ? "已支付"
+        : order.payStatus === "PARTIAL_REFUND" ? "部分退款"
+        : order.payStatus === "REFUND" ? "全额退款"
+        : "未支付");
   
       const statusStyle = order.status === 1
         ? "success" : order.status === 0

+ 11 - 0
haha-admin/src/main/resources/db/migration/V5__create_refund_tables.sql

@@ -56,3 +56,14 @@ ADD COLUMN `current_refund_id` BIGINT DEFAULT NULL COMMENT '当前退款记录ID
 -- ============================================
 ALTER TABLE `t_fund_log`
 ADD COLUMN `refund_id` BIGINT DEFAULT NULL COMMENT '退款记录ID(关联 t_refund.id)' AFTER `order_no`;
+
+-- ============================================
+-- 字典数据:支付状态新增"部分退款","支付失败"改为"全额退款"
+-- 注意:字典使用数字编码(与代码字符串编码不一致,需后续统一)
+-- ============================================
+UPDATE `t_dict_data` SET `item_label` = '部分退款', `list_class` = 'warning'
+WHERE `dict_code` = 'pay_status' AND `item_value` = '2';
+
+INSERT IGNORE INTO `t_dict_data` (`dict_code`, `item_value`, `item_label`, `list_class`, `is_default`, `status`, `sort_order`, `create_by`)
+VALUES
+('pay_status', '3', '全额退款', 'danger', 0, 1, 4, 'system');

+ 9 - 3
haha-common/src/main/java/com/haha/common/constant/OrderConstants.java

@@ -40,9 +40,14 @@ public final class OrderConstants {
      * 支付状态:已支付
      */
     public static final String PAY_STATUS_PAID = "PAID";
-    
+
+    /**
+     * 支付状态:部分退款(累计退款金额 > 0 且 < 实付金额)
+     */
+    public static final String PAY_STATUS_PARTIAL_REFUND = "PARTIAL_REFUND";
+
     /**
-     * 支付状态:已退款
+     * 支付状态:全额退款(累计退款金额 >= 实付金额)
      */
     public static final String PAY_STATUS_REFUND = "REFUND";
     
@@ -83,7 +88,8 @@ public final class OrderConstants {
         return switch (payStatus) {
             case PAY_STATUS_UNPAID -> "未支付";
             case PAY_STATUS_PAID -> "已支付";
-            case PAY_STATUS_REFUND -> "已退款";
+            case PAY_STATUS_PARTIAL_REFUND -> "部分退款";
+            case PAY_STATUS_REFUND -> "全额退款";
             default -> "未知";
         };
     }

+ 4 - 2
haha-common/src/main/java/com/haha/common/enums/PayStatus.java

@@ -7,7 +7,8 @@ public enum PayStatus {
 
     UNPAID("UNPAID", "未支付"),
     PAID("PAID", "已支付"),
-    REFUND("REFUND", "已退款");
+    PARTIAL_REFUND("PARTIAL_REFUND", "部分退款"),
+    REFUND("REFUND", "全额退款");
 
     private final String code;
     private final String description;
@@ -38,8 +39,9 @@ public enum PayStatus {
         return UNPAID.code.equals(code);
     }
 
+    /** 已支付或部分退款均视为已付款状态 */
     public static boolean isPaid(String code) {
-        return PAID.code.equals(code);
+        return PAID.code.equals(code) || PARTIAL_REFUND.code.equals(code);
     }
 
     public static boolean isRefund(String code) {

+ 2 - 1
haha-common/src/main/java/com/haha/common/utils/EntityLabelUtils.java

@@ -85,7 +85,8 @@ public class EntityLabelUtils {
         return switch (payStatus) {
             case OrderConstants.PAY_STATUS_UNPAID -> StatusLabel.warning("未支付");
             case OrderConstants.PAY_STATUS_PAID -> StatusLabel.success("已支付");
-            case OrderConstants.PAY_STATUS_REFUND -> StatusLabel.danger("已退款");
+            case OrderConstants.PAY_STATUS_PARTIAL_REFUND -> StatusLabel.warning("部分退款");
+            case OrderConstants.PAY_STATUS_REFUND -> StatusLabel.danger("全额退款");
             default -> StatusLabel.info("未知");
         };
     }

+ 1 - 1
haha-miniapp/src/main/resources/sql/order.sql

@@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `t_order` (
   `user_id` BIGINT NOT NULL COMMENT '用户ID',
   `device_id` VARCHAR(64) NOT NULL COMMENT '设备序列号',
   `total_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单总金额',
-  `pay_status` VARCHAR(20) NOT NULL DEFAULT 'UNPAID' COMMENT '支付状态:UNPAID-未支付,PAID-已支付,REFUND-已退款',
+  `pay_status` VARCHAR(20) NOT NULL DEFAULT 'UNPAID' COMMENT '支付状态:UNPAID-未支付,PAID-已支付,PARTIAL_REFUND-部分退款,REFUND-全额退款',
   `video_url` VARCHAR(500) DEFAULT NULL COMMENT '购物视频URL',
   `confidence` DECIMAL(5,2) DEFAULT NULL COMMENT '识别置信度(0-100)',
   `items_json` TEXT DEFAULT NULL COMMENT '订单商品明细JSON',

+ 3 - 1
haha-service/src/main/java/com/haha/service/impl/OrderServiceImpl.java

@@ -222,8 +222,10 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
                 return "未支付";
             case OrderConstants.PAY_STATUS_PAID:
                 return "已支付";
+            case OrderConstants.PAY_STATUS_PARTIAL_REFUND:
+                return "部分退款";
             case OrderConstants.PAY_STATUS_REFUND:
-                return "已退款";
+                return "全额退款";
             default:
                 return "未知";
         }

+ 10 - 6
haha-service/src/main/java/com/haha/service/payment/impl/PaymentServiceImpl.java

@@ -166,13 +166,14 @@ public class PaymentServiceImpl implements PaymentService {
                     .build();
         }
 
-        // 3. 校验订单状态(必须是已支付)
-        if (!OrderConstants.PAY_STATUS_PAID.equals(order.getPayStatus())) {
+        // 3. 校验订单状态(已支付或部分退款均可继续退款)
+        if (!OrderConstants.PAY_STATUS_PAID.equals(order.getPayStatus())
+                && !OrderConstants.PAY_STATUS_PARTIAL_REFUND.equals(order.getPayStatus())) {
             log.error("退款失败: 订单状态不正确, orderId={}, payStatus={}", orderId, order.getPayStatus());
             return RefundResult.builder()
                     .success(false)
                     .errorCode("INVALID_ORDER_STATUS")
-                    .errorMsg("订单状态不正确,只有已支付订单可以退款")
+                    .errorMsg("订单状态不正确,只有已支付或部分退款订单可以退款")
                     .build();
         }
 
@@ -625,14 +626,17 @@ public class PaymentServiceImpl implements PaymentService {
         order.setRefundTime(LocalDateTime.now());
         order.setRefundReason(reason);
 
-        // 仅当累计退款 >= 实付金额时,才标记为全退款
+        // 仅当累计退款 >= 实付金额时,才标记为全退款
         BigDecimal paidAmount = order.getPaidAmount() != null ? order.getPaidAmount() : order.getTotalAmount();
         if (cumulativeAmount.compareTo(paidAmount) >= 0) {
             order.setPayStatus(OrderConstants.PAY_STATUS_REFUND);
-            log.info("订单全部退款完成 - orderId={}, cumulativeAmount={}, paidAmount={}",
+            log.info("订单全额退款完成 - orderId={}, cumulativeAmount={}, paidAmount={}",
+                    order.getId(), cumulativeAmount, paidAmount);
+        } else {
+            order.setPayStatus(OrderConstants.PAY_STATUS_PARTIAL_REFUND);
+            log.info("订单部分退款 - orderId={}, cumulativeAmount={}, paidAmount={}",
                     order.getId(), cumulativeAmount, paidAmount);
         }
-        // 部分退款时保持 payStatus = PAID,允许继续退款
 
         orderService.updateById(order);
         log.info("订单退款状态已更新 - orderId={}, refundAmount={}(累计), payStatus={}",

+ 3 - 2
haha-service/src/main/java/com/haha/service/payment/payscore/impl/PayScoreServiceImpl.java

@@ -530,8 +530,9 @@ public class PayScoreServiceImpl implements PayScoreService {
             return PayScoreResult.fail("ORDER_NOT_CREATED", "该订单未创建支付分服务订单");
         }
 
-        if (!OrderConstants.PAY_STATUS_PAID.equals(order.getPayStatus())) {
-            return PayScoreResult.fail("ORDER_NOT_PAID", "只有已支付订单可以退款");
+        if (!OrderConstants.PAY_STATUS_PAID.equals(order.getPayStatus())
+                && !OrderConstants.PAY_STATUS_PARTIAL_REFUND.equals(order.getPayStatus())) {
+            return PayScoreResult.fail("ORDER_NOT_PAID", "只有已支付或部分退款订单可以退款");
         }
 
         // 检查 7 天退款期限