/*
 * Copyright (c) 京北方信息技术股份有限公司 Corporation 2022 . All rights reserved.
 *
 */
package net.northking.cctp.device.api.mobile.service;

import net.northking.cctp.common.db.BasicService;
import net.northking.cctp.common.db.Pagination;
import net.northking.cctp.common.db.annotation.DatabaseReadOnly;
import net.northking.cctp.common.db.annotation.DatabaseWriteOnly;
import net.northking.cctp.common.db.excel.AbstractExcelService;
import net.northking.cctp.common.db.excel.ExcelHeader;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.common.http.QueryByPage;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.common.util.UUIDUtil;

import net.northking.cctp.device.bus.feign.CctpPlatformFeign;
import net.northking.cctp.device.bus.feign.dto.CltDictCatalog;
import net.northking.cctp.device.bus.publisher.CtrlMQPublisher;
import net.northking.cctp.device.constants.DeviceConstants;
import net.northking.cctp.device.constants.DeviceError;
import net.northking.cctp.device.db.entity.*;
import net.northking.cctp.device.db.service.*;

import net.northking.cctp.device.dto.device.*;
import net.northking.cctp.device.bus.feign.dto.CltDictItem;
import net.northking.cctp.device.dto.pcDevice.CdDeviceRemoteDto;
import net.northking.cctp.device.util.DeviceStatusUtil;
import net.northking.cctp.device.util.IpUtil;
import net.northking.cctp.device.util.OkHttpClientUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * 移动设备信息表服务实现类
 *
 * @author maven-cctp-plugin
 * @since 1.0
 */

@Service
@ConditionalOnMissingBean(name = "CdMobileDeviceApiServiceImpl")
public class CdMobileDeviceApiServiceImpl extends AbstractExcelService<CdMobileDevice> implements CdMobileDeviceApiService {
    /**
     * 日志
     */
    private final static Logger logger = LoggerFactory.getLogger(CdMobileDeviceApiServiceImpl.class);

    @Autowired
    private CdMobileDeviceService cdMobileDeviceService;

    @Autowired
    private CdDeviceStatusService cdDeviceStatusService;

    @Autowired
    private CdDeviceCtrlService cdDeviceCtrlService;

    @Autowired
    private CdDeviceTokenService cdDeviceTokenService;

    @Autowired
    private CdDeviceUsageLogService cdDeviceUsageLogService;

    @Autowired
    private CdPcDeviceService cdPcDeviceService;

    @Autowired
    private CdDeviceTenantService cdDeviceTenantService;

    @Autowired
    private CdEngineInfoService cdEngineInfoService;

    @Autowired
    private CdDeviceProjectService cdDeviceProjectService;

    @Autowired
    private CdDeviceModelService cdDeviceModelService;

    @Autowired
    private CdMobileBrandService cdMobileBrandService;

    @Autowired
    private CctpPlatformFeign cctpPlatformFeign;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Autowired
    private OkHttpClientUtil okHttpClientUtil;

    @Autowired
    private CtrlMQPublisher ctrlMQPublisher;

    @Autowired
    private IpUtil ipUtil;

    @Autowired
    private DeviceStatusUtil statusUtil;

    //= Excel 导入导出相关代码 start ==================//
    @Override
    public BasicService<CdMobileDevice> getService() {
        return this.cdMobileDeviceService;
    }

    @Override
    public ExcelHeader[] excelHeaders() {
        ExcelHeader id = new ExcelHeader("主键id", "id", String.class);
        ExcelHeader modelId = new ExcelHeader("型号id", "modelId", String.class);
        ExcelHeader platform = new ExcelHeader("平台类型", "platform", String.class);
        ExcelHeader platformVersion = new ExcelHeader("平台版本", "platformVersion", String.class);
        ExcelHeader ctrlId = new ExcelHeader("上位机id", "ctrlId", String.class);
        ExcelHeader registedTime = new ExcelHeader("注册时间", "registedTime", Date.class);
        ExcelHeader updatedBy = new ExcelHeader("更新人", "updatedBy", String.class);
        ExcelHeader updatedTime = new ExcelHeader("更新时间", "updatedTime", Date.class);
        ExcelHeader tenantId = new ExcelHeader("租户id", "tenantId", String.class);
        // TODO 定义表头及顺序
        return new ExcelHeader[]{};
    }

    /**
     * Excel导入，入库之前的处理
     *
     * @param records 从Excel读取到的记录
     * @return 待入库的记录
     */
    @Override
    protected List<CdMobileDevice> beforeInsertBatch(List<CdMobileDevice> records) {
        for (CdMobileDevice record : records) {
            record.setId(UUIDUtil.create32UUID());
        }
        return records;
    }

