Browse Source

支付:1.增加锁,避免重复支付等问题2.支付成功,处理拼团逻辑增加补偿机制。失败了,定时任务进行补偿,保证数据一致性

lc 1 tuần trước cách đây
mục cha
commit
30eaaa0a6c

+ 6 - 0
mall-service/src/main/java/com/txz/mall/core/cache/RedisKey.java

@@ -0,0 +1,6 @@
+package com.txz.mall.core.cache;
+
+public interface RedisKey {
+
+  String pay_lock = "pay:lock:payLock:";
+}

+ 9 - 0
mall-service/src/main/java/com/txz/mall/dao/DistributedTxRecordMapper.java

@@ -0,0 +1,9 @@
+package com.txz.mall.dao;
+
+import com.txz.mall.core.Mapper;
+import com.txz.mall.model.DistributedTxRecord;
+
+public interface DistributedTxRecordMapper extends Mapper<DistributedTxRecord> {
+}
+
+

+ 48 - 0
mall-service/src/main/java/com/txz/mall/model/DistributedTxRecord.java

@@ -0,0 +1,48 @@
+package com.txz.mall.model;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Data
+@Table(name = "m_distributed_tx_record")
+public class DistributedTxRecord {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(name = "order_id")
+    @ApiModelProperty("订单id")
+    private Long orderId;
+
+    @Column(name = "order_no")
+    @ApiModelProperty("订单号")
+    private String orderNo;
+
+    @ApiModelProperty("用户ID")
+    private Long uid;
+
+    @Column(name = "order_status")
+    @ApiModelProperty("订单状态")
+    private Integer orderStatus;
+    @Column(name = "biz_type")
+    @ApiModelProperty(value = "补偿类型:OPEN_GROUP 支付开团")
+    private String bizType;
+
+    @ApiModelProperty(value = "状态:NEED_RETRY 待补偿  FINISH 补偿成功 / FAILED 补偿失败")
+    private String status;
+    @Column(name = "retry_count")
+    @ApiModelProperty("补偿次数")
+    private Integer retryCount;
+    @Column(name = "last_retry_time")
+    @ApiModelProperty("最后一次补偿时间")
+    private LocalDateTime lastRetryTime;
+    @Column(name = "create_time")
+    @ApiModelProperty("添加时间")
+    private LocalDateTime createTime;
+}
+
+

+ 5 - 0
mall-service/src/main/java/com/txz/mall/model/OrderIndex.java

@@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.persistence.*;
+import java.math.BigDecimal;
 import java.util.Date;
 
 @Data
