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