    /**
     * 写入Excel之前的处理<br>
     * 数据的特殊处理在此处加工处理
     *
     * @param record 来自数据库记录
     * @return 待写入Excel的记录
     */
    @Override
    protected CdMobileDevice beforeWriteExcel(CdMobileDevice record) {
        return record;
    }

    //= Excel 导入导出相关代码 end =======================//

    //= 增删改查 相关代码 start ==================//
    @Override
    @DatabaseReadOnly
    public Pagination<CdMobileDeviceDetailDto> pagingQuery(QueryByPage<CdMobileDeviceQueryDto> queryByPage) {
        //查询当前租户分页
        queryByPage.getQuery().setTenantId(NKSecurityContext.getTenantId());
        return this.cdMobileDeviceService.queryPages(queryByPage);
    }

    @Override
    @DatabaseWriteOnly
    public String add(CdMobileDeviceAddDto dto) {
        String uuid = UUIDUtil.create32UUID();
        CdMobileDevice entity = new CdMobileDevice();
        BeanUtils.copyProperties(dto, entity);
        entity.setId(uuid);
        this.cdMobileDeviceService.insert(entity);
        return uuid;
    }

    @Override
    @DatabaseWriteOnly
    public Integer updateByPK(CdMobileDeviceUpdateDto dto) throws IllegalAccessException {
        CdMobileDevice entity = new CdMobileDevice();
        BeanUtils.copyProperties(dto, entity);
        return this.cdMobileDeviceService.updateByPrimaryKey(entity);
    }

    @Override
    @DatabaseReadOnly
    public CdMobileDeviceDetailDto findByPK(String uuid) {
        CdMobileDevice example = new CdMobileDevice();
        example.setId(uuid);
        List<CdMobileDevice> entitys = this.cdMobileDeviceService.query(example);
        if (entitys.isEmpty()) {
            return null;
        }
        CdMobileDeviceDetailDto dto = new CdMobileDeviceDetailDto();
        BeanUtils.copyProperties(entitys.get(0), dto);
        return dto;
    }

    @Override
    @DatabaseWriteOnly
    public Integer deleteByPK(String uuid) {
        CdMobileDevice entity = new CdMobileDevice();
        entity.setId(uuid);
        return this.cdMobileDeviceService.deleteByExample(entity);
    }

    @Override
    public List<String> addBatch(List<CdMobileDeviceAddDto> batch) {
        List<String> result = new ArrayList<>();
        for (CdMobileDeviceAddDto dto : batch) {
            result.add(this.add(dto));
        }
        return result;
    }

    @Override
    public Integer updateBatch(List<CdMobileDeviceUpdateDto> batch) throws IllegalAccessException {
        int sum = 0;
        for (CdMobileDeviceUpdateDto dto : batch) {
            sum = sum + this.updateByPK(dto);
        }
        return sum;
    }

    @Override
    public Integer deleteBatch(List<Object> batch) {
        return this.cdMobileDeviceService.deleteByPrimaryKeys(batch);
    }

    @Override
    public Pagination<CdMobileDeviceDetailDto> pagingQueryByTenant(QueryByPage<CdMobileDeviceQueryDto> dto) {
        //SaaS分页
        Pagination<CdMobileDeviceDetailDto> pagination = this.cdMobileDeviceService.queryPages(dto);
        return pagination;
    }

    @Override
    public Pagination<CdMobileDeviceDetailDto> pagingNoneTenant(QueryByPage<CdMobileDeviceQueryDto> dto) {
        //未分配给租户
        dto.getQuery().setTenantNull(DeviceConstants.STR_TRUE);
        Pagination<CdMobileDeviceDetailDto> pagination = this.cdMobileDeviceService.queryPages(dto);
        return pagination;
    }

    @Override
    public Pagination<CdMobileDeviceDetailDto> pagingNoneProject(QueryByPage<CdMobileDeviceQueryDto> dto) {
        //未分配给系统
        dto.getQuery().setTenantId(NKSecurityContext.getTenantId());
        dto.getQuery().setProNull(DeviceConstants.STR_TRUE);
        Pagination<CdMobileDeviceDetailDto> pagination = this.cdMobileDeviceService.queryPages(dto);
        return pagination;
    }

    @Override
    public Pagination<CdMobileDeviceDetailDto> mobileByTenant(QueryByPage<CdMobileDeviceQueryDto> dto) {
        dto.getQuery().setTenantCheck(DeviceConstants.STR_TRUE);
        Pagination<CdMobileDeviceDetailDto> pagination = this.cdMobileDeviceService.queryPages(dto);
        return pagination;
    }

    @Override
    public Pagination<CdMobileDeviceDetailDto> mobileByProject(QueryByPage<CdMobileDeviceQueryDto> dto) {
        dto.getQuery().setProCheck(DeviceConstants.STR_TRUE);
        Pagination<CdMobileDeviceDetailDto> pagination = this.cdMobileDeviceService.queryPages(dto);
        return pagination;
    }


    @Override
    public Integer removeDevice(String id) {
        CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(id);
        if (status.getDeviceStatus().equals(DeviceConstants.DEVICE_STATUS_REMOVE)){
            throw new PlatformRuntimeException(DeviceError.DEVICE_IS_DELETE);
        }
        status.setDeviceStatus(DeviceConstants.DEVICE_STATUS_REMOVE);
        status.setId(id);
        statusUtil.updateMobStatus(status);
        CdMobileDevice device = new CdMobileDevice();
        device.setCtrlId("");
        device.setId(id);
        cdMobileDeviceService.updateByPrimaryKey(device);
        return 1;
    }

    @Override
    public CdDeviceCtrl findCtrlById(String id) {
        CdMobileDevice device = this.cdMobileDeviceService.findByPrimaryKey(id);
        CdDeviceCtrl ctrl = this.cdDeviceCtrlService.findByPrimaryKey(device.getCtrlId());
        return ctrl;
    }

    @Override
    @Transactional
    public DeviceInfoDto achieveMobDevice(CdDeviceRemoteDto dto, HttpServletRequest request) {
        DeviceInfoDto mobDevice = new DeviceInfoDto();
        Lock lock = redisLockRegistry.obtain(DeviceConstants.MOB_DEVICE_LOCK + dto.getDeviceId());
        try {
            if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
                logger.info("申请移动设备:{}-------------------->start",dto.getDeviceId());
                mobDevice = remoteMobDevice(dto, request);
                System.out.println("申请移动设备是"+mobDevice);
            }
        } catch (InterruptedException e) {
            logger.error("申请移动设备异常:", e);
            Thread.currentThread().interrupt();
            throw new PlatformRuntimeException(DeviceError.REMOTE_FAIL);
        } finally {
            lock.unlock();
        }
        logger.info("申请移动设备:{}-------------------->end",dto.getDeviceId());
        return mobDevice;
    }


    @Override
    public String restartDevice(List<String> ids) {
        //查询所有空闲设备的上位机id
        List<String> ctrlIds = this.cdMobileDeviceService.queryCtrlIds(ids);
        //所有移动设备的信息
        List<CdMobileDevice> cdMobileDevices = cdMobileDeviceService.queryRestartList(ids);
        //遍历组装队列数据
        int restartNum = 0;
        int errorNum = 0;
        for (String ctrlId : ctrlIds) {
            List<Map<String, String>> list = new ArrayList<>();
            for (CdMobileDevice device : cdMobileDevices) {
                if (device != null && device.getCtrlId().equals(ctrlId)) {
                    //发送队列
                    CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(device.getId());
                    if (status.getDeviceStatus().equals(DeviceConstants.DEVICE_STATUS_ONLINE) && status.getUsageStatus().equals(DeviceConstants.USAGE_STATUS_FREE)) {
                        Map<String, String> map = new HashMap<>();
                        map.put(DeviceConstants.DEVICE_PLATFORM, device.getPlatform());
                        map.put(DeviceConstants.DEVICE_ID, device.getId());
                        list.add(map);
                        restartNum++;
                    } else {
                        errorNum++;
                    }
                }
            }
            if (list.size() > 0) {
                ctrlMQPublisher.restartDeviceList(list, ctrlId);
                logger.info("重启移动设备:------>上位机:{} 设备id:{}", ctrlId, list.toString());
            }
        }
        if (errorNum > 0){
            if (restartNum == 0){
                throw new PlatformRuntimeException(DeviceError.DEVICE_NOT_ONLINE_FREE);
            }
            throw new PlatformRuntimeException(DeviceError.DEVICE_RESTART_ERROR,restartNum,errorNum);
        }
        return DeviceConstants.DEVICE_IS_RESTART;
    }

    @Override
    public String registerMobile(CdDeviceRegisterDto dto) {
        CdMobileDevice registerDevice = dto.getCdMobileDevice();
        CdDeviceModel registerModel = dto.getCdDeviceModel();
        //校验上位机最大接入数
        CdDeviceCtrl ctrl = this.cdDeviceCtrlService.findByPrimaryKey(registerDevice.getCtrlId());
        if (ObjectUtils.isEmpty(ctrl)){
            throw new PlatformRuntimeException(DeviceError.DEVICE_CTRL_IS_NOT_EXIST);
        }
        CdMobileDevice cdMobileDevice = new CdMobileDevice();
        cdMobileDevice.setCtrlId(registerDevice.getCtrlId());
        List<CdMobileDevice> queryUpLimit = this.cdMobileDeviceService.query(cdMobileDevice);
        if (queryUpLimit.size() > ctrl.getCtrlUpLimit() || queryUpLimit.size() == ctrl.getCtrlUpLimit()) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_CTRL_UP_LIMIT);
        }

        //校验品牌
        CdMobileBrand cdMobileBrand = new CdMobileBrand();
        cdMobileBrand.setBrandName(dto.getCdMobileBrand().getBrandName());
        List<CdMobileBrand> brandList = this.cdMobileBrandService.query(cdMobileBrand);

        if (brandList.size() == 0) {
            //不存在就新增
            String brandId = UUIDUtil.create32UUID();
            dto.getCdMobileBrand().setId(brandId);
            if (StringUtils.isEmpty(dto.getCdMobileBrand().getBrandNameCn())){
                dto.getCdMobileBrand().setBrandNameCn(dto.getCdMobileBrand().getBrandName());
            }
            this.cdMobileBrandService.insert(dto.getCdMobileBrand());
            registerModel.setBrandId(brandId);
        } else {
            registerModel.setBrandId(brandList.get(0).getId());
        }
        //校验机型
        CdDeviceModel cdDeviceModel = new CdDeviceModel();
        cdDeviceModel.setModelName(registerModel.getModelName());
        List<CdDeviceModel> modelList = this.cdDeviceModelService.query(cdDeviceModel);
        if (modelList.size() == 0) {
            String modelId = UUIDUtil.create32UUID();
            registerModel.setId(modelId);
            this.cdDeviceModelService.insert(registerModel);
            logger.info("新增机型id："+modelId);
            registerDevice.setModelId(modelId);
        } else {
            logger.info("查询机型id："+modelList.get(0).getId());
            registerDevice.setModelId(modelList.get(0).getId());
        }

        CdMobileDevice device = this.cdMobileDeviceService.findByPrimaryKey(dto.getDeviceInfo().get(DeviceConstants.MOB_SERIAL));
        if (device == null) {
            String deviceId = (String) dto.getDeviceInfo().get(DeviceConstants.MOB_SERIAL);
            registerDevice.setId(deviceId);
            logger.info("设置机型id："+ registerDevice.getModelId());
            registerDevice.setModelId(registerDevice.getModelId());
            //feign获取字典值
            String platform = findPlatform(registerDevice.getPlatform());
            registerDevice.setPlatform(platform);
            registerDevice.setRegistedTime(new Date());
            if (dto.getDeviceInfo().get(DeviceConstants.MOB_WDA_URL)!=null) {
                registerDevice.setWdaUrl(dto.getDeviceInfo().get(DeviceConstants.MOB_WDA_URL).toString());
            }
            this.cdMobileDeviceService.insert(registerDevice);
            CdDeviceStatus cdDeviceStatus = new CdDeviceStatus();
            cdDeviceStatus.setId(registerDevice.getId());
            cdDeviceStatus.setDeviceType(DeviceConstants.DEVICE_MOB_TYPE);
            cdDeviceStatus.setDeviceStatus(DeviceConstants.DEVICE_STATUS_ONLINE);
            cdDeviceStatus.setUsageStatus(DeviceConstants.USAGE_STATUS_FREE);
            cdDeviceStatus.setLastHeartbeatTime(new Date());
            this.cdDeviceStatusService.insert(cdDeviceStatus);
        } else {
            //更新机型信息
            device.setCtrlId(registerDevice.getCtrlId());
            String platform = findPlatform(registerDevice.getPlatform());
            device.setPlatform(platform);
            device.setPlatformVersion(registerDevice.getPlatformVersion());
            device.setRegistedTime(new Date());
            if (dto.getDeviceInfo().get(DeviceConstants.MOB_WDA_URL)!=null) {
                device.setWdaUrl(dto.getDeviceInfo().get(DeviceConstants.MOB_WDA_URL).toString());
            }
            this.cdMobileDeviceService.updateByPrimaryKey(device);
            CdDeviceStatus cdDeviceStatus = new CdDeviceStatus();
            // 校验token
            CdDeviceToken token = this.cdDeviceTokenService.findByPrimaryKey(device.getId());
            if (ObjectUtils.isEmpty(token)){
                cdDeviceStatus.setUsageStatus(DeviceConstants.USAGE_STATUS_FREE);
            }else {
                cdDeviceStatus.setUsageStatus(DeviceConstants.USAGE_STATUS_USE);
            }
            //修改设备状态
            cdDeviceStatus.setId(registerDevice.getId());
            cdDeviceStatus.setDeviceStatus(DeviceConstants.DEVICE_STATUS_ONLINE);
            statusUtil.updateMobStatus(cdDeviceStatus);
        }
        //将deviceInfo放到缓存中
        redisTemplate.opsForValue().set(DeviceConstants.MOBILE_INFO_PREFIX+dto.getDeviceInfo().get(DeviceConstants.MOB_SERIAL),dto.getDeviceInfo());
        return DeviceConstants.MOB_REGISTER_SUCCESS;
    }

    @Override
    @Transactional
    public String allocateTenant(CdMobileDeviceRecycleDto dto) {
        //校验
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        Date date = cal.getTime();
        if (dto.getExpiredTime().before(date)) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_TIME_ERROR);
        }
        if (dto.getIds() != null && dto.getIds().size() > 0) {
            for (String id : dto.getIds()) {
                Lock lock = redisLockRegistry.obtain(DeviceConstants.TENANT_ALLOCATE_DEVICE + id);
                try {
                    if (lock.tryLock()) {
                        CdDeviceTenant deviceTenant = this.cdDeviceTenantService.findByPrimaryKey(id);
                        if (deviceTenant!=null){
                            CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(id);
                            String deviceName = "";
                            if (status.getDeviceType().equals(DeviceConstants.DEVICE_MOB_TYPE)){
                                CdMobileDevice mobileDevice = this.cdMobileDeviceService.findByPrimaryKey(id);
                                CdDeviceModel deviceModel = this.cdDeviceModelService.findByPrimaryKey(mobileDevice.getModelId());
                                deviceName = deviceModel.getModelName();
                            }else if (status.getDeviceType().equals(DeviceConstants.DEVICE_PC_TYPE)){
                                CdPcDevice pcDevice = this.cdPcDeviceService.findByPrimaryKey(id);
                                deviceName = pcDevice.getDeviceName();
                            }
                            throw new PlatformRuntimeException(DeviceError.DEVICE_IS_PRIVATE_BY_TENANT, deviceName);
                        }
                        CdDeviceTenant insertDto = new CdDeviceTenant();
                        insertDto.setId(id);
                        insertDto.setRecycleType(dto.getRecycleType());
                        insertDto.setExpiredTime(dto.getExpiredTime());
                        insertDto.setTenantId(dto.getTenantId());
                        insertDto.setRecycleType(dto.getRecycleType());
                        this.cdDeviceTenantService.insert(insertDto);
                    }
                } catch (PlatformRuntimeException e){
                    throw e;
                } catch (Exception e) {
                    logger.error("分配设备到租户异常：" + e);
                    throw new PlatformRuntimeException(DeviceError.DEVICE_IS_PRIVATE);
                } finally {
                    lock.unlock();
                }
            }
            return DeviceConstants.ALLOCATE_DEVICE_SUCCESS;
        }
        return null;
    }

    @Override
    public String recycleTenant(List<String> ids) {
        List<Object> list = new ArrayList<>(ids);
        //先回收分配到系统的设备
        int deleteByPro = this.cdDeviceProjectService.deleteByPrimaryKeys(list);
        //回收分配到租户的
        int deleteByTenant = this.cdDeviceTenantService.deleteByPrimaryKeys(list);

        if (deleteByTenant == 0) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_HAS_RECYCLE);
        }
        return DeviceConstants.RECYCLE_DEVICE_SUCCESS;
    }


    @Override
    public Integer extendTenant(CdDeviceTenant dto) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        Date date = cal.getTime();
        if (dto.getExpiredTime().before(date)) {
            throw new PlatformRuntimeException(DeviceError.EXPIRE_TIME_ERROR);
        }
        //校验时间
        CdDeviceTenant query = this.cdDeviceTenantService.findByPrimaryKey(dto.getId());
        logger.info("当前时间："+ dto.getExpiredTime());
        logger.info("到期时间："+ query.getExpiredTime());
        if (dto.getExpiredTime().before(query.getExpiredTime())) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_TIME_ERROR);
        }
        int update = this.cdDeviceTenantService.updateByPrimaryKey(dto);
        return update;
    }

    @Override
    @Transactional
    public String allocateProject(CdMobileDeviceRecycleDto dto) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        Date date = cal.getTime();
        if (dto.getExpiredTime().before(date)) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_TIME_ERROR);
        }
        if (dto.getIds().size() > 0 && dto.getIds() != null) {
            for (String id : dto.getIds()) {
                Lock lock = redisLockRegistry.obtain(DeviceConstants.PRO_ALLOCATE_DEVICE + id);
                try {
                    if (lock.tryLock()) {
                        //校验分配设备的到期是否符合要求
                        CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(id);
                        CdDeviceTenant deviceTenant = this.cdDeviceTenantService.findByPrimaryKey(id);
                        if (status.getDeviceType().equals(DeviceConstants.DEVICE_MOB_TYPE)) {
                            CdMobileDevice mobileDevice = this.cdMobileDeviceService.findByPrimaryKey(id);
                            CdDeviceModel deviceModel = this.cdDeviceModelService.findByPrimaryKey(mobileDevice.getModelId());
                            //判断是否分配到别的系统
                            CdDeviceProject proCheck = this.cdDeviceProjectService.findByPrimaryKey(id);
                            if (proCheck != null) {
                                throw new PlatformRuntimeException(DeviceError.DEVICE_IS_PRIVATE_BY_PRO,deviceModel.getModelName());
                            }
                            if (dto.getExpiredTime().after(deviceTenant.getExpiredTime())) {
                                SimpleDateFormat sdf = new SimpleDateFormat(DeviceConstants.SIMPLE_DATE_FORMAT);
                                throw new PlatformRuntimeException(DeviceError.PRO_TIME_AFTER_TENANT_TIME, deviceModel.getModelName(), sdf.format(deviceTenant.getExpiredTime()));
                            }
                        } else {
                            CdPcDevice device = this.cdPcDeviceService.findByPrimaryKey(id);
                            //判断是否分配到别的系统
                            CdDeviceProject proCheck = this.cdDeviceProjectService.findByPrimaryKey(id);
                            if (proCheck != null) {
                                throw new PlatformRuntimeException(DeviceError.DEVICE_IS_PRIVATE_BY_PRO,device.getDeviceName());
                            }
                            if (dto.getExpiredTime().after(deviceTenant.getExpiredTime())) {
                                SimpleDateFormat sdf = new SimpleDateFormat(DeviceConstants.SIMPLE_DATE_FORMAT);
                                throw new PlatformRuntimeException(DeviceError.PRO_TIME_AFTER_TENANT_TIME, device.getDeviceName(), sdf.format(deviceTenant.getExpiredTime()));
                            }
                        }
                        CdDeviceProject project = new CdDeviceProject();
                        project.setId(id);
                        project.setProjectId(dto.getProjectId());
                        project.setRecycleType(dto.getRecycleType());
                        project.setExpiredTime(dto.getExpiredTime());
                        this.cdDeviceProjectService.insert(project);
                    }
                } catch (PlatformRuntimeException e){
                    throw e;
                }catch (Exception e) {
                    logger.error("分配设备异常：", e);
                    throw new PlatformRuntimeException(DeviceError.DEVICE_IS_PRIVATE);
                } finally {
                    lock.unlock();
                }
            }
            return DeviceConstants.ALLOCATE_DEVICE_SUCCESS;
        }
        return null;
    }

    @Override
    public String recycleProject(List<String> ids) {
        if (ids.size() > 0) {
            List<Object> list = new ArrayList<>(ids);
            int delete = this.cdDeviceProjectService.deleteByPrimaryKeys(list);
            if (delete == 0){
                throw new PlatformRuntimeException(DeviceError.DEVICE_HAS_RECYCLE);
            }
        }
        return DeviceConstants.RECYCLE_DEVICE_SUCCESS;
    }

    @Override
    public Integer extendProject(CdDeviceProject dto) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        Date date = cal.getTime();
        if (dto.getExpiredTime().before(date)) {
            throw new PlatformRuntimeException(DeviceError.EXPIRE_TIME_ERROR);
        }

        CdDeviceProject project = this.cdDeviceProjectService.findByPrimaryKey(dto.getId());
        if (ObjectUtils.isEmpty(project)){
            throw new PlatformRuntimeException(DeviceError.DEVICE_HAS_RECYCLE);
        }
        if (dto.getExpiredTime().before(project.getExpiredTime())) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_TIME_ERROR);
        }
        CdDeviceTenant query = this.cdDeviceTenantService.findByPrimaryKey(dto.getId());
        logger.info("当前时间："+ dto.getExpiredTime());
        logger.info("到期时间："+ query.getExpiredTime());
        if (query.getExpiredTime().before(dto.getExpiredTime())) {
            throw new PlatformRuntimeException(DeviceError.PROJECT_TIME_BEFROE_TENALATE_TIME_ERROR);
        }
        int update = this.cdDeviceProjectService.updateByPrimaryKey(dto);
        return update;
    }

    @Override
    public Pagination<ScriptResultDto> scriptDeviceByPro(QueryByPage<CdMobileDeviceScriptDto> dto) {
        Pagination<ScriptResultDto> pagination = this.cdMobileDeviceService.queryDeviceByPro(dto);
        return pagination;
    }

    @Override
    @Transactional
    public List<Map<String, String>> remoteMobileDeviceList(Map<String, Object> deviceMap, HttpServletRequest request) {
        List<String> deviceList = (List<String>) deviceMap.get(DeviceConstants.DEVICE_LIST);
        String id = deviceMap.get(DeviceConstants.DEVICE_UUID).toString();
        String userBy = deviceMap.get(DeviceConstants.USER_BY).toString();//engine
        List<Map<String, String>> list = new ArrayList<>();
        for (String deviceId : deviceList) {
            CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(deviceId);
            if (status != null && status.getDeviceType().equals(DeviceConstants.DEVICE_MOB_TYPE)
                    && status.getUsageStatus().equals(DeviceConstants.USAGE_STATUS_FREE) && status.getDeviceStatus().equals(DeviceConstants.DEVICE_STATUS_ONLINE)) {
                CdDeviceRemoteDto remoteDto = new CdDeviceRemoteDto();
                remoteDto.setDeviceId(deviceId);
                remoteDto.setId(id);
                if (userBy.equals(DeviceConstants.ENGINE_NAME)) {
                    remoteDto.setUserBy(DeviceConstants.USE_BY_ENGINE);
                } else {
                    remoteDto.setUserBy(DeviceConstants.USE_BY_USER);
                }
                //申请移动设备
                DeviceInfoDto dto = this.achieveMobDevice(remoteDto, request);
                Map<String, String> map = new HashMap<>();
                map.put(DeviceConstants.DEVICE_UUID, deviceId);
                map.put(DeviceConstants.DEVICE_TOKEN, dto.getToken());
                list.add(map);
            }else {
                throw new PlatformRuntimeException(DeviceError.ACHIEVE_DEVICE_ERROR);
            }
        }
        return list;
    }

    @Override
    @Transactional
    public List<Map<String, String>> achieveMobileDevice(Map<String, Object> deviceMap, HttpServletRequest request) {
        List<String> deviceList = (List<String>) deviceMap.get(DeviceConstants.DEVICE_LIST);
        String id = deviceMap.get(DeviceConstants.DEVICE_UUID).toString();
        String userBy = deviceMap.get(DeviceConstants.USER_BY).toString();//engine
        List<Map<String, String>> list = new ArrayList<>();
        for (String deviceId : deviceList) {
            CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(deviceId);
            if (status != null && status.getDeviceType().equals(DeviceConstants.DEVICE_MOB_TYPE) && status.getUsageStatus().equals(DeviceConstants.USAGE_STATUS_FREE) && status.getDeviceStatus().equals(DeviceConstants.DEVICE_STATUS_ONLINE)) {
                CdDeviceRemoteDto remoteDto = new CdDeviceRemoteDto();
                remoteDto.setDeviceId(deviceId);
                remoteDto.setId(id);
                if (userBy.equals(DeviceConstants.ENGINE_NAME)) {
                    remoteDto.setUserBy(DeviceConstants.USE_BY_ENGINE);
                } else {
                    remoteDto.setUserBy(DeviceConstants.USE_BY_USER);
                }
                //申请移动设备
                DeviceInfoDto dto = this.achieveMobDevice(remoteDto, request);
                Map<String, String> map = new HashMap<>();
                map.put(DeviceConstants.DEVICE_UUID, deviceId);
                map.put(DeviceConstants.DEVICE_TOKEN, dto.getToken());
                list.add(map);
            }
        }
        return list;
    }

    @Override
    public Map<String, Object> queryCtrlIp(String uuid) {
        CdMobileDevice dto = this.cdMobileDeviceService.findByPrimaryKey(uuid);
        CdDeviceCtrl ctrl = this.cdDeviceCtrlService.findByPrimaryKey(dto.getCtrlId());
        //平台类型(0-Android 1-IOS)
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put(DeviceConstants.CTRL_IP,ctrl.getCtrlIp());
        resultMap.put(DeviceConstants.CTRL_PORT,ctrl.getCtrlPort());
        resultMap.put(DeviceConstants.DEVICE_PLATFORM,dto.getPlatform());
        return resultMap;
    }

    @Override
    @Transactional
    public List<DeviceInfoDto> mobGroupControl(List<String> deviceList, HttpServletRequest request) {
        List<DeviceInfoDto> dtoList = new ArrayList<>();
        for (String uuid : deviceList) {
            CdDeviceRemoteDto remoteDto = new CdDeviceRemoteDto();
            remoteDto.setId(NKSecurityContext.getUserId());
            remoteDto.setDeviceId(uuid);
            remoteDto.setUserBy(DeviceConstants.USE_BY_USER);
            DeviceInfoDto deviceInfo = this.achieveMobDevice(remoteDto, request);
            dtoList.add(deviceInfo);
        }
        return dtoList;
    }

    @Override
    public Pagination<ScriptResultDto> scriptDeviceByPro2(QueryByPage<CdMobileDeviceScriptDto2> queryByPage) {
        Pagination<ScriptResultDto> pagination = this.cdMobileDeviceService.queryDeviceByPro2(queryByPage);
        return pagination;
    }

    public String findPlatform(String platform) {
        //feign：获取字典值将系统改成对应的字典值
        CltDictCatalog cltDictCatalog = new CltDictCatalog();
        cltDictCatalog.setTenantId(NKSecurityContext.getTenantId());
        cltDictCatalog.setCatalogCode(DeviceConstants.MOB_DEVICE_PLATFORM);
        List<CltDictItem> cltDictItems = this.cctpPlatformFeign.queryCatalogItem(cltDictCatalog);
        for (CltDictItem item : cltDictItems) {
            if (item.getItemName().equals(platform)) {
                platform = item.getItemCode();
                return platform;
            }
        }
        return platform;
    }


    private DeviceInfoDto remoteMobDevice(CdDeviceRemoteDto dto, HttpServletRequest request){
        DeviceInfoDto remoteDto = new DeviceInfoDto();
        String id = dto.getDeviceId();
        CdDeviceStatus status = this.cdDeviceStatusService.findByPrimaryKey(id);
        //当前设备是否在线
        if (!status.getDeviceStatus().equals(DeviceConstants.DEVICE_STATUS_ONLINE)) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_NOT_ONLINE);
        }
        //远程所需的参数
        CdMobileDevice device = this.cdMobileDeviceService.findByPrimaryKey(id);
        CdDeviceCtrl ctrl = this.cdDeviceCtrlService.findByPrimaryKey(device.getCtrlId());
        //设备品牌 机型
        CdDeviceModel deviceModel = this.cdDeviceModelService.findByPrimaryKey(device.getModelId());
        CdMobileBrand brand = this.cdMobileBrandService.findByPrimaryKey(deviceModel.getBrandId());
        //判断当前设备是否被占用
        CdDeviceToken checkToken = this.cdDeviceTokenService.findByPrimaryKey(id);
        if (checkToken != null) {
            throw new PlatformRuntimeException(DeviceError.DEVICE_IS_USE);
        }

        Date date = new Date();
        String index = device.getId() + DeviceConstants.STR_REGEX + ctrl.getId() + DeviceConstants.STR_REGEX + date;
        String token = DigestUtils.md5DigestAsHex(index.getBytes());

        //记录到设备令牌表
        CdDeviceToken cdDeviceToken = new CdDeviceToken();
        cdDeviceToken.setId(id);
        cdDeviceToken.setDeviceType(status.getDeviceType());
        cdDeviceToken.setToken(token);
        cdDeviceToken.setUsageType("1");
        cdDeviceToken.setStartTime(date);
        cdDeviceToken.setUserBy(dto.getUserBy());
        cdDeviceToken.setUserId(dto.getId());
        this.cdDeviceTokenService.insert(cdDeviceToken);//1

        //修改使用状态
        CdDeviceStatus cdDeviceStatus = new CdDeviceStatus();
        cdDeviceStatus.setId(id);
        cdDeviceStatus.setUsageStatus(DeviceConstants.USAGE_STATUS_USE);
        this.cdDeviceStatusService.updateByPrimaryKey(cdDeviceStatus);//1

        //新增日志
        CdDeviceModel model = this.cdDeviceModelService.findByPrimaryKey(device.getModelId());
        CdDeviceUsageLog log = new CdDeviceUsageLog();
        log.setId(UUIDUtil.create32UUID());
        log.setDeviceId(id);
        log.setStartTime(date);
        log.setDeviceType(DeviceConstants.DEVICE_MOB_TYPE);
        log.setUsageLog(DeviceConstants.MOB_USAGE_LOG + model.getModelName());
        log.setUserBy(dto.getId());
        log.setUserIp(ipUtil.getRemoteIP(request));
        this.cdDeviceUsageLogService.insert(log);//1
        remoteDto.setCdDeviceCtrl(ctrl);
        remoteDto.setCdMobileDevice(device);
        remoteDto.setToken(token);
        remoteDto.setCdMobileBrand(brand);
        remoteDto.setCdDeviceModel(deviceModel);
        LinkedHashMap<String, Object> link = (LinkedHashMap<String, Object>) redisTemplate.opsForValue().get(DeviceConstants.MOBILE_INFO_PREFIX + id);
        if (link != null) {
            link.put(DeviceConstants.DEVICE_PLATFORM, device.getPlatform()); //系统类型 (字典匹配)
            link.put(DeviceConstants.DEVICE_PLATFORM_VERSION, device.getPlatformVersion()); //系统版本
            link.put(DeviceConstants.DEVICE_SCREEN_SIZE, deviceModel.getScreenSize());  //屏幕尺寸
            link.put(DeviceConstants.DEVICE_RESOLUTION, deviceModel.getResolution());  //分辨率
            link.put(DeviceConstants.DEVICE_CPU_BRAND, deviceModel.getCpuBrand());   //cpu品牌
            link.put(DeviceConstants.DEVICE_CPU_MODEL, deviceModel.getCpuModel());   //cpu型号
            link.put(DeviceConstants.DEVICE_CPU_CORE_NUM, deviceModel.getCpuCoreNum()); //cpu核心数
            link.put(DeviceConstants.DEVICE_RAM_SIZE, deviceModel.getRamSize()); //内存
            remoteDto.setDeviceInfo(link);
        }
        String stfAddr = (String) redisTemplate.opsForValue().get(DeviceConstants.STF_PREFIX + id);
        if (stfAddr != null) {
            remoteDto.setStfAddr(stfAddr);
        }
        return remoteDto;
    }

}
