|  | @@ -0,0 +1,452 @@
 | 
	
		
			
				|  |  | +package com.xunmei.deploy.service.impl;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import com.alibaba.fastjson.JSON;
 | 
	
		
			
				|  |  | +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 | 
	
		
			
				|  |  | +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 | 
	
		
			
				|  |  | +import com.github.zafarkhaja.semver.Version;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.dao.*;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.domain.FrontTask;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.domain.HostInfo;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.domain.HostZipInfo;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.domain.SysConf;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.service.AsyncService;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.service.BeringService;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.service.FrontTaskService;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.util.RedisPrefix;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.util.RedisTemplateUtil;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.util.UTCTimeUtils;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.MachineInfo;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.OrgVo;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.TokenCache;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.TokenVo;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.heart.AppRunningInfo;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.heart.HeartBeat;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.heart.HeartResponse;
 | 
	
		
			
				|  |  | +import com.xunmei.deploy.vo.heart.HeartTimeVo;
 | 
	
		
			
				|  |  | +import org.apache.commons.lang3.StringUtils;
 | 
	
		
			
				|  |  | +import org.slf4j.Logger;
 | 
	
		
			
				|  |  | +import org.slf4j.LoggerFactory;
 | 
	
		
			
				|  |  | +import org.springframework.beans.factory.annotation.Autowired;
 | 
	
		
			
				|  |  | +import org.springframework.beans.factory.annotation.Value;
 | 
	
		
			
				|  |  | +import org.springframework.scheduling.annotation.Scheduled;
 | 
	
		
			
				|  |  | +import org.springframework.stereotype.Service;
 | 
	
		
			
				|  |  | +import org.springframework.transaction.annotation.Transactional;
 | 
	
		
			
				|  |  | +import org.springframework.util.DigestUtils;
 | 
	
		
			
				|  |  | +import javax.annotation.Resource;
 | 
	
		
			
				|  |  | +import java.rmi.ServerException;
 | 
	
		
			
				|  |  | +import java.util.Collections;
 | 
	
		
			
				|  |  | +import java.util.Iterator;
 | 
	
		
			
				|  |  | +import java.util.List;
 | 
	
		
			
				|  |  | +import java.util.Set;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +@Service
 | 
	
		
			
				|  |  | +public class BeringServiceImpl  extends ServiceImpl<HostInfoDao, HostInfo> implements BeringService {
 | 
	
		
			
				|  |  | +    private Logger logger = LoggerFactory.getLogger(getClass());
 | 
	
		
			
				|  |  | +    @Value("${apps.version}")
 | 
	
		
			
				|  |  | +    private String defaultVersion;
 | 
	
		
			
				|  |  | +    private static final long MAX_HEART_TIME = 5 * 60 * 1000;
 | 
	
		
			
				|  |  | +    @Resource
 | 
	
		
			
				|  |  | +    private RedisTemplateUtil redisTemplateUtil;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private SysConfDao sysConfDao;
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private FrontTaskDao frontTaskDao;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private HostInfoDao hostInfoDao;
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private HostZipInfoDao hostZipInfoDao;
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private UpgradeBatchInfoDao upgradeBatchInfoDao;
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private OrgVoDao orgVoDao;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private FrontTaskService frontTaskService;
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    private AsyncService asyncService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 获取令牌
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    public HostInfo generateAccessToken(TokenVo tokenVo) throws Exception {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //1.先判断请求参数是否合法
 | 
	
		
			
				|  |  | +        if(tokenVo == null){
 | 
	
		
			
				|  |  | +            logger.error("主机获取令牌失败:参数不能为空!");
 | 
	
		
			
				|  |  | +            throw new ServerException("主机获取令牌失败:参数不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if(StringUtils.isEmpty(tokenVo.getClient_id())){
 | 
	
		
			
				|  |  | +            logger.error("主机获取令牌失败:主机ID不能为空!");
 | 
	
		
			
				|  |  | +            throw new ServerException("主机获取令牌失败:主机ID不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        MachineInfo machineInfo = tokenVo.getMachineInfo();
 | 
	
		
			
				|  |  | +        if(machineInfo == null){
 | 
	
		
			
				|  |  | +            logger.error("主机获取令牌失败:计算机信息不能为空!");
 | 
	
		
			
				|  |  | +            throw new ServerException("主机获取令牌失败:计算机信息不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if(StringUtils.isEmpty(tokenVo.getGrant_type())){
 | 
	
		
			
				|  |  | +            logger.error("主机获取令牌失败:认证类型不能为空!");
 | 
	
		
			
				|  |  | +            throw new ServerException("主机获取令牌失败:认证类型不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if(!tokenVo.getGrant_type().equals("client_credentials")){
 | 
	
		
			
				|  |  | +            logger.error("主机获取令牌失败:请求参数认证类型错误!");
 | 
	
		
			
				|  |  | +            throw new ServerException("主机获取令牌失败:请求参数认证类型错误!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //验证秘钥是否合法
 | 
	
		
			
				|  |  | +        List<String> macs = machineInfo.getMacs();
 | 
	
		
			
				|  |  | +        if (null == macs || macs.size() <= 0){
 | 
	
		
			
				|  |  | +            logger.error("主机获取令牌失败:计算机macs信息不能为空!");
 | 
	
		
			
				|  |  | +            throw new ServerException("主机获取令牌失败:计算机macs信息不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Collections.sort(macs);
 | 
	
		
			
				|  |  | +        String macss = StringUtils.join(macs.toArray(), "");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        String str = macss + "zmoon";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //md5加密--->按照生成规则生成后的秘钥
 | 
	
		
			
				|  |  | +        String code = DigestUtils.md5DigestAsHex(str.getBytes());
 | 
	
		
			
				|  |  | +        if(!code.equals(tokenVo.getClient_secret())){
 | 
	
		
			
				|  |  | +            logger.error("主机获取令牌失败:主机秘钥认证失败!");
 | 
	
		
			
				|  |  | +            throw new ServerException("主机获取令牌失败:主机秘钥认证失败!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //2.验证该秘钥是否在数据库中已经存在
 | 
	
		
			
				|  |  | +        TokenCache tokenCache = null;
 | 
	
		
			
				|  |  | +        String result = redisTemplateUtil.get(RedisPrefix.CACHE_TOKENS + ":" + tokenVo.getClient_secret());
 | 
	
		
			
				|  |  | +        if (StringUtils.isNotBlank(result)){
 | 
	
		
			
				|  |  | +            tokenCache =  JSON.parseObject(result, TokenCache.class);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        HostInfo hostInfo = baseMapper.selectById(tokenVo.getClient_secret());
 | 
	
		
			
				|  |  | +        //秘钥验证成功且数据库无该秘钥,判断为数据库丢失数据,需要重新注册
 | 
	
		
			
				|  |  | +        if (hostInfo == null){
 | 
	
		
			
				|  |  | +            //判断是否允许重新注册
 | 
	
		
			
				|  |  | +            SysConf sysConf = sysConfDao.getByCode("register_again");
 | 
	
		
			
				|  |  | +            if ("1".equals(sysConf.getValue())){
 | 
	
		
			
				|  |  | +                logger.info("数据库注册数据丢失,程序允许主机重新注册!\n"+ JSON.toJSONString(machineInfo));
 | 
	
		
			
				|  |  | +                hostInfo = new HostInfo();
 | 
	
		
			
				|  |  | +                hostInfo.setId(code);
 | 
	
		
			
				|  |  | +                hostInfo.setHostName(machineInfo.getMachineName());
 | 
	
		
			
				|  |  | +                hostInfo.setHostIp(StringUtils.join(machineInfo.getIpAddresses().toArray(),","));
 | 
	
		
			
				|  |  | +                hostInfo.setHostSystem(machineInfo.getOsPlatform());
 | 
	
		
			
				|  |  | +                hostInfo.setHostFrame(machineInfo.getOsArchitecture());
 | 
	
		
			
				|  |  | +                hostInfo.setHostMac(StringUtils.join(macs.toArray(),","));
 | 
	
		
			
				|  |  | +                hostInfo.setHostOrg(null);
 | 
	
		
			
				|  |  | +                hostInfo.setHostStatus(1);
 | 
	
		
			
				|  |  | +                hostInfo.setIsPush(0);
 | 
	
		
			
				|  |  | +                baseMapper.insert(hostInfo);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                //删除frontTask中数据
 | 
	
		
			
				|  |  | +                QueryWrapper<FrontTask> wrapper = new QueryWrapper<>();
 | 
	
		
			
				|  |  | +                wrapper.eq("host_id",code);
 | 
	
		
			
				|  |  | +                frontTaskDao.delete(wrapper);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                //添加缓存信息
 | 
	
		
			
				|  |  | +                TokenCache tc = new TokenCache();
 | 
	
		
			
				|  |  | +                tc.setClientId(hostInfo.getId());
 | 
	
		
			
				|  |  | +                tc.setClientSecret(hostInfo.getId());
 | 
	
		
			
				|  |  | +                redisTemplateUtil.set(RedisPrefix.CACHE_TOKENS + ":" +hostInfo.getId(),tc,2 * 60 * 60);
 | 
	
		
			
				|  |  | +            }else {
 | 
	
		
			
				|  |  | +                logger.error("数据库注册数据丢失,程序不允许主机重新注册!\n"+ JSON.toJSONString(machineInfo));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        long time = System.currentTimeMillis();
 | 
	
		
			
				|  |  | +        //返回有效的token
 | 
	
		
			
				|  |  | +        //判断缓存中令牌是否存在
 | 
	
		
			
				|  |  | +        if(null != tokenCache && StringUtils.isNotEmpty(tokenCache.getAccessToken())){
 | 
	
		
			
				|  |  | +            //判断令牌的有效期是否过期
 | 
	
		
			
				|  |  | +            //验证该token是否在系统中存在
 | 
	
		
			
				|  |  | +            Long tokenCreateTime = tokenCache.getTokenDate();
 | 
	
		
			
				|  |  | +            int expiresIn = tokenCache.getExpiresIn();
 | 
	
		
			
				|  |  | +            //与当前时间判断 是否超过9授权时间,如果超过,则重新生成token
 | 
	
		
			
				|  |  | +            long timeDiff = (time - tokenCreateTime) / 1000;
 | 
	
		
			
				|  |  | +            //令牌时间过期
 | 
	
		
			
				|  |  | +            if(timeDiff > expiresIn){
 | 
	
		
			
				|  |  | +                //先删除之前tokentimes的缓存
 | 
	
		
			
				|  |  | +                redisTemplateUtil.del(RedisPrefix.CACHE_TOKEN_TIMES + ":" +tokenCache.getAccessToken());
 | 
	
		
			
				|  |  | +                //重新生成token 并更新数据库与缓存
 | 
	
		
			
				|  |  | +                String key = tokenCache.getClientSecret();
 | 
	
		
			
				|  |  | +                key = key + time;
 | 
	
		
			
				|  |  | +                String newMd5 = DigestUtils.md5DigestAsHex(key.getBytes());
 | 
	
		
			
				|  |  | +                tokenCache.setAccessToken(newMd5);
 | 
	
		
			
				|  |  | +                tokenCache.setExpiresIn(7200);
 | 
	
		
			
				|  |  | +                tokenCache.setTokenDate(time);
 | 
	
		
			
				|  |  | +                redisTemplateUtil.set(RedisPrefix.CACHE_TOKENS + ":" + tokenCache.getClientId(),tokenCache,2 * 60 * 60);
 | 
	
		
			
				|  |  | +                redisTemplateUtil.set(RedisPrefix.CACHE_TOKEN_TIMES + ":" + tokenCache.getAccessToken(),tokenCache,2 * 60 * 60);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                hostInfo.setTokenCreateTime(time);
 | 
	
		
			
				|  |  | +                hostInfo.setExpiresIn(7200);
 | 
	
		
			
				|  |  | +                hostInfo.setAccessToken(newMd5);
 | 
	
		
			
				|  |  | +                hostInfo.setHostName(tokenVo.getMachineInfo().getMachineName());
 | 
	
		
			
				|  |  | +                logger.info("token不为空,时效超时,请求里面的ip 为:"+tokenVo.getMachineInfo().getIpAddresses().toString());
 | 
	
		
			
				|  |  | +                hostInfo.setHostIp(tokenVo.getMachineInfo().getIpAddresses().toString());
 | 
	
		
			
				|  |  | +                baseMapper.updateById(hostInfo);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }else{
 | 
	
		
			
				|  |  | +            //生成新的令牌 更新数据库与缓存
 | 
	
		
			
				|  |  | +            String t = tokenVo.getClient_secret()+time;
 | 
	
		
			
				|  |  | +            String md5Token = DigestUtils.md5DigestAsHex(t.getBytes());
 | 
	
		
			
				|  |  | +            hostInfo.setAccessToken(md5Token);
 | 
	
		
			
				|  |  | +            hostInfo.setExpiresIn(7200);
 | 
	
		
			
				|  |  | +            hostInfo.setScope("* identity");
 | 
	
		
			
				|  |  | +            hostInfo.setTokenType("Bearer");
 | 
	
		
			
				|  |  | +            hostInfo.setTokenCreateTime(System.currentTimeMillis());
 | 
	
		
			
				|  |  | +            logger.info("token为空,请求里面的ip 为:"+tokenVo.getMachineInfo().getIpAddresses().toString());
 | 
	
		
			
				|  |  | +            hostInfo.setHostName(tokenVo.getMachineInfo().getMachineName());
 | 
	
		
			
				|  |  | +            hostInfo.setHostIp(tokenVo.getMachineInfo().getIpAddresses().toString());
 | 
	
		
			
				|  |  | +            baseMapper.updateById(hostInfo);
 | 
	
		
			
				|  |  | +            //tokenTimes 缓存新增一条
 | 
	
		
			
				|  |  | +            TokenCache tc = new TokenCache();
 | 
	
		
			
				|  |  | +            tc.setClientId(hostInfo.getId());
 | 
	
		
			
				|  |  | +            tc.setClientSecret(hostInfo.getId());
 | 
	
		
			
				|  |  | +            tc.setAccessToken(hostInfo.getAccessToken());
 | 
	
		
			
				|  |  | +            tc.setExpiresIn(hostInfo.getExpiresIn());
 | 
	
		
			
				|  |  | +            tc.setTokenDate(hostInfo.getTokenCreateTime());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            redisTemplateUtil.set(RedisPrefix.CACHE_TOKENS + ":" + tokenVo.getClient_id(),tc,2 * 60 * 60);
 | 
	
		
			
				|  |  | +            redisTemplateUtil.set(RedisPrefix.CACHE_TOKEN_TIMES + ":" + md5Token,tc,2 * 60 * 60);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return hostInfo;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Override
 | 
	
		
			
				|  |  | +    @Transactional(rollbackFor = Exception.class)
 | 
	
		
			
				|  |  | +    public HeartResponse heartBeat(String authorization, HeartBeat heartBeat) throws Exception {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        logger.info("心跳携带令牌:{}", authorization);
 | 
	
		
			
				|  |  | +        HeartResponse response = new HeartResponse();
 | 
	
		
			
				|  |  | +        //参数严重
 | 
	
		
			
				|  |  | +        String time = heartBeat.getTime();
 | 
	
		
			
				|  |  | +        if(StringUtils.isBlank(time)){
 | 
	
		
			
				|  |  | +            logger.error("白令海节点计算机的UTC时间不能为空!");
 | 
	
		
			
				|  |  | +            throw new RuntimeException("白令海节点计算机的UTC时间不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        String version = heartBeat.getVersion();
 | 
	
		
			
				|  |  | +        if(StringUtils.isBlank(version)){
 | 
	
		
			
				|  |  | +            logger.error("白令海自身版本号不能为空!");
 | 
	
		
			
				|  |  | +            throw new RuntimeException("白令海自身版本号不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Boolean allowPrerelsease = heartBeat.getAllowPrerelease();
 | 
	
		
			
				|  |  | +        if(allowPrerelsease == null){
 | 
	
		
			
				|  |  | +            logger.error("白令海允许升级到预发布版本不能为空!");
 | 
	
		
			
				|  |  | +            throw new RuntimeException("白令海允许升级到预发布版本不能为空!");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        String description = heartBeat.getDescription();
 | 
	
		
			
				|  |  | +        if(StringUtils.isBlank(description)){
 | 
	
		
			
				|  |  | +            logger.error("白令海节点描述不能为空!");
 | 
	
		
			
				|  |  | +            throw new RuntimeException("白令海节点描述不能为空!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //根据token获取主机信息
 | 
	
		
			
				|  |  | +        TokenCache tokenCache = null;
 | 
	
		
			
				|  |  | +        String result = redisTemplateUtil.get(RedisPrefix.CACHE_TOKEN_TIMES + ":" + authorization);
 | 
	
		
			
				|  |  | +        if (StringUtils.isNotBlank(result)){
 | 
	
		
			
				|  |  | +            tokenCache =  JSON.parseObject(result, TokenCache.class);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if(tokenCache == null){
 | 
	
		
			
				|  |  | +            logger.error(authorization + "令牌不合法!");
 | 
	
		
			
				|  |  | +            throw new RuntimeException(authorization + "令牌不合法!");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //获取秘钥
 | 
	
		
			
				|  |  | +        String clientId = tokenCache.getClientId();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        HostInfo hostInfo = hostInfoDao.selectById(clientId);
 | 
	
		
			
				|  |  | +        if (null == hostInfo){
 | 
	
		
			
				|  |  | +            logger.error("主机{}在数据库不存在", clientId);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        logger.info("主机{},心跳获取参数:{}", hostInfo.getId(), JSON.toJSONString(heartBeat));
 | 
	
		
			
				|  |  | +        //查询hostZipInfo信息是否存在,如果不存在,按照默认新建
 | 
	
		
			
				|  |  | +        QueryWrapper<HostZipInfo> qw = new QueryWrapper<>();
 | 
	
		
			
				|  |  | +        qw.eq("host_id",hostInfo.getId());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        HostZipInfo hostZipInfo = hostZipInfoDao.selectOne(qw);
 | 
	
		
			
				|  |  | +        if(hostZipInfo == null){
 | 
	
		
			
				|  |  | +            String orgId = upgradeBatchInfoDao.getOrgId(hostInfo.getId());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if(StringUtils.isNotEmpty(orgId) || "YCAF".equals(hostInfo.getHostName())){
 | 
	
		
			
				|  |  | +                OrgVo org = orgVoDao.getByOrgId(orgId);
 | 
	
		
			
				|  |  | +                if(org == null && !"YCAF".equals(hostInfo.getHostName())){
 | 
	
		
			
				|  |  | +                    throw new RuntimeException("心跳异常:组织机构不存在,该机器组织机构id"+orgId);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                hostZipInfo = new HostZipInfo();
 | 
	
		
			
				|  |  | +                hostZipInfo.setStatus(1);
 | 
	
		
			
				|  |  | +                hostZipInfo.setZipVersion(defaultVersion);
 | 
	
		
			
				|  |  | +                hostZipInfo.setTargetVersion(defaultVersion);
 | 
	
		
			
				|  |  | +                hostZipInfo.setHostId(hostInfo.getId());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                hostZipInfo.setHostIp(hostInfo.getHostIp());
 | 
	
		
			
				|  |  | +                if (org != null){
 | 
	
		
			
				|  |  | +                    hostZipInfo.setOrgId(org.getOrgId()+"");
 | 
	
		
			
				|  |  | +                    hostZipInfo.setOrgName(org.getOrgName());
 | 
	
		
			
				|  |  | +                }else {
 | 
	
		
			
				|  |  | +                    hostZipInfo.setOrgId(null);
 | 
	
		
			
				|  |  | +                    hostZipInfo.setOrgName(null);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                hostZipInfo.setHostName(hostInfo.getHostName());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                hostZipInfoDao.insert(hostZipInfo);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }else{
 | 
	
		
			
				|  |  | +            hostZipInfo.setHostName(hostInfo.getHostName());
 | 
	
		
			
				|  |  | +            hostZipInfo.setHostIp(hostInfo.getHostIp());
 | 
	
		
			
				|  |  | +            hostZipInfo.setHostId(hostInfo.getId());
 | 
	
		
			
				|  |  | +            String orgId = upgradeBatchInfoDao.getOrgId(hostInfo.getId());
 | 
	
		
			
				|  |  | +            if(StringUtils.isNotEmpty(orgId)){
 | 
	
		
			
				|  |  | +                OrgVo org = orgVoDao.getByOrgId(orgId);
 | 
	
		
			
				|  |  | +                if(org == null){
 | 
	
		
			
				|  |  | +                    throw new RuntimeException("心跳异常:组织机构不存在,该机器组织机构id"+orgId);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                hostZipInfo.setOrgId(orgId);
 | 
	
		
			
				|  |  | +                hostZipInfo.setOrgName(org.getOrgName());
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            hostZipInfoDao.updateById(hostZipInfo);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //获取到心跳信息,获取缓存中是否有心跳信息。
 | 
	
		
			
				|  |  | +        HeartTimeVo heartTimeVo = null;
 | 
	
		
			
				|  |  | +        String heartResult = redisTemplateUtil.get(RedisPrefix.CACHE_HEARTS + ":" + clientId);
 | 
	
		
			
				|  |  | +        if (org.apache.commons.lang3.StringUtils.isNotBlank(heartResult)){
 | 
	
		
			
				|  |  | +            heartTimeVo =  JSON.parseObject(heartResult, HeartTimeVo.class);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if(heartTimeVo == null){
 | 
	
		
			
				|  |  | +            //缓存中不存在该token信息,缓存中添加心跳信息
 | 
	
		
			
				|  |  | +            heartTimeVo = new HeartTimeVo();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            heartTimeVo.setClientId(tokenCache.getClientId());
 | 
	
		
			
				|  |  | +            heartTimeVo.setHeartTime(System.currentTimeMillis());
 | 
	
		
			
				|  |  | +            logger.info("第一次心跳向心跳缓存中添加信息:key:{},value:{}",clientId,JSON.toJSONString(heartTimeVo));
 | 
	
		
			
				|  |  | +            //缓存中添加
 | 
	
		
			
				|  |  | +            redisTemplateUtil.set(RedisPrefix.CACHE_HEARTS + ":" + clientId,heartTimeVo,2 * 60 * 60);
 | 
	
		
			
				|  |  | +        }else{
 | 
	
		
			
				|  |  | +            //存在该心跳,1.更新心态缓存
 | 
	
		
			
				|  |  | +            long now = System.currentTimeMillis();
 | 
	
		
			
				|  |  | +            heartTimeVo.setHeartTime(now);
 | 
	
		
			
				|  |  | +            if(hostInfo.getHostStatus() == 2){
 | 
	
		
			
				|  |  | +                //如果是离线状态,改为在线状态
 | 
	
		
			
				|  |  | +                hostInfo.setHostStatus(1);
 | 
	
		
			
				|  |  | +                hostInfoDao.updateById(hostInfo);
 | 
	
		
			
				|  |  | +                heartTimeVo.setHostStatus(1);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            redisTemplateUtil.set(RedisPrefix.CACHE_HEARTS + ":" + clientId,heartTimeVo,2 * 60 * 60);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //调用是否生成任务清单判断,线程池开启线程
 | 
	
		
			
				|  |  | +        frontTaskService.validateAppInfoList(clientId,heartBeat.getVersion());
 | 
	
		
			
				|  |  | +        //获取缓存中数据,告知白代理需要来弄一波任务清单推送了哦
 | 
	
		
			
				|  |  | +        List<FrontTask> hasTask = null;
 | 
	
		
			
				|  |  | +        String frontTaskResult = redisTemplateUtil.get(RedisPrefix.CACHE_FRONT_TASKS + ":" + clientId);
 | 
	
		
			
				|  |  | +        if (StringUtils.isNotBlank(frontTaskResult)){
 | 
	
		
			
				|  |  | +            hasTask = JSON.parseArray(frontTaskResult,FrontTask.class);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        response.setHasTask(false);
 | 
	
		
			
				|  |  | +        if(hasTask != null && hasTask.size() > 0 ){
 | 
	
		
			
				|  |  | +            logger.info("判断{}需要推送任务清单:{}",clientId,JSON.toJSONString(hasTask));
 | 
	
		
			
				|  |  | +            response.setHasTask(true);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        response.setName("部署中心");
 | 
	
		
			
				|  |  | +        response.setVersion("1.0.0");
 | 
	
		
			
				|  |  | +        if(hostInfo.getCoreTimeStamp() == 0){
 | 
	
		
			
				|  |  | +            response.setDeployTimeStamp(heartBeat.getDeployTimeStamp());
 | 
	
		
			
				|  |  | +        }else{
 | 
	
		
			
				|  |  | +            response.setDeployTimeStamp(UTCTimeUtils.getLong2UtcStr(hostInfo.getCoreTimeStamp()));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        response.setTime(UTCTimeUtils.getUTCTimeStr());
 | 
	
		
			
				|  |  | +        response.setAgentLastVersion(getNewlyAgentVersion(hostInfo));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        List<AppRunningInfo> appRunningInfos = heartBeat.getApps();
 | 
	
		
			
				|  |  | +        if (null != appRunningInfos && appRunningInfos.size() > 0){
 | 
	
		
			
				|  |  | +            asyncService.asyncSaveOrUpdateBatch(appRunningInfos,hostInfo.getId());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        logger.info("心跳返回结果:{}",JSON.toJSONString(response));
 | 
	
		
			
				|  |  | +        return response;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 定时任务消费心跳
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public void scheduleHearts(){
 | 
	
		
			
				|  |  | +        String lockKey = RedisPrefix.HEART_LOCK_PREFIX;
 | 
	
		
			
				|  |  | +        boolean isLock = !redisTemplateUtil.hasKey(lockKey) && redisTemplateUtil.set(lockKey, lockKey, 10);
 | 
	
		
			
				|  |  | +        if(!isLock) {
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Set<String> hallKeys = redisTemplateUtil.getKeys(RedisPrefix.CACHE_HEARTS + ":*");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Iterator<String> iterator = hallKeys.iterator();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        while (iterator.hasNext()){
 | 
	
		
			
				|  |  | +            String key = iterator.next();
 | 
	
		
			
				|  |  | +            String heartResult = redisTemplateUtil.get(key);
 | 
	
		
			
				|  |  | +            HeartTimeVo hv = null;
 | 
	
		
			
				|  |  | +            if (org.apache.commons.lang3.StringUtils.isNotBlank(heartResult)){
 | 
	
		
			
				|  |  | +                hv =  JSON.parseObject(heartResult, HeartTimeVo.class);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            if (null == hv){
 | 
	
		
			
				|  |  | +                continue;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            Long heartTime = hv.getHeartTime();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            long newTime = System.currentTimeMillis();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            long mid = newTime - heartTime;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            //log.info("主机:{},30秒一次查看心跳时间,上一次心跳时间:{},当前时间:{},时间差:{}秒",hv.getClientId(),heartTime,newTime,mid/1000);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if(mid > MAX_HEART_TIME){
 | 
	
		
			
				|  |  | +                //时间已经超过最大心跳时间,我们认为主机已经离线了
 | 
	
		
			
				|  |  | +                String clientId = hv.getClientId();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                HostInfo hostInfo = hostInfoDao.selectById(clientId);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                hostInfo.setHostStatus(2);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                hostInfoDao.updateById(hostInfo);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                hv.setHostStatus(2);
 | 
	
		
			
				|  |  | +                //离线后删除心跳缓存
 | 
	
		
			
				|  |  | +                redisTemplateUtil.del(key);
 | 
	
		
			
				|  |  | +                logger.info("主机:{},上一次心跳时间:{},当前时间:{},时间差:{}秒,修改主机为离线",hv.getClientId(),heartTime,newTime,mid/1000);
 | 
	
		
			
				|  |  | +                //log.info("主机:{},分析结果:心跳超过5分钟,时间差:{}秒,修改主机状态为离线且删除心跳缓存",mid/1000,hv.getClientId());
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 获取白令海的最新版本号
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private String getNewlyAgentVersion(HostInfo hostInfo){
 | 
	
		
			
				|  |  | +        //需要升级的版本为空  直接返回agent自身版本
 | 
	
		
			
				|  |  | +        if (StringUtils.isEmpty(hostInfo.getUploadVersion())){
 | 
	
		
			
				|  |  | +            return hostInfo.getAgentVersion();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //对比agent自身版本 和 要升级的版本
 | 
	
		
			
				|  |  | +        Version agentVersion = Version.valueOf(hostInfo.getAgentVersion());
 | 
	
		
			
				|  |  | +        Version uploadVersion = Version.valueOf(hostInfo.getUploadVersion());
 | 
	
		
			
				|  |  | +        if (agentVersion.compareTo(uploadVersion) < 0){
 | 
	
		
			
				|  |  | +            return hostInfo.getUploadVersion();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return hostInfo.getAgentVersion();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |