package com.fdkankan.scene.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fdkankan.common.constant.CameraTypeEnum;
import com.fdkankan.common.constant.CommonStatus;
import com.fdkankan.common.constant.ErrorCode;
import com.fdkankan.common.constant.TbStatus;
import com.fdkankan.common.exception.BusinessException;
import com.fdkankan.common.util.RandomUtil;
import com.fdkankan.redis.constant.RedisKey;
import com.fdkankan.redis.constant.RedisLockKey;
import com.fdkankan.redis.util.RedisLockUtil;
import com.fdkankan.redis.util.RedisUtil;
import com.fdkankan.scene.entity.Scene3dNum;
import com.fdkankan.scene.mapper.IScene3dNumMapper;
import com.fdkankan.scene.service.IScene3dNumService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
*
* 八目场景编码表 服务实现类
*
*
* @author dengsixing
* @since 2021-12-23
*/
@Slf4j
@Service
@RefreshScope
public class Scene3dNumServiceImpl extends ServiceImpl implements IScene3dNumService {
private
@Autowired
RedisUtil redisUtil;
@Autowired
RedisLockUtil redisLockUtil;
@Value("${scene.num.threshold:1000}")
private int threshold;
@Value("${scene.num.prefix:V4-}")
private String numPrefix;
@Value("${scene.num.batchSize:100}")
private int batchSize;
@Override
public String generateSceneNum(Integer cameraType){
// 从缓存中获取
String sceneNum = redisUtil.lLeftPop(RedisKey.FDKANKAN_SCENE_NUMS);
if(Objects.nonNull(sceneNum)){
return addPrefix(sceneNum,cameraType);
}
// 分布式加锁
Long loading = redisUtil.incr(RedisLockKey.LOCK_FDKANKAN_SCENE_NUMS, 1);
if (loading.compareTo(1L) == 0) {
try {
log.info("开始从数据库加载场景码");
List nums = this.findSceneNum();
this.updateUsedStatus(nums);
redisUtil.lRightPushAll(RedisKey.FDKANKAN_SCENE_NUMS, nums);
log.info("场景码加载完成");
} catch (Exception e) {
log.error("场景码加载失败", e);
} finally {
redisUtil.decr(RedisLockKey.LOCK_FDKANKAN_SCENE_NUMS, 1);
}
}else{
// 等待1秒加载缓存
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
sceneNum = redisUtil.lLeftPop(RedisKey.FDKANKAN_SCENE_NUMS);
if(StrUtil.isEmpty(sceneNum)){
log.error("场景码加载失败");
throw new BusinessException(ErrorCode.FAILURE_CODE_5053);
}
return addPrefix(sceneNum,cameraType);
}
private static String addPrefix( String num,Integer cameraType){
if(cameraType == null){
return num;
}
return CameraTypeEnum.getSceneNumPrefixByType(cameraType) + num;
}
@Override
public void batchCreateSceneNum() {
String lockKey = String.format(RedisLockKey.LOCK_BATCH_CREATE_NUM);
boolean lock = redisLockUtil.lock(lockKey, RedisKey.EXPIRE_TIME_10_MINUTE);
if(!lock){
return;
}
try {
long count = this.count(
new LambdaQueryWrapper().eq(Scene3dNum::getUsed, CommonStatus.NO.code()));
if(count > threshold){
return;
}
int batchCnt = threshold / batchSize + (threshold % batchSize > 0 ? 1 : 0);
for (int i = 0; i < batchCnt; i++){
Set numSet = this.turnCreateSceneNum(batchSize);
List scene3dNumList = numSet.stream().map(num -> {
Scene3dNum scene3dNum = new Scene3dNum();
scene3dNum.setNum(num);
return scene3dNum;
}).collect(Collectors.toList());
this.saveBatch(scene3dNumList);
}
}finally {
redisLockUtil.unlockLua(lockKey);
}
}
private Set turnCreateSceneNum(int cnt){
String num;
Set numSet = new HashSet<>();
for(int i = 0; i < cnt; i++){
num = numPrefix + RandomUtil.generateShortUuid();
numSet.add(num);
}
List existList = this.list(
new LambdaQueryWrapper()
.in(Scene3dNum::getNum, numSet));
Set existNumSet = null;
if(CollUtil.isNotEmpty(existList)){
existNumSet = existList.stream().map(Scene3dNum::getNum).collect(Collectors.toSet());
}
if(CollUtil.isNotEmpty(existNumSet)){
numSet.removeAll(existNumSet);
}
if(numSet.size() < cnt){
numSet.addAll(this.turnCreateSceneNum(cnt - numSet.size()));
}
return numSet;
}
@Override
public List findSceneNum() {
return baseMapper.findSceneNum();
}
@Override
public Scene3dNum findByNum(String sceneCode) {
List list = this.list(new LambdaQueryWrapper()
.eq(Scene3dNum::getTbStatus, TbStatus.VALID.code())
.eq(Scene3dNum::getNum, sceneCode));
if(CollUtil.isEmpty(list)){
return null;
}
return list.get(0);
}
@Override
public void updateUsedStatus(List nums) {
baseMapper.updateUsedStatus(nums);
}
}