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