package com.yn.bftl.thirdparty.common.util; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.support.atomic.RedisAtomicLong; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.text.DecimalFormat; import java.util.concurrent.TimeUnit; /** * redis工具类 * * @author huabiao * @create 2021/5/29 9:26 **/ @Component @Slf4j public class RedisUtil { @Resource private StringRedisTemplate stringRedisTemplate; private static String QUOTATION_INIT = "000000"; /** * 锁redis key模板 */ private static String REDIS_LOCK_KEY_TEMPLATE = "_redis_:_lock_:%s"; /** * 单据锁默认超时时间(秒) */ private static Integer REDIS_LOCK_DEFAULT_EXPIRE_TIME = 86400; /** * 用于生成唯一的锁ID的redis key */ private static String REDIS_LOCK_UNIQUE_ID_KEY = "lock_unique_id"; /** * 百度ai access_token key */ private static final String BAIDUAI_ACCESS_TOKEN_KEY = "BAIDUAI:ACCESS_TOKEN"; /** * 获取百度ai access_token * * @return */ public String getBaiduaiAccessToken() { ValueOperations<String, String> accessTokenOpt = stringRedisTemplate.opsForValue(); return accessTokenOpt.get(BAIDUAI_ACCESS_TOKEN_KEY); } /** * 缓存百度ai access_token * * @param accessToken * @param timeout 有效时长 单位秒 * @return */ public void cacheBaiduaiAccessToken(String accessToken, Integer timeout) { stringRedisTemplate.opsForValue().set(BAIDUAI_ACCESS_TOKEN_KEY, accessToken, timeout, TimeUnit.SECONDS); } /** * 加锁 * @param key * @param value 当前时间 + 超时时间 * @return */ public boolean lock(String key, String value) { if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) { return true; } // 避免死锁,且只让一个线程拿到锁 String currentValue = stringRedisTemplate.opsForValue().get(key); // 如果锁过期了 if (StringUtils.isNotBlank(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) { //获取上一个锁的时间 String oldValues = stringRedisTemplate.opsForValue().getAndSet(key, value); if (StringUtils.isNotBlank(oldValues) && oldValues.equals(currentValue)) { return true; } } return false; } /** * 解锁 * @param key * @param value */ public void unlock(String key, String value) { try { String currentValue = stringRedisTemplate.opsForValue().get(key); if (StringUtils.isNotBlank(currentValue) && currentValue.equals(value)) { stringRedisTemplate.opsForValue().getOperations().delete(key); } } catch (Exception e) { log.error("redis分布式锁解锁异常,{}", e); } } public String incr(String key, long liveTime) { // 基于这个key创建一个redis的值,默认的value为0 RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, stringRedisTemplate.getConnectionFactory()); // 对于这个key进行自增1的操作 然后返回自增1 之前的数据 Long increment = entityIdCounter.getAndIncrement(); // 如果为0 则是第一次创建这个可以,则设置这个key的过期时间为1天 if ((null == increment || increment.longValue() == 0) & liveTime > 0) {//初始设置过期时间 entityIdCounter.expire(liveTime, TimeUnit.DAYS); //设置自增值过期时间,liveTime 过期时间;TimeUnit.DAYS 过期时间单位,我这边设置为天 } //位数不够,前面补0 DecimalFormat decimalFormat = new DecimalFormat(QUOTATION_INIT); return decimalFormat.format(increment); } /** * 加单据锁 * @param intId 锁ID * @param intExpireTime 锁过期时间(秒) * @return bool|int 加锁成功返回唯一锁ID,加锁失败返回0 */ public Integer addLock(String intId, Long intExpireTime) { //参数校验 if (intId == null || intExpireTime <= 0) { return 0; } //生成唯一锁ID,解锁需持有此ID Long intUniqueLockId = generateUniqueLockId(); //根据模板,结合单据ID,生成唯一Redis key(一般来说,单据ID在业务中系统中唯一的) String strKey = String.format(REDIS_LOCK_KEY_TEMPLATE, intId); //加锁(通过Redis setnx指令实现,从Redis 2.6.12开始,通过set指令可选参数也可以实现setnx,同时可原子化地设置超时时间) Boolean bolRes = stringRedisTemplate.opsForValue().setIfAbsent(strKey, String.valueOf(intUniqueLockId), intExpireTime, TimeUnit.SECONDS); //加锁成功返回锁ID,加锁失败返回false return Boolean.TRUE.equals(bolRes) ? intUniqueLockId.intValue() : 0; } /** * 解单据锁 * @param intId 锁ID * @param intLockId 锁唯一ID * @return bool */ public Boolean releaseLock(String intId, Integer intLockId) { //参数校验 if (intId == null || intLockId <= 0) { return false; } //生成Redis key String strKey = String.format(REDIS_LOCK_KEY_TEMPLATE, intId); //监听Redis key防止在【比对lock id】与【解锁事务执行过程中】被修改或删除,提交事务后会自动取消监控,其他情况需手动解除监控 stringRedisTemplate.watch(strKey); if (intLockId.toString().equals(stringRedisTemplate.opsForValue().get(strKey))) { stringRedisTemplate.delete(strKey); return true; } stringRedisTemplate.unwatch(); return false; } /** * 生成锁唯一ID(通过Redis incr指令实现简易版本,可结合日期、时间戳、取余、字符串填充、随机数等函数,生成指定位数唯一ID) * @return mixed */ private Long generateUniqueLockId() { return stringRedisTemplate.opsForValue().increment(REDIS_LOCK_UNIQUE_ID_KEY); } /** * 获取契约锁配置更新属性 * 设计器未配置redis,暂时搁置 * * @author chendehuihuo * @date 2022-08-26 11:22 * @return Boolean */ public Boolean getRenewalContractLock() { String isRenewalContractLock = stringRedisTemplate.opsForValue().get("table:thirdPartyPaySet:renewalContractLock"); if ("true".equals(isRenewalContractLock)) { return true; } return false; } }