cr 1 өдөр өмнө
parent
commit
af03704efb

+ 175 - 72
blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/MiddleMeterApplyServiceImpl.java

@@ -696,85 +696,112 @@ public class MiddleMeterApplyServiceImpl extends BaseServiceImpl<MiddleMeterAppl
         return vo;
     }
 
-    /**
-     * 获取计量单编号
-     */
     @Override
     public String getMeterNumber(MiddleMeterApply apply) {
         if (apply.getContractPeriodId() == -1) {
             return "";
         }
 
-        String lockKey = "meter_number:" + apply.getContractId() + ":" + apply.getContractPeriodId();
+        String lockKey = "meter_number_lock:" + apply.getContractId() + ":" + apply.getContractPeriodId();
         String requestId = UUID.randomUUID().toString();
 
         try {
-            // 尝试获取分布式锁
-            boolean locked = false;
-            int lockRetry = 0;
-            int maxLockRetry = 3;
-
-            while (!locked && lockRetry < maxLockRetry) {
-                // 先检查是否已存在锁
-                if (!bladeRedis.exists(lockKey)) {
-                    // 不存在时尝试设置锁
-                    bladeRedis.setEx(lockKey, requestId,10L);
-                    // 再次检查确认自己是锁的持有者
-                    String currentOwner = bladeRedis.get(lockKey);
-                    if (requestId.equals(currentOwner)) {
-                        locked = true;
-                    }
-                }
-                if (!locked) {
-                    lockRetry++;
-                    try {
-                        Thread.sleep(100 * lockRetry);
-                    } catch (InterruptedException e) {
-                        Thread.currentThread().interrupt();
-                        break;
-                    }
-                }
-            }
-            if (!locked) {
+            // 获取分布式锁
+            if (!tryLock(lockKey, requestId, 10L)) {
                 return "";
             }
-            // 获取锁成功后的业务逻辑
+
+            // 生成并检查编号
             int maxRetries = 5;
-            int retryCount = 0;
-            while (retryCount < maxRetries) {
+            for (int retryCount = 0; retryCount < maxRetries; retryCount++) {
+                String number = generateMeterNumber(apply);
+                if (StringUtils.isBlank(number)) {
+                    continue;
+                }
+
+                // 在Redis中预占编号
+                if (reserveNumber(number)) {
+                    return number;
+                }
+
                 try {
-                    String number = generateMeterNumber(apply);
-                    if (!isNumberExists(number)) {
-                        return number;
-                    }
-                    retryCount++;
-                    try {
-                        Thread.sleep(100 * retryCount);
-                    } catch (InterruptedException e) {
-                        Thread.currentThread().interrupt();
-                        break;
-                    }
-                } catch (Exception e) {
-                    retryCount++;
+                    Thread.sleep(100 * (retryCount + 1));
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    break;
                 }
             }
             return "";
 
         } finally {
-            // 安全释放锁
-            try {
-                String currentValue = bladeRedis.get(lockKey);
-                if (requestId.equals(currentValue)) {
-                    bladeRedis.del(lockKey);
+            releaseLock(lockKey, requestId);
+        }
+    }
+
+    /**
+     * 使用setEx实现分布式锁
+     */
+    private boolean tryLock(String lockKey, String requestId, long expireSeconds) {
+        int maxRetry = 3;
+        for (int i = 0; i < maxRetry; i++) {
+            // 先检查是否已存在锁
+            if (!bladeRedis.exists(lockKey)) {
+                // 不存在时尝试设置锁
+                bladeRedis.setEx(lockKey, requestId, expireSeconds);
+
+                // 再次检查确认自己是锁的持有者
+                String currentOwner = bladeRedis.get(lockKey);
+                if (requestId.equals(currentOwner)) {
+                    return true;
+                }
+            }
+
+            if (i < maxRetry - 1) {
+                try {
+                    Thread.sleep(100 * (i + 1));
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    break;
                 }
-            } catch (Exception e) {
             }
         }
+        return false;
     }
 
     /**
-     * 生成计量单编号
+     * 安全释放锁
      */
+    private void releaseLock(String lockKey, String requestId) {
+        try {
+            String currentValue = bladeRedis.get(lockKey);
+            if (requestId.equals(currentValue)) {
+                bladeRedis.del(lockKey);
+            }
+        } catch (Exception e) {
+            log.error("释放锁失败", e);
+        }
+    }
+
+    /**
+     * 预占编号 - 使用setEx + 检查机制
+     */
+    private boolean reserveNumber(String number) {
+        String numberKey = "meter_number_reserve:" + number;
+
+        // 先检查是否已存在
+        if (bladeRedis.exists(numberKey)) {
+            return false;
+        }
+
+        // 尝试设置,如果在此期间有其他线程设置,可能会覆盖,但通过value来区分
+        String reserveValue = "reserved:" + System.currentTimeMillis() + ":" + Thread.currentThread().getId();
+        bladeRedis.setEx(numberKey, reserveValue, 300L); // 5分钟过期
+
+        // 再次检查确认我们是设置者
+        String currentValue = bladeRedis.get(numberKey);
+        return reserveValue.equals(currentValue);
+    }
+
     private String generateMeterNumber(MiddleMeterApply apply) {
         StringBuilder str = new StringBuilder();
 
@@ -793,7 +820,7 @@ public class MiddleMeterApplyServiceImpl extends BaseServiceImpl<MiddleMeterAppl
         }
         str.append(contractMeterPeriod.getPeriodNumber()).append("-");
 
-        // 获取流水号
+        // 从Redis获取流水号
         Integer nextSerial = getNextSerialNumber(apply);
         if (nextSerial == null) {
             log.error("获取流水号失败");
@@ -804,43 +831,119 @@ public class MiddleMeterApplyServiceImpl extends BaseServiceImpl<MiddleMeterAppl
     }
 
     /**
-     * 获取下一个流水号
+     * 使用Redis管理可用流水号
      */
     private Integer getNextSerialNumber(MiddleMeterApply apply) {
-        List<MiddleMeterApply> allApply = baseMapper.getAllAPPly(apply.getContractId(), apply.getContractPeriodId());
-        if (allApply.isEmpty()) {
-            return 1;
+        String serialKey = "meter_serial_available:" + apply.getContractId() + ":" + apply.getContractPeriodId();
+
+        try {
+            // 先尝试从Redis获取最小可用流水号
+            String minSerial = bladeRedis.lIndex(serialKey, 0);
+            if (minSerial != null) {
+                // 弹出最小的可用流水号
+                String popped = bladeRedis.lPop(serialKey);
+                if (popped != null) {
+                    return Integer.parseInt(popped);
+                }
+            }
+
+            // Redis中没有可用流水号,从数据库初始化
+            return initAndGetSerialFromRedis(apply);
+
+        } catch (Exception e) {
+            log.error("Redis获取流水号异常", e);
+            return getNextSerialNumberFromDB(apply);
         }
-        // 提取已使用的流水号
-        List<Integer> usedSerials = allApply.stream()
-                .filter(l -> l.getApproveStatus() != null)
-                .map(MiddleMeterApply::getApproveStatus)
-                .sorted()
-                .collect(Collectors.toList());
+    }
+
+    /**
+     * 初始化Redis中的流水号池
+     */
+    private Integer initAndGetSerialFromRedis(MiddleMeterApply apply) {
+        String lockKey = "serial_init_lock:" + apply.getContractId() + ":" + apply.getContractPeriodId();
+        String requestId = UUID.randomUUID().toString();
+
+        try {
+            // 加锁防止重复初始化
+            if (tryLock(lockKey, requestId, 10L)) {
+                // 再次检查,防止其他线程已经初始化完成
+                String serialKey = "meter_serial_available:" + apply.getContractId() + ":" + apply.getContractPeriodId();
+                if (bladeRedis.lLen(serialKey) > 0) {
+                    String popped = bladeRedis.lPop(serialKey);
+                    return popped != null ? Integer.parseInt(popped) : null;
+                }
+
+                // 从数据库获取已使用的流水号
+                List<Integer> usedSerials = getUsedSerialsFromDB(apply);
+                int nextSerial = findNextAvailableSerial(usedSerials);
 
+                // 将后续的可用流水号预先放入Redis
+                for (int i = nextSerial + 1; i <= nextSerial + 10; i++) {
+                    bladeRedis.rPush(serialKey, String.valueOf(i));
+                }
+                bladeRedis.expire(serialKey, 86400L);
+
+                return nextSerial;
+            } else {
+                // 等待其他线程初始化完成
+                Thread.sleep(100);
+                return getNextSerialNumber(apply); // 重试
+            }
+        } catch (Exception e) {
+            log.error("初始化流水号池失败", e);
+            return getNextSerialNumberFromDB(apply);
+        } finally {
+            releaseLock(lockKey, requestId);
+        }
+    }
+
+    /**
+     * 从数据库获取已使用的流水号
+     */
+    private List<Integer> getUsedSerialsFromDB(MiddleMeterApply apply) {
+        try {
+            List<MiddleMeterApply> allApply = baseMapper.getAllAPPly(apply.getContractId(), apply.getContractPeriodId());
+            return allApply.stream()
+                    .filter(l -> l.getApproveStatus() != null)
+                    .map(MiddleMeterApply::getApproveStatus)
+                    .sorted()
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("从数据库获取已使用流水号失败", e);
+            return new ArrayList<>();
+        }
+    }
+
+    /**
+     * 查找下一个可用的流水号
+     */
+    private int findNextAvailableSerial(List<Integer> usedSerials) {
         if (usedSerials.isEmpty()) {
             return 1;
         }
+
         // 查找第一个可用的流水号
         for (int i = 0; i < usedSerials.size(); i++) {
             if (usedSerials.get(i) != i + 1) {
                 return i + 1;
             }
         }
+
         // 如果所有序号都连续,返回下一个
         return usedSerials.size() + 1;
     }
 
     /**
-     * 检查编号是否已存在
+     * 从数据库获取流水号(备用方案)
      */
-    private boolean isNumberExists(String number) {
-        if (StringUtils.isBlank(number)) {
-            return true; // 空编号视为已存在,需要重新生成
+    private Integer getNextSerialNumberFromDB(MiddleMeterApply apply) {
+        try {
+            List<Integer> usedSerials = getUsedSerialsFromDB(apply);
+            return findNextAvailableSerial(usedSerials);
+        } catch (Exception e) {
+            log.error("从数据库获取流水号失败", e);
+            return null;
         }
-        LambdaQueryWrapper<MiddleMeterApply> query = new LambdaQueryWrapper<>();
-        query.eq(MiddleMeterApply::getMeterNumber, number);
-        return count(query) > 0;
     }