@@ -35,6 +36,10 @@ public class OrderIndex {
     private String sku;
     @ApiModelProperty(value = "订单状态(0:待发货;1:待收货;3:已完成; 4:已关闭  5:已取消)")
     private Integer status;
+
+    @Column(name = "pay_price")
+    @ApiModelProperty(value = "实际支付金额")
+    private BigDecimal payPrice;
     @ApiModelProperty(value = "状态 1进行中 2已完成 3未完成")
     @Column(name = "store_pink_status")
     private Integer storePinkStatus;

+ 12 - 0
mall-service/src/main/java/com/txz/mall/service/DistributedTxRecordService.java

@@ -0,0 +1,12 @@
+package com.txz.mall.service;
+
+import com.txz.mall.core.Service;
+import com.txz.mall.model.DistributedTxRecord;
+
+
+/**
+ * Created by CodeGenerator on 2025/08/05.
+ */
+public interface DistributedTxRecordService extends Service<DistributedTxRecord> {
+
+}

+ 4 - 1
mall-service/src/main/java/com/txz/mall/service/StoreOrderService.java

@@ -177,5 +177,8 @@ public interface StoreOrderService extends Service<StoreOrder> {
 
     List<StoreOrder> findByOrderIds(List<String> orderIds);
 
-
+  /**
+   * 事务补偿
+   */
+  void retryDistributedTxRecord();
 }

+ 16 - 0
mall-service/src/main/java/com/txz/mall/service/impl/DistributedTxRecordServiceImpl.java

@@ -0,0 +1,16 @@
+package com.txz.mall.service.impl;
+
+import com.txz.mall.core.AbstractService;
+import com.txz.mall.model.DistributedTxRecord;
+import com.txz.mall.service.DistributedTxRecordService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+
+/**
+ * Created by CodeGenerator on 2025/08/05.
+ */
+@Service
+@Transactional
+public class DistributedTxRecordServiceImpl extends AbstractService<DistributedTxRecord> implements DistributedTxRecordService {
+}

+ 126 - 37
mall-service/src/main/java/com/txz/mall/service/impl/StoreOrderServiceImpl.java

@@ -22,6 +22,7 @@ import com.txz.cif.param.JoinParam;
 import com.txz.cif.param.OpenParam;
 import com.txz.mall.constants.Constants;
 import com.txz.mall.core.*;
+import com.txz.mall.core.cache.RedisKey;
 import com.txz.mall.dao.StoreOrderMapper;
 import com.txz.mall.dao.StorePinkMapper;
 import com.txz.mall.dao.StorePinkSummaryMapper;
@@ -58,6 +59,7 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.net.URLEncoder;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.stream.Collectors;
@@ -88,6 +90,7 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
     private final OrderIndexService orderIndexService;
     @Resource
     private StoreOrderMapper storeOrderMapper;
+    private final DistributedTxRecordService distributedTxRecordService;
 
     @Override
     public StoreOrderCountItemVO getOrderStatusNum() {
@@ -1712,6 +1715,7 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
       orderIndex.setImage(detailVo.getImage());
       orderIndex.setSku(detailVo.getSku());
       orderIndex.setStatus(storeOrder.getStatus());
+      orderIndex.setPayPrice(storeOrder.getPayPrice());
       orderIndex.setStorePinkStatus(StorePinkStatusEnum.NOT_DRAWN.getKey());
       orderIndex.setDeliveryId(storeOrder.getDeliveryId());
       orderIndex.setDeliveryName(storeOrder.getDeliveryName());
@@ -1794,44 +1798,49 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
       storeOrderInfoService.saveOrderInfos(storeOrderInfos);
     }
 
-  @Override
-    @Transactional(propagation = Propagation.REQUIRED)
+    @Override
+    @Transactional(rollbackFor = Exception.class)
     public com.txz.mall.core.Result goPay(GoPinkDTO dto) {
         log.info("this is pay start");
         if (ObjectUtils.isEmpty(dto.getType())) {
             throw new ServiceException(I18nUtil.get("participation.type.is.empty"));
         }
-        StoreOrder storeOrder = findBy("orderId", dto.getOrderId());
-        if (ObjectUtils.isEmpty(storeOrder)) {
+        boolean restLock = RedissonLockUtil.tryLock(RedisKey.pay_lock+dto.getOrderId(),20,60);
+        try {
+          if(!restLock){
+            throw new ServiceException(I18nUtil.get("order.is.empty.pay.lock"));
+          }
+          StoreOrder storeOrder = findBy("orderId", dto.getOrderId());
+          if (ObjectUtils.isEmpty(storeOrder)) {
             throw new ServiceException(I18nUtil.get("order.is.empty.orderid") + dto.getOrderId());
-        }
-        if (storeOrder.getStatus().equals(PinkOrderStatusEnum.GROUP_ORDER_PAID.getKey())) {
+          }
+          if (storeOrder.getStatus().equals(PinkOrderStatusEnum.GROUP_ORDER_PAID.getKey())) {
             throw new ServiceException(I18nUtil.get("order.has.been.paid.orderid") + dto.getOrderId());
-        }
+          }
 
-        PinkOrderStatusEnum pinkOrderStatusEnum = OrderStateMachine.handleEvent(storeOrder, OrderEventsEnum.GROUP_PURCHASE_PAY, null);
+          PinkOrderStatusEnum pinkOrderStatusEnum = OrderStateMachine.handleEvent(storeOrder, OrderEventsEnum.GROUP_PURCHASE_PAY, null);
 
-        if (ObjectUtils.isEmpty(pinkOrderStatusEnum)) {
+          if (ObjectUtils.isEmpty(pinkOrderStatusEnum)) {
             throw new ServiceException(I18nUtil.get("current.status.does.not.support.payment.orderid") + dto.getOrderId());
-        }
+          }
 
-        log.info("accountDubboServiceClient.getWalletAccount");
-        // 检验余额够不够
-        Result result = accountDubboServiceClient.getWalletAccount(storeOrder.getUid());
-        log.info("accountDubboServiceClient.getWalletAccount result" + JSONObject.toJSONString(result));
-        if (result.getCode().equals(ResultCode.SUCCESS.getCode())) {
+          log.info("accountDubboServiceClient.getWalletAccount");
+          // 检验余额够不够
+          Result result = accountDubboServiceClient.getWalletAccount(storeOrder.getUid());
+          log.info("accountDubboServiceClient.getWalletAccount result" + JSONObject.toJSONString(result));
+          if (result.getCode().equals(ResultCode.SUCCESS.getCode())) {
             AccountDTO accountDTO = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), AccountDTO.class);
             if (accountDTO.getPayPrice().compareTo(storeOrder.getPayPrice()) < 0) {
-                //  throw new ServiceException(ResultCode.INSUFFICIENT_BALANCE);
-                log.info("ResultGenerator.genFailResult" + JSONObject.toJSONString(result));
-                throw new ServiceException(I18nUtil.get("insufficient.balance"));
+              //  throw new ServiceException(ResultCode.INSUFFICIENT_BALANCE);
+              log.info("ResultGenerator.genFailResult" + JSONObject.toJSONString(result));
+              throw new ServiceException(I18nUtil.get("insufficient.balance"));
             }
-        } else {
+          } else {
             throw new ServiceException(I18nUtil.get("failed.to.obtain.user.wallet.account"));
-        }
+          }
 
-
-        if ("open".equals(dto.getType())) {
+          // todo 事物没有一致性,先扣款或者后扣款,其中哪个服务出现问题,数据不会一致性
+          if ("open".equals(dto.getType())) {
             OpenParam openParam = new OpenParam();
             openParam.setBizId(storeOrder.getId().toString());
             openParam.setBizNo(storeOrder.getOrderId());
@@ -1839,19 +1848,27 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
             openParam.setAmount(storeOrder.getPayPrice());
             openParam.setTransTime(new Date());
             log.info("accountDubboServiceClient.openGroup openParam" + openParam);
-            //
+            // 1.操作账户余额
             Result result1 = accountDubboServiceClient.openGroup(openParam);
             log.info("accountDubboServiceClient.openGroup result1:" + result1);
             if (!result1.getCode().equals("200")) {
-                throw new ServiceException(I18nUtil.get("payment.failed"));
+              throw new ServiceException(I18nUtil.get("payment.failed"));
+            }
+            try {
+              // 2. 执行拼团逻辑
+              updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
+              log.info("pinkSuccess storeOrder1" + storeOrder);
+              storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
+              log.info("pinkSuccess storeOrder2" + storeOrder);
+            } catch (Exception e) {
+              // 3. 执行失败,标记为“待补偿”
+              saveTransactionRecord(storeOrder.getId(),storeOrder.getUid(),storeOrder.getOrderId(), "NEED_RETRY", pinkOrderStatusEnum.getKey());
+              log.error("pinkSuccess failed, will retry later", e);
+              throw new ServiceException(I18nUtil.get("payment.success"));
             }
-            updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
-            log.info("pinkSuccess storeOrder1" + storeOrder);
-            storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
-            log.info("pinkSuccess storeOrder2" + storeOrder);
             // goOpen(dto.getOrderId());
-        }
-        if ("join".equals(dto.getType())) {
+          }
+          if ("join".equals(dto.getType())) {
             log.info("join1");
             JoinParam joinParam = new JoinParam();
             joinParam.setBizId(storeOrder.getId().toString());
@@ -1860,18 +1877,46 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
             joinParam.setAmount(storeOrder.getPayPrice());
             joinParam.setTransTime(new Date());
             log.info("accountDubboServiceClient.joinGroup1:" + joinParam);
+            // 1.操作账户余额
             Result result1 = accountDubboServiceClient.joinGroup(joinParam);
             log.info("accountDubboServiceClient.joinGroup2 result1:" + result1);
             if (!result1.getCode().equals("200")) {
-                throw new ServiceException(I18nUtil.get("payment.failed"));
+              throw new ServiceException(I18nUtil.get("payment.failed"));
             }
-            updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
-            log.info("storePinkService.pinkSuccess1 :" + storeOrder);
-            storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
-            log.info("storePinkService.pinkSuccess2 :" + storeOrder);
-            // goPink(dto);
+            try {
+              // 2. 执行拼团逻辑
+              updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
+              log.info("storePinkService.pinkSuccess1 :" + storeOrder);
+              storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
+              log.info("storePinkService.pinkSuccess2 :" + storeOrder);
+            } catch (Exception e) {
+              // 3. 执行失败,标记为“待补偿”
+              saveTransactionRecord(storeOrder.getId(),storeOrder.getUid(),storeOrder.getOrderId(), "NEED_RETRY", pinkOrderStatusEnum.getKey());
+              throw new ServiceException(I18nUtil.get("payment.success"));
+            }
+          }
+          return ResultGenerator.genSuccessResult();
+        } finally {
+          RedissonLockUtil.unlock(RedisKey.pay_lock+dto.getOrderId());
         }
-        return ResultGenerator.genSuccessResult();
+    }
+
+  /**
+   * 失败补偿记录
+   * @param orderId
+   * @param status
+   */
+  private void saveTransactionRecord(Long id,Long uid,String orderId,String status,Integer orderStatus){
+      DistributedTxRecord distributedTxRecord = new DistributedTxRecord();
+      distributedTxRecord.setOrderId(id);
+      distributedTxRecord.setOrderNo(orderId);
+      distributedTxRecord.setUid(uid);
+      distributedTxRecord.setStatus(status);
+      distributedTxRecord.setOrderStatus(orderStatus);
+      distributedTxRecord.setBizType("OPEN_GROUP");
+      distributedTxRecord.setCreateTime(LocalDateTime.now());
+      distributedTxRecord.setRetryCount(0);
+      distributedTxRecordService.save(distributedTxRecord);
     }
 
     @Resource
@@ -2681,5 +2726,49 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
     return findByCondition(condition);
   }
 
+  @Override
+  public void retryDistributedTxRecord() {
+    Condition condition = new Condition(DistributedTxRecord.class);
+    Example.Criteria criteria = condition.createCriteria();
+    criteria.andEqualTo("status", "NEED_RETRY")
+      .andLessThan("retry_count",5);
+    List<DistributedTxRecord> distributedTxRecords = distributedTxRecordService.findByCondition(condition);
+    if(CollUtil.isNotEmpty(distributedTxRecords)){
+
+      for (DistributedTxRecord r : distributedTxRecords) {
+        DistributedTxRecord distributedTxRecord = new DistributedTxRecord();
+        distributedTxRecord.setId(r.getId());
+        try {
+          // simple backoff: 如果刚刚重试过则跳过
+          if (r.getLastRetryTime() != null && r.getLastRetryTime().isAfter(LocalDateTime.now().minusSeconds(30))) {
+            continue;
+          }
+          // 2. 执行拼团逻辑
+          updateOrderStatus(r.getOrderNo(),r.getOrderStatus());
+          StoreOrder storeOrder = new StoreOrder();
+          storeOrder.setId(r.getOrderId());
+          storeOrder.setOrderId(r.getOrderNo());
+          storeOrder.setUid(r.getUid());
+          storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
+          log.info("pinkSuccess storeOrder2" + storeOrder);
+          // 成功则更新 tx record
+          distributedTxRecord.setStatus("FINISH");
+          distributedTxRecord.setLastRetryTime(LocalDateTime.now());
+          distributedTxRecordService.update(distributedTxRecord);
+        } catch (Exception ex) {
+          distributedTxRecord.setRetryCount(r.getRetryCount() + 1);
+          distributedTxRecord.setLastRetryTime(LocalDateTime.now());
+          // 超过最大重试次数,标记为 FAIL (或保留 NEED_RETRY 并告警)
+          if (r.getRetryCount() >= 5) {
+            distributedTxRecord.setStatus("FAILED");
+          } else {
+            distributedTxRecord.setStatus("NEED_RETRY");
+          }
+          distributedTxRecordService.update(distributedTxRecord);
+        }
+      }
+    }
+  }
+
 
 }

+ 14 - 0
mall-service/src/main/java/com/txz/mall/task/GeneralJob.java

@@ -114,4 +114,18 @@ public class GeneralJob {
     }
 
 
+    @XxlJob("retryPendingDistributedTxRecord")
+    public ReturnT<String> retryPendingDistributedTxRecord(String param) throws Exception {
+      try {
+        log.info("retryPendingDistributedTxRecord start");
+        storeOrderService.retryDistributedTxRecord();
+        log.info("retryPendingDistributedTxRecord end");
+      } catch (Exception e) {
+        log.error("retryPendingDistributedTxRecord:e{}", e);
+        return ReturnT.FAIL;
+      }
+      return ReturnT.SUCCESS;
+    }
+
+
 }

+ 3 - 1
mall-service/src/main/resources/i18n/messages_bn_BD.properties

@@ -131,10 +131,12 @@ express.delivery.courier=\u098F\u0995\u09CD\u09B8\u09AA\u09CD\u09B0\u09C7\u09B8
 please.select.a.delivery.address=\u0985\u09A8\u09C1\u0997\u09CD\u09B0\u09B9 \u0995\u09B0\u09C7 \u098F\u0995\u099F\u09BF \u09A1\u09C7\u09B2\u09BF\u09AD\u09BE\u09B0\u09BF \u09A0\u09BF\u0995\u09BE\u09A8\u09BE \u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09A8 \u0995\u09B0\u09C1\u09A8\u0964
 delivery.address.is.incorrect=\u09A1\u09C7\u09B2\u09BF\u09AD\u09BE\u09B0\u09BF \u09A0\u09BF\u0995\u09BE\u09A8\u09BE \u09AD\u09C1\u09B2
 order.is.empty.orderid=\u0985\u09B0\u09CD\u09A1\u09BE\u09B0 \u0996\u09BE\u09B2\u09BF (\u0985\u09B0\u09CD\u09A1\u09BE\u09B0: )
+order.is.empty.pay.lock=\u09AC\u09B0\u09CD\u09A4\u09AE\u09BE\u09A8 \u0985\u09B0\u09CD\u09A1\u09BE\u09B0\u099F\u09BF \u09AA\u09CD\u09B0\u0995\u09CD\u09B0\u09BF\u09AF\u09BC\u09BE \u0995\u09B0\u09BE \u09B9\u099A\u09CD\u099B\u09C7, \u0985\u09A8\u09C1\u0997\u09CD\u09B0\u09B9 \u0995\u09B0\u09C7 \u09AA\u09B0\u09C7 \u0986\u09AC\u09BE\u09B0 \u099A\u09C7\u09B7\u09CD\u099F\u09BE \u0995\u09B0\u09C1\u09A8\u0964
 order.has.been.paid.orderid=\u0985\u09B0\u09CD\u09A1\u09BE\u09B0\u09C7\u09B0 \u0985\u09B0\u09CD\u09A5 \u09AA\u09CD\u09B0\u09A6\u09BE\u09A8 \u0995\u09B0\u09BE \u09B9\u09AF\u09BC\u09C7\u099B\u09C7 (\u0985\u09B0\u09CD\u09A1\u09BE\u09B0 \u0986\u0987\u09A1\u09BF: )
 current.status.does.not.support.payment.orderid=\u09AC\u09B0\u09CD\u09A4\u09AE\u09BE\u09A8 \u0985\u09AC\u09B8\u09CD\u09A5\u09BE \u09AA\u09C7\u09AE\u09C7\u09A8\u09CD\u099F \u09B8\u09AE\u09B0\u09CD\u09A5\u09A8 \u0995\u09B0\u09C7 \u09A8\u09BE (\u0985\u09B0\u09CD\u09A1\u09BE\u09B0 \u0986\u0987\u09A1\u09BF: )
 failed.to.obtain.user.wallet.account=\u09AC\u09CD\u09AF\u09AC\u09B9\u09BE\u09B0\u0995\u09BE\u09B0\u09C0\u09B0 \u0993\u09AF\u09BC\u09BE\u09B2\u09C7\u099F \u0985\u09CD\u09AF\u09BE\u0995\u09BE\u0989\u09A8\u09CD\u099F \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF
 payment.failed=\u0985\u09B0\u09CD\u09A5\u09AA\u09CD\u09B0\u09A6\u09BE\u09A8 \u09AC\u09CD\u09AF\u09B0\u09CD\u09A5 \u09B9\u09AF\u09BC\u09C7\u099B\u09C7\u09F7
+payment.success=\u09AA\u09C7\u09AE\u09C7\u09A8\u09CD\u099F \u09B8\u09AB\u09B2 \u09B9\u09AF\u09BC\u09C7\u099B\u09C7, \u0985\u09B0\u09CD\u09A1\u09BE\u09B0 \u09AA\u09CD\u09B0\u0995\u09CD\u09B0\u09BF\u09AF\u09BC\u09BE \u0995\u09B0\u09BE \u09B9\u099A\u09CD\u099B\u09C7\u0964
 order.does.not.exist.when.initiating.active.refund.id=\u09B8\u0995\u09CD\u09B0\u09BF\u09AF\u09BC \u09B0\u09BF\u09AB\u09BE\u09A8\u09CD\u09A1 \u09B6\u09C1\u09B0\u09C1 \u0995\u09B0\u09BE\u09B0 \u09B8\u09AE\u09AF\u09BC \u0985\u09B0\u09CD\u09A1\u09BE\u09B0\u099F\u09BF \u09AC\u09BF\u09A6\u09CD\u09AF\u09AE\u09BE\u09A8 \u09A8\u09C7\u0987 (\u0986\u0987\u09A1\u09BF: )
 current.status.does.not.support.refund.cancellation.id=\u09AC\u09B0\u09CD\u09A4\u09AE\u09BE\u09A8 \u0985\u09AC\u09B8\u09CD\u09A5\u09BE\u09DF \u09B0\u09BF\u09AB\u09BE\u09A8\u09CD\u09A1 \u09AC\u09BE\u09A4\u09BF\u09B2\u0995\u09B0\u09A3 \u09B8\u09AE\u09B0\u09CD\u09A5\u09A8 \u0995\u09B0\u09BE \u09B9\u09DF \u09A8\u09BE (\u0986\u0987\u09A1\u09BF: )
 current.member.is.the.group.leader.and.the.group.has.not.drawn.a.prize.refund.is.temporarily.not.supported=\u0997\u09CD\u09B0\u09C1\u09AA \u0995\u09C7\u09A8\u09BE\u09B0 \u09B8\u09AE\u09AF\u09BC\u09B8\u09C0\u09AE\u09BE \u09B6\u09C7\u09B7 \u09B9\u0993\u09AF\u09BC\u09BE\u09B0 \u0995\u09BE\u09B0\u09A3\u09C7 \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09BE\u09B0 \u09B8\u09AE\u09AF\u09BC \u0997\u09CD\u09B0\u09C1\u09AA \u0995\u09C7\u09A8\u09BE\u09B0 \u09A4\u09A5\u09CD\u09AF \u09AC\u09BF\u09A6\u09CD\u09AF\u09AE\u09BE\u09A8 \u09A5\u09BE\u0995\u09C7 \u09A8\u09BE (\u0986\u0987\u09A1\u09BF: )
@@ -178,4 +180,4 @@ method=\u09AA\u09A6\u09CD\u09A7\u09A4\u09BF
 exception.summary=\u09AC\u09CD\u09AF\u09A4\u09BF\u0995\u09CD\u09B0\u09AE \u09B8\u09BE\u09B0\u09BE\u0982\u09B6:
 
 
-insufficient.balance=\u0069\u006e\u0073\u0075\u0066\u0066\u0069\u0063\u0069\u0065\u006e\u0074\u0020\u0062\u0061\u006c\u0061\u006e\u0063\u0065
+insufficient.balance=insufficient\u0020balance

+ 3 - 1
mall-service/src/main/resources/i18n/messages_en_US.properties

@@ -120,10 +120,12 @@ express.delivery.courier=express delivery courier
 please.select.a.delivery.address=please select a delivery address
 delivery.address.is.incorrect=delivery address is incorrect
 order.is.empty.orderid=order is empty orderid
+order.is.empty.pay.lock=The current order is being processed, please try again later.
 order.has.been.paid.orderid=order has been paid orderid
 current.status.does.not.support.payment.orderid=current status does not support payment orderid
 failed.to.obtain.user.wallet.account=failed to obtain user's wallet account
 payment.failed=payment failed
+payment.success=Payment successful, order being processed.
 order.does.not.exist.when.initiating.active.refund.id=order does not exist when initiating active refund id
 current.status.does.not.support.refund.cancellation.id=current status does not support refund cancellation id
 current.member.is.the.group.leader.and.the.group.has.not.drawn.a.prize.refund.is.temporarily.not.supported=current member is the group leader, and the group has not drawn a prize; refund is temporarily not supported:
@@ -180,4 +182,4 @@ the.partition.key.cannot.be.empty.must.include.either.uid.or.order.id=the partit
 no.matching.table.found=no matching table found:
 table.key=, table key:
 
-insufficient.balance=insufficient balance
+insufficient.balance=insufficient balance

+ 3 - 1
mall-service/src/main/resources/i18n/messages_zh_CN.properties

@@ -124,9 +124,11 @@ please.select.a.delivery.address=\u8BF7\u9009\u62E9\u6536\u8D27\u5730\u5740
 delivery.address.is.incorrect=\u6536\u8D27\u5730\u5740\u6709\u8BEF
 order.is.empty.orderid=\u8BA2\u5355\u4E3A\u7A7AorderId:
 order.has.been.paid.orderid=\u8BA2\u5355\u5DF2\u652F\u4ED8orderId:
+order.is.empty.pay.lock=\u5F53\u524D\u8BA2\u5355\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5
 current.status.does.not.support.payment.orderid=\u5F53\u524D\u72B6\u6001\u4E0D\u652F\u6301\u53BB\u652F\u6301\u53BB\u652F\u4ED8orderId:
 failed.to.obtain.user.wallet.account=\u83B7\u53D6\u7528\u6237\u94B1\u5305\u8D26\u6237\u5931\u8D25
 payment.failed=\u652F\u4ED8\u5931\u8D25
+payment.success=\u652F\u4ED8\u6210\u529F,\u8BA2\u5355\u5904\u7406\u4E2D
 order.does.not.exist.when.initiating.active.refund.id=\u8BA2\u5355\u4E3B\u52A8\u9000\u6B3E\u65F6\u8BA2\u5355\u4E0D\u5B58\u5728id:
 current.status.does.not.support.refund.cancellation.id=\u5F53\u524D\u72B6\u6001\u4E0D\u652F\u6301\u9000\u6B3E\u5173\u95EDid\uFF1A
 current.member.is.the.group.leader.and.the.group.has.not.drawn.a.prize.refund.is.temporarily.not.supported=\u5F53\u524D\u6210\u5458\u662F\u56E2\u957F\uFF0C\u4E14\u8BE5\u56E2\u672A\u5F00\u5956\uFF0C\u6682\u65F6\u4E0D\u652F\u6301\u9000\u6B3E\uFF1A
@@ -176,4 +178,4 @@ the.partition.key.cannot.be.empty.must.include.either.uid.or.order.id=\u5206\u88
 no.matching.table.found=\u672A\u627E\u5230\u5339\u914D\u7684\u8868:
 table.key=, \u5206\u8868\u952E:
 
-insufficient.balance=\u4f59\u989d\u4e0d\u8db3
+insufficient.balance=\u4F59\u989D\u4E0D\u8DB3