package com.fdkankan.contro.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.CommonStatus;
import com.fdkankan.common.constant.ErrorCode;
import com.fdkankan.common.exception.BusinessException;
import com.fdkankan.common.util.RandomUtil;
import com.fdkankan.contro.entity.CameraType;
import com.fdkankan.contro.entity.Scene3dNum;
import com.fdkankan.contro.enums.CameraTypeEnum;
import com.fdkankan.contro.mapper.IScene3dNumMapper;
import com.fdkankan.contro.service.ICameraTypeService;
import com.fdkankan.contro.service.IScene3dNumService;
//import com.fdkankan.dingtalk.DingTalkSendUtils;
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 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 org.springframework.util.CollectionUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
*
* 八目场景编码表 服务实现类
*
*
* @author dengsixing
* @since 2021-12-23
*/
@RefreshScope
@Slf4j
@Service
public class Scene3dNumServiceImpl extends ServiceImpl implements IScene3dNumService {
public static final String DINGTALK_MSG_PATTERN =
"**环境**: %s\n\n" +
"**标题**: %s\n\n" +
"**告警信息**: %s\n\n";
@Autowired
private RedisUtil redisUtil;
@Autowired
private RedisLockUtil redisLockUtil;
@Value("${scene.num.cachePageSize:500}")
private int cachePageSize;
@Value("${scene.num.threshold:1000}")
private int threshold;
@Value("${scene.num.prefix}")
private String numPrefix;
@Value("${scene.num.batchSize:100}")
private int batchSize;
@Value("${main.url}")
private String mainUrl;
// @Autowired
// private DingTalkSendUtils dingTalkSendUtils;
@Autowired
private ICameraTypeService cameraTypeService;
@Override
public String generateSceneNum(Integer cameraType) throws Exception {
// 从缓存中获取
String sceneNum = redisUtil.lLeftPop(RedisKey.FDKANKAN_SCENE_NUMS);
if(Objects.nonNull(sceneNum)){
return addPrefix(sceneNum,cameraType);
}
//为了防止场景量暴增导致定时任务来还不急处理,如果上面redis获取不到,需要调用一下生成场景码方法
log.warn("定时任务没有生成足够的场景码,此处实时调用批量生成场景码程序");
this.generateSceneNumHandler();
Thread.sleep(5000L);
// 从缓存中获取
sceneNum = redisUtil.lLeftPop(RedisKey.FDKANKAN_SCENE_NUMS);
if(Objects.isNull(sceneNum)){
String content = String.format(this.DINGTALK_MSG_PATTERN, mainUrl, "场景码穷尽告警", "场景计算获取场景码失败");
// dingTalkSendUtils.sendActioncardMsgToDingRobot(content, "场景码穷尽告警");
throw new Exception("场景计算获取场景码失败");
}
return addPrefix(sceneNum,cameraType);
}
@Override
public void generateSceneNumHandler() {
boolean lock = redisLockUtil.lock(RedisLockKey.LOCK_FDKANKAN_SCENE_NUMS, RedisKey.EXPIRE_TIME_30_MINUTE);
if(!lock){
return;
}
// 分布式加锁
try {
//检查mysql码池中是否有足够的未使用场景,不够时,需要创建一批
batchCreateSceneNum(false);
//检查redis中场景码是否少于指定缓存数量,少于时,需要从码池中获取
long redisCnt = redisUtil.lGetSize(RedisKey.FDKANKAN_SCENE_NUMS);
if(redisCnt >= cachePageSize){
return;
}
log.info("开始加载场景码缓存");
List nums = this.findSceneNum(cachePageSize);
if(CollUtil.isEmpty(nums) || nums.size() < cachePageSize){
String content = String.format(this.DINGTALK_MSG_PATTERN, mainUrl, "场景码穷尽告警", "场景码表中未使用状态少于" + cachePageSize);
// dingTalkSendUtils.sendActioncardMsgToDingRobot(content, "场景码穷尽告警");
}
if(CollUtil.isNotEmpty(nums)){
redisUtil.lRightPushAll(RedisKey.FDKANKAN_SCENE_NUMS, nums);
this.updateUsedStatus(nums);
}
log.info("场景码加载缓存完成");
} catch (Exception e) {
log.error("场景码加载缓存失败", e);
} finally {
redisLockUtil.unlockLua(RedisLockKey.LOCK_FDKANKAN_SCENE_NUMS);
}
}
private String addPrefix(String num,Integer camType){
if(camType == null){
return num;
}
CameraType cameraType = cameraTypeService.getByCamType(camType);
if(Objects.isNull(cameraType)){
return num;
}
return cameraType.getScenePrefix() + num;
}
@Override
public void batchCreateSceneNum(boolean force) {
if (!force) {
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++){//分批生成,每批次batchSize个
Set numSet = this.turnCreateSceneNum(batchSize);
List scene3dNumList = numSet.parallelStream().map(num -> {
Scene3dNum scene3dNum = new Scene3dNum();
scene3dNum.setCode(num);
return scene3dNum;
}).collect(Collectors.toList());
try{
this.saveBatch(scene3dNumList);
}catch (Exception e){
log.error("场景码插入异常!");
e.printStackTrace();
}
}
}
private Set turnCreateSceneNum(int cnt){
Set numSet = new HashSet<>();
for(int i = 0; i < cnt; i++){
numSet.add(numPrefix + RandomUtil.generateShortUuid());
}
return numSet;
}
@Override
public List findSceneNum(int pageSize) {
return baseMapper.findSceneNum(pageSize);
}
@Override
public void updateUsedStatus(List nums) {
baseMapper.updateUsedStatus(nums);
}
}