package com.ltkj.hosp.idutil; import cn.hutool.core.util.StrUtil; import com.ltkj.hosp.mapper.OrderNumberMapper; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * @Company: 西安路泰科技有限公司 * @Author: zhaowenxuan * @Date: 2024/10/10 14:55 */ @Configuration @Slf4j public class IdUtils { private static final String LIS_LAST_ID_KEY = "id:generate:lis:id"; private static final String TJH_LAST_ID_KEY = "id:generate:tjhs:tjh"; private static final String LIS_LAST_ID_INCR_KEY = "id:generate:lis:id:incr"; private static final String LIS_CURRENT_DATE_KEY = "id:generate:lis:currentDate"; @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private OrderNumberMapper orderNumberMapper; @Autowired private RedissonClient redissonClient; public synchronized String yuangenerateLisID(String prefix) { String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date()); String storedDate = stringRedisTemplate.opsForValue().get(LIS_CURRENT_DATE_KEY); String lastIdStr = stringRedisTemplate.opsForValue().get(LIS_LAST_ID_KEY); int lastId; if (storedDate == null || !storedDate.equals(currentDate)) { lastId = 1; stringRedisTemplate.opsForValue().set(LIS_LAST_ID_KEY, String.valueOf(lastId)); stringRedisTemplate.opsForValue().set(LIS_CURRENT_DATE_KEY, currentDate); } else { lastId = Integer.parseInt(lastIdStr) + 1; stringRedisTemplate.opsForValue().set(LIS_LAST_ID_KEY, String.valueOf(lastId)); } String yyMMdd = currentDate.substring(2); return String.format(prefix+"%s%05d", yyMMdd, lastId); } //redis分布式锁和MySQL公用 public String generateLisID(String prefix) { String lockKey = "lock:tmh:tj_tmh_lock"; RLock lock = redissonClient.getLock(lockKey); try { if (lock.tryLock(3, 5, TimeUnit.SECONDS)) { String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date()); String storedDate = stringRedisTemplate.opsForValue().get(LIS_CURRENT_DATE_KEY); String lastIdStr = stringRedisTemplate.opsForValue().get(LIS_LAST_ID_KEY); // 2. 如果 Redis 中没有或者编号丢失,查询数据库并同步到 Redis if (storedDate == null || !storedDate.equals(currentDate) || lastIdStr == null) { Integer lastIdFromDb = orderNumberMapper.getLastId(currentDate,lockKey); lastIdFromDb = (lastIdFromDb == null) ? 0 : lastIdFromDb; // 同步到 Redis stringRedisTemplate.opsForValue().set(LIS_LAST_ID_KEY, String.valueOf(lastIdFromDb), 1, TimeUnit.DAYS); stringRedisTemplate.opsForValue().set(LIS_CURRENT_DATE_KEY, currentDate, 1, TimeUnit.DAYS); } // 3. 使用 Redis 的 INCR 保证流水号唯一 long lastId = stringRedisTemplate.opsForValue().increment(LIS_LAST_ID_KEY); // 4. 同时更新数据库,确保一致性 orderNumberMapper.updateLastId(currentDate, (int) lastId,lockKey); return String.format(prefix+"%s%05d", currentDate.substring(2), lastId); } else { throw new RuntimeException("获取条码号失败,请重试"); } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } /** * 生成无限递增条码号 * @param prefix * @return */ public synchronized String generateLisNextId(String prefix){ String lastIdStr = stringRedisTemplate.opsForValue().get(LIS_LAST_ID_INCR_KEY); int current; if (StrUtil.isBlank(lastIdStr)) { current = 1; }else { current = Integer.parseInt(lastIdStr); } int numberLength = String.valueOf(99999).length(); String result = prefix + String.format("%0" + numberLength + "d", current); current++; stringRedisTemplate.opsForValue().set(LIS_LAST_ID_INCR_KEY,String.valueOf(current)); return result; } //生成体检号用 private static long lastTimestamp = -1; private static final Random random = new Random(); // public static synchronized String getTjNumber() { // long timestamp = System.currentTimeMillis(); // 获取当前时间戳(毫秒) // // // 如果时间戳和上次生成的时间戳相同,生成一个新的随机数 // if (timestamp == lastTimestamp) { // return String.format("%09d", (timestamp % 1000000000) + random.nextInt(900) + 100); // } else { // lastTimestamp = timestamp; // 更新最后生成时间戳 // return String.format("%09d", (timestamp % 1000000000) + random.nextInt(900) + 100); // } // } public synchronized String getTjNumber() { String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date()); String storedDate = stringRedisTemplate.opsForValue().get(LIS_CURRENT_DATE_KEY); String lastIdStr = stringRedisTemplate.opsForValue().get(TJH_LAST_ID_KEY); if (StrUtil.isBlank(lastIdStr))lastIdStr = "0"; int lastId; if (storedDate == null || !storedDate.equals(currentDate)) { lastId = 1; stringRedisTemplate.opsForValue().set(TJH_LAST_ID_KEY, String.valueOf(lastId)); stringRedisTemplate.opsForValue().set(LIS_CURRENT_DATE_KEY, currentDate); } else { lastId = Integer.parseInt(lastIdStr) + 1; stringRedisTemplate.opsForValue().set(TJH_LAST_ID_KEY, String.valueOf(lastId)); } String yyMMdd = currentDate.substring(2); return String.format("%s%05d", yyMMdd, lastId); } // 使用分布式ID生成器(如Snowflake) public static String generateExamNumber() { // long id = uidGenerator.getUID(); // 获取生成的唯一ID // return String.format("%09d", Math.abs(id) % 1000000000); // 格式化为9位 return null; } // redis分布式锁 public String getNewTjNumberRedisLock() { String lockKey = "lock:tjh:tj_number_lock"; String lockValue = UUID.randomUUID().toString(); Boolean locked = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS); if (Boolean.TRUE.equals(locked)) { try { String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date()); String storedDate = stringRedisTemplate.opsForValue().get(LIS_CURRENT_DATE_KEY); if (storedDate == null || !storedDate.equals(currentDate)) { // 日期变更,重置 Redis 计数器 stringRedisTemplate.opsForValue().set(TJH_LAST_ID_KEY, "1", 1, TimeUnit.DAYS); stringRedisTemplate.opsForValue().set(LIS_CURRENT_DATE_KEY, currentDate, 1, TimeUnit.DAYS); return String.format("%s%05d", currentDate.substring(2), 1); } // 使用 Redis INCR 确保唯一性 long lastId = stringRedisTemplate.opsForValue().increment(TJH_LAST_ID_KEY); return String.format("%s%05d", currentDate.substring(2), lastId); } finally { // 释放锁(确保是当前线程持有的锁才删除) String currentLockValue = stringRedisTemplate.opsForValue().get(lockKey); if (lockValue.equals(currentLockValue)) { stringRedisTemplate.delete(lockKey); } } } else { throw new RuntimeException("获取流水号失败,请重试"); } } //redis分布式锁和MySQL公用 public String getNewTjNumberRedisLockAndMysql() { String lockKey = "lock:tjh:tj_number_lock"; RLock lock = redissonClient.getLock(lockKey); try { if (lock.tryLock(5, 10, TimeUnit.SECONDS)) { String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date()); // 1. 先从 Redis 获取编号 String storedDate = stringRedisTemplate.opsForValue().get(LIS_CURRENT_DATE_KEY); String lastIdStr = stringRedisTemplate.opsForValue().get(TJH_LAST_ID_KEY); // 2. 如果 Redis 中没有或者编号丢失,查询数据库并同步到 Redis if (storedDate == null || !storedDate.equals(currentDate) || lastIdStr == null) { Integer lastIdFromDb = orderNumberMapper.getLastId(currentDate,lockKey); lastIdFromDb = (lastIdFromDb == null) ? 0 : lastIdFromDb; // 同步到 Redis stringRedisTemplate.opsForValue().set(TJH_LAST_ID_KEY, String.valueOf(lastIdFromDb), 1, TimeUnit.DAYS); stringRedisTemplate.opsForValue().set(LIS_CURRENT_DATE_KEY, currentDate, 1, TimeUnit.DAYS); } // 3. 使用 Redis 的 INCR 保证流水号唯一 long lastId = stringRedisTemplate.opsForValue().increment(TJH_LAST_ID_KEY); // 4. 同时更新数据库,确保一致性 orderNumberMapper.updateLastId(currentDate, (int) lastId,lockKey); return String.format("%s%05d", currentDate.substring(2), lastId); } else { throw new RuntimeException("获取体检号失败,请重试"); } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } }