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

Merge branch 'dev/shenliang'

Huanyi преди 19 часа
родител
ревизия
838df4eaab

+ 2 - 1
.gitignore

@@ -20,7 +20,8 @@ target/
 .springBeans
 
 ### IntelliJ IDEA ###
-.idea
+.idea/*
+!.idea/runConfigurations/
 *.iws
 *.iml
 *.ipr

+ 8 - 0
.idea/runConfigurations/分布式事务_8091.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="分布式事务_8091" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="中间件">
+  <module name="ruoyi-seata-server" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.apache.seata.server.SeataServerApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=8091" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/定时任务_8800.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="定时任务_8800" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="中间件">
+  <module name="ruoyi-snailjob-server" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.snailjob.SnailJobServerApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=8800" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/定时任务服务_6007.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="定时任务服务_6007" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="ruoyi-job" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.job.RuoYiJobApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6007" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/履约者服务_6004.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="履约者服务_6004" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="yingpaipay-fulfiller" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.fulfiller.YingpaipayFulfillerApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6004" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/服务类型服务_6006.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="服务类型服务_6006" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="yingpaipay-service" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.service.YingpaipayServiceApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6006" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/档案服务_6003.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="档案服务_6003" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="yingpaipay-archieves" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.archieves.YingpaipayArchievesApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6003" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/注册中心_8848.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="注册中心_8848" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="中间件">
+  <module name="ruoyi-nacos" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="com.alibaba.nacos.Nacos" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=8848" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/系统服务_6001.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="系统服务_6001" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="ruoyi-system" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.system.RuoYiSystemApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6001" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/网关_8080.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="网关_8080" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="中间件">
+  <module name="ruoyi-gateway" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.gateway.RuoYiGatewayApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=8080" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/订单服务_6005.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="订单服务_6005" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="yingpaipay-order" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.order.YingpaipayOrderApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6005" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/认证中心_6000.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="认证中心_6000" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="ruoyi-auth" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.auth.RuoYiAuthApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6000" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 8 - 0
.idea/runConfigurations/资源服务_6002.xml

@@ -0,0 +1,8 @@
+<configuration default="false" name="资源服务_6002" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="业务模块">
+  <module name="ruoyi-resource" />
+  <option name="SPRING_BOOT_MAIN_CLASS" value="org.dromara.resource.RuoYiResourceApplication" />
+  <option name="VM_PARAMETERS" value="-Dserver.port=6002" />
+  <method v="2">
+    <option name="Make" enabled="true" />
+  </method>
+</configuration>

+ 45 - 32
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/service/impl/SysOrderServiceImpl.java

@@ -2,7 +2,10 @@ package org.dromara.order.service.impl;
 
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.IdUtil;
+import com.baomidou.lock.LockInfo;
 import com.baomidou.lock.LockTemplate;
+import com.baomidou.lock.executor.RedissonLockExecutor;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.CacheConstants;
@@ -42,6 +45,11 @@ public class SysOrderServiceImpl implements ISysOrderService {
 
     private final LockTemplate lockTemplate;
 
+    /**
+     * 订单号生成分布式锁Key前缀
+     */
+    private static final String ORDER_CODE_LOCK_KEY = "order:code:lock:";
+
     @Transactional(rollbackFor = Exception.class)
     @Override
     public boolean create(SysOrderCreateBo bo) {
@@ -168,47 +176,52 @@ public class SysOrderServiceImpl implements ISysOrderService {
 
     /**
      * 按照一定规则生成订单号
+     * <p>
+     * 规则:yyyyMMdd + 6位当日递增序号。
+     * 常规路径直接 Redis INCR 原子递增;
+     * 仅在计数器丢失时(Redis重启/Key过期),加分布式锁后查数据库恢复。
      */
     private String generateOrderCode() {
+        String date = DateUtil.format(new Date(), "yyyyMMdd");
+        String redisKey = CacheConstants.SYS_ORDER_COUNT + date;
+        String lockKey = ORDER_CODE_LOCK_KEY + date;
 
         try {
-            // 方案一:使用分布式锁来串行化
-//        String date = DateUtil.format(new Date(), "yyyyMMddHHmmss");
-
-//        LockInfo locker = lockTemplate.lock(LockContants.ORDER, 500L, 1000L);
-//
-//        try {
-//
-//            Long count = RedisUtils.getCacheObject(CacheConstants.SYS_ORDER_COUNT);
-//            if (count == null) {
-//                count = 1L;
-//            }
-//            RedisUtils.setCacheObject(CacheConstants.SYS_ORDER_COUNT, count + 1L);
-//
-//        } finally {
-//            // 无论是否抛出异常,都解锁
-//            if (locker != null) {
-//                lockTemplate.releaseLock(locker);
-//            }
-//        }
-//        return date;
-
-            // 方案二:Redis原子递增 以每天的订单为准
-            // 先不按照品牌或门店隔离
-            String date = DateUtil.format(new Date(), "yyyyMMdd");
-            String redisKey = CacheConstants.SYS_ORDER_COUNT + date;
-            long count = RedisUtils.incrAtomicValue(redisKey);
-            if (count == 1L) {
-                // 应对服务器时钟微调或网络延迟导致的临界点问题
-                RedisUtils.expire(redisKey, 60L * 60L * 25L);
+            Long exists = RedisUtils.getCacheObject(redisKey);
+            if (exists == null) {
+                LockInfo lockInfo = lockTemplate.lock(lockKey, 5000L, 3000L, RedissonLockExecutor.class);
+                if (lockInfo == null) {
+                    log.warn("订单号恢复锁获取超时,降级使用雪花ID");
+                    return "ORD" + IdUtil.getSnowflakeNextIdStr();
+                }
+                try {
+                    exists = RedisUtils.getCacheObject(redisKey);
+                    if (exists == null) {
+                        SysOrder lastOrder = baseMapper.selectOne(
+                            Wrappers.lambdaQuery(SysOrder.class)
+                                .likeRight(SysOrder::getCode, date)
+                                .orderByDesc(SysOrder::getId)
+                                .last("LIMIT 1")
+                        );
+                        if (lastOrder != null) {
+                            String lastCode = lastOrder.getCode();
+                            exists = Long.parseLong(lastCode.substring(lastCode.length() - 6));
+                        } else {
+                            exists = 0L;
+                        }
+                        RedisUtils.setCacheObject(redisKey, exists);
+                        RedisUtils.expire(redisKey, 60L * 60L * 25L);
+                    }
+                } finally {
+                    lockTemplate.releaseLock(lockInfo);
+                }
             }
+            long count = RedisUtils.incrAtomicValue(redisKey);
             return date + String.format("%06d", count);
         } catch (Exception e) {
-            // 容灾 如果Redis挂掉,降级采用雪花ID
-            log.error("Redis 订单号生成失败,降级使用本地雪花算法", e);
+            log.error("订单号生成失败,降级使用本地雪花算法", e);
             return "ORD" + IdUtil.getSnowflakeNextIdStr();
         }
-
     }
 
 }