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();
|
}
|
}
|
}
|
|
|
|
}
|