package com.xy.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import com.xy.annotation.LogOperate;
import com.xy.collections.list.JArrayList;
import com.xy.collections.list.JList;
import com.xy.collections.map.JHashMap;
import com.xy.collections.map.JMap;
import com.xy.config.DeviceThreadPoolConfig;
import com.xy.consts.DictConsts;
import com.xy.device.*;
import com.xy.dto.*;
import com.xy.dto.api.biz.ContainerAddDTO;
import com.xy.dto.be.MercDto;
import com.xy.dto.common.MercPlaceDto;
import com.xy.dto.device.DeviceQueryDTO;
import com.xy.dto.device.DeviceRegDTO;
import com.xy.dto.mini.MiniDeviceInfoDto;
import com.xy.dto.nfc.ActiveDeviceDTO;
import com.xy.entity.*;
import com.xy.enums.FileExportType;
import com.xy.enums.LogEnum;
import com.xy.error.CommRuntimeException;
import com.xy.mapper.DeviceInfoMapper;
import com.xy.mapper.entity.DeviceInfoQueryPage;
import com.xy.service.be.MercFeignService;
import com.xy.service.be.MercService;
import com.xy.service.common.MercPlaceService;
import com.xy.service.common.MercRegionService;
import com.xy.sys.EnumDataClearSize;
import com.xy.util.ExcelUtils;
import com.xy.utils.*;
import com.xy.utils.Enum.AlgorithmTypeEnum;
import com.xy.utils.enums.*;
import com.xy.vo.DeviceQueryVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jodd.introspector.MapperFunction;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.xy.utils.PlusBeans.*;

/**
 * <p>
 * 设备-信息 服务实现类
 * </p>
 *
 * @author lijin
 * @since 2022-12-23
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Api(tags = "设备-信息")
public class DeviceInfoServiceImpl extends ServiceImpl<DeviceInfoMapper, DeviceInfo> implements DeviceInfoService {

    /**
     * 质检商户code
     */
    public static final String QA_MERC_CODE = "10001";
    /**
     * 聚邻社质检
     */
    public static final String JLS_QA_MERC_CODE = "10112";
    /**
     * 聚邻社顶级商户
     */
    public static final String JLS_MERC_CODE = "10168";
     private final CloudWalkApiService cloudWalkApiService;
    private final AlgorithmService algorithmService;
    private final CountApiService countApiService;
    private final TyApiService tyApiService;
    private final MercFeignService mercFeignService;
    private final DeviceSysinfoServiceImpl deviceSysinfoService;
    private final DeviceStatusServiceImpl deviceStatusService;
    private final DeviceRegisterServiceImpl deviceRegisterService;
    private final DeviceEventMsgServiceImpl deviceEventMsgService;
    private final DeviceDataServiceImpl deviceDataService;
    private final DeviceTempRecordsServiceImpl deviceTempRecordsService;
    private final DeviceNetRecordServiceImpl deviceNetRecordService;
    private final GoodsDeviceService goodsDeviceService;
    private final FileExportService fileExportService;
    private final RedisService<String> redisService;
    private final GoodsService goodsService;
    private final AlipayDeviceService alipayDeviceService;
    private final String keyPrefix = "device:history:";

    private final MercRegionService mercRegionService;

    private final DeviceAlgorithmChargingServiceImpl deviceAlgorithmChargingService;

    private final MercPlaceService mercPlaceService;

    private final MercUserBindDeviceService mercUserBindDeviceService;

    private final DevicePartServiceImpl devicePartService;
    private final MercService mercService;

    private final MercDeviceQrConfigService mercDeviceQrConfigService;

    private final DeviceCreateIdsServiceImpl deviceCreateIdsService;

    private final DeviceAnnualFeeServiceImpl deviceAnnualFeeService;

    private final MercDeviceCostConfigService mercDeviceCostConfigService;


    @Override
    @ApiOperation("设备列表-管理员")
    public R<List<DeviceInfoDto.ListByAdminName>> listByAdminName(@RequestBody @Validated DeviceInfoDto.GroupByAdminNameDto dto) {
        String noAdmin = "未分配管理员";
        //是否质检商户
        boolean isQa = BooleanUtil.isTrue(dto.getIsQaMode());

        List<DeviceInfoDto.ListByAdminName> deviceInfoList = baseMapper.listByAdminName(dto);
        deviceInfoList.forEach(i -> {
            SysDictRedis dictDeviceType = SysDictUtils.get(DictConsts.DEVICE_TYPE, String.valueOf(i.getDeviceType()));
            i.setDeviceTypeName(dictDeviceType.getMsg());
            if (!Emptys.check(i.getDeviceName())) {
                i.setDeviceName("");
            }
        });

        // 没有管理员的的设置默认值
        deviceInfoList.stream().filter(s -> s.getAdminName() == null).forEach(s -> s.setAdminName(noAdmin));
        return R.ok(deviceInfoList);
    }

    //@Override
    @ApiOperation("设备分页-管理员")
    @Override
    public R<PageBean<DeviceInfoDto.ListByAdminName>> pageByAdminName(@RequestBody @Validated DeviceInfoDto.PageByAdminNameDto dto) {
        PageBean page = dto.getPage();
        String noAdmin = "未分配管理员";
        IPage<DeviceInfoDto.ListByAdminName> deviceInfoList = baseMapper.pageByAdminName(toIPage(page), dto);
        deviceInfoList.getRecords().forEach(i -> {
            SysDictRedis dictDeviceType = SysDictUtils.get(DictConsts.DEVICE_TYPE, String.valueOf(i.getDeviceType()));
            i.setDeviceTypeName(dictDeviceType.getMsg());
            if (!Emptys.check(i.getDeviceName())) {
                i.setDeviceName("");
            }
        });

        // 没有管理员的的设置默认值
        deviceInfoList.getRecords().stream().filter(s -> s.getAdminName() == null).forEach(s -> s.setAdminName(noAdmin));
        return R.ok(toPageBean(DeviceInfoDto.ListByAdminName.class, deviceInfoList));
    }

    @Override
    @ApiOperation("设备分组-管理员")
    public R<List<DeviceInfoDto.GroupByAdminNameVo>> groupByAdminName(@RequestBody @Validated DeviceInfoDto.GroupByAdminNameDto dto) {


        String noAdmin = "未分配管理员";
        List<DeviceInfoDto.ListByAdminName> deviceInfoList = listByAdminName(dto).getData();
        // 根据管理员名字分组
        Map<String, List<DeviceInfoDto.ListByAdminName>> deviceMap = deviceInfoList.stream().collect(Collectors.groupingBy(DeviceInfoDto.ListByAdminName::getAdminName));
        List<DeviceInfoDto.GroupByAdminNameVo> list = new ArrayList<>();
        // 不包含未分配管理员的
        deviceMap.forEach(
                (k, i) -> {
                    DeviceInfoDto.GroupByAdminNameVo vo = new DeviceInfoDto.GroupByAdminNameVo();
                    if (!noAdmin.equals(k)) {
                        vo.setAdminName(k)
                                .setDeviceNum(i.size())
                                .setDeviceInfos(i);
                        list.add(vo);
                    }
                }
        );
        // 包含分配管理员的
        List<DeviceInfoDto.ListByAdminName> noAdminNamesList = deviceMap.get(noAdmin);
        if (Emptys.check(noAdminNamesList)) {
            DeviceInfoDto.GroupByAdminNameVo vo = new DeviceInfoDto.GroupByAdminNameVo();
            vo.setAdminName(noAdmin)
                    .setDeviceNum(noAdminNamesList.size())
                    .setDeviceInfos(noAdminNamesList);
            list.add(vo);
        }
        return R.ok(list);
    }


    @PostMapping("eventList")
    @ApiOperation("根据事件编码查询设备")
    public R<PageBean<DeviceInfoDto.EventListVo>> eventList(@RequestBody @Validated DeviceInfoDto.EventList eventList) {
        String countAs = "id";
        QueryWrapper<DeviceEventMsg> queryWrapper = new QueryWrapper<DeviceEventMsg>()
                .select(LambdaUtils.getUnderlineCaseName(DeviceEventMsg::getDeviceId) + " as " + LambdaUtils.getProperty(DeviceEventMsg::getDeviceId), "count(*) as " + countAs)
                .eq(LambdaUtils.getUnderlineCaseName(DeviceEventMsg::getCode), eventList.getCode())
                .between(LambdaUtils.getUnderlineCaseName(DeviceEventMsg::getCreateTime), eventList.getBeginTime(), eventList.getEndTime())
                .groupBy(LambdaUtils.getUnderlineCaseName(DeviceEventMsg::getDeviceId))
                .last("HAVING " + countAs + " >= " + eventList.getSize());
        IPage<DeviceEventMsg> iPage = deviceEventMsgService.page(toIPage(eventList.getPage()), queryWrapper);
        PageBean<DeviceInfoDto.EventListVo> pageBean = new PageBean<DeviceInfoDto.EventListVo>()
                .setSize(iPage.getSize())
                .setCurrent(iPage.getCurrent())
                .setTotal(iPage.getTotal());
        List<DeviceEventMsg> list = iPage.getRecords();
        if (!Emptys.check(list)) {
            return R.ok(pageBean);
        }
        List<DeviceInfo> deviceInfos = listByIds(new JArrayList<>(list).getProperty(DeviceEventMsg::getDeviceId));
        List<DeviceInfoDto.EventListVo> eventListVos = copy(DeviceInfoDto.EventListVo.class, deviceInfos);
        List<DeviceInfoDto.EventListVo> builder = copy(eventListVos)
                .target(list, DeviceInfoDto.EventListVo::getDeviceId, DeviceInfoDto.EventListVo::getSize, DeviceEventMsg::getDeviceId, DeviceEventMsg::getId)
                .builder();
        return R.ok(pageBean.setRecords(builder));
    }

    @ApiOperation("拓元算法设备列表")
    @PostMapping("tyDeviceList")
    public R<PageBean<DeviceInfoDto.Vo>> tyDeviceList(@RequestBody @Validated DeviceInfoDto.TyDeviceDTO dto) {
        PageBean pageBean = dto.getPage();
        String searchKey = dto.getSearchKey();
        Integer thirdStatus = dto.getThirdStatus();
        List<String> statusList = new ArrayList<>();
        // 1待审核，2已审核
        if (thirdStatus != null && thirdStatus.intValue() == 1) {
            statusList = CollUtil.newArrayList("1000", "2000", "3000", "5000");
        }
        // 2已审核  4000： 设备登记成功
        if (thirdStatus != null && thirdStatus.intValue() == 2) {
            statusList.add("4000");
        }
        LambdaQueryWrapper<DeviceInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DeviceInfo::getAlgorithmId, AlgorithmTypeEnum.TY.getId());
        lqw.in(CollUtil.isNotEmpty(statusList), DeviceInfo::getThirdStatus, statusList);
        lqw.and(StrUtil.isNotEmpty(searchKey), wrapper -> wrapper
                .eq(DeviceInfo::getDeviceId, searchKey)
                .or()
                .likeRight(DeviceInfo::getDeviceName, searchKey));
        IPage<DeviceInfo> iPage = page(toIPage(pageBean), lqw);
        List<DeviceInfo> records = iPage.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            ThreadPoolUtils.Execute execute = ThreadPoolUtils.excPoll(DeviceThreadPoolConfig.TY_DEVICE_STATUS, records.size());
            records.forEach(deviceInfo -> execute.execute(() -> {
                Long deviceId = deviceInfo.getDeviceId();
                /**
                 * 请求状态码。
                 * 1000： 未知的设备CPUID
                 * 2000： 设备登记 审批中
                 * 3000： 设备登记已拒绝
                 * 4000： 设备登记成功
                 * 5000： 设备已禁⽤
                 */
                String tyStatus = deviceInfo.getThirdStatus();
                if (!"4000".equals(tyStatus)) {
                    // 未登记成功的需要实时查询
                    // 查询登记设备
                    DeviceQueryVO deviceQueryVO = tyApiService.deviceQuery(new DeviceQueryDTO().setCpuId(String.valueOf(deviceId)));
                    Integer status = deviceQueryVO.getStatus();
                    String message = deviceQueryVO.getMessage();
                    deviceInfo.setThirdStatus(String.valueOf(status));
                    deviceInfo.setThirdResult(message);
                    updateById(deviceInfo);
                }
            }));
            execute.end();

        }
        PageBean<DeviceInfoDto.Vo> voPageBean = toPageBean(DeviceInfoDto.Vo.class, iPage);

        return R.ok(voPageBean);
    }

    /**
     * 商户设备列表分页-穿梭框用
     *
     * @param dto
     * @return {@link R}<{@link List}<{@link DeviceInfoDto.MyDeviceInfo}>>
     */
    public R<PageBean<DeviceInfoDto.MyDeviceInfo>> myDeviceList(@RequestBody @Validated DeviceInfoDto.MyDeviceDTO dto) {
        PageBean pageBean = dto.getPage();
        String searchKey = dto.getSearchKey();
        Long mercId = dto.getMercId();
        Integer type = dto.getType();
        Long strategyId = dto.getStrategyId();

        GoodsDeviceDto.SelectList selectList = new GoodsDeviceDto.SelectList();
        if (strategyId != null) {
            selectList.setMercId(mercId).setPriceStrategyId(strategyId);
        }
        List<GoodsDeviceDto.Vo> goodsDevices = R.feignCheckData(goodsDeviceService.list(selectList));
        if (type == 2 && CollUtil.isEmpty(goodsDevices)) {
            // 右侧已选的
            return R.ok(new PageBean<>());
        }
        List<Long> deviceIds = goodsDevices.stream().map(GoodsDeviceDto.Vo::getDeviceId).collect(Collectors.toList());

        LambdaQueryWrapper<DeviceInfo> lqw = new MybatisPlusQuery().eqWrapper(dto, DeviceInfo.class)
                .build();
        lqw.in(type == 2 && CollUtil.isNotEmpty(deviceIds), DeviceInfo::getDeviceId, deviceIds);
        // 左侧排除掉已关联的
        lqw.notIn(type == 1 && CollUtil.isNotEmpty(deviceIds), DeviceInfo::getDeviceId, deviceIds);
        lqw.and(StrUtil.isNotEmpty(searchKey), wrapper -> wrapper
                .eq(DeviceInfo::getDeviceId, searchKey)
                .or()
                .likeRight(DeviceInfo::getDeviceName, searchKey));
        IPage<DeviceInfo> iPage = page(toIPage(pageBean), lqw);
        return R.ok(toPageBean(DeviceInfoDto.MyDeviceInfo.class, iPage));
    }


    @ApiOperation("点位设备数量查询")
    @Override
    public R<List<DeviceInfoDto.PlaceDeviceNumVo>> placeDeviceNum(@RequestBody @Validated DeviceInfoDto.PlaceDeviceNumDto dto) {
        return R.ok(baseMapper.placeDeviceNum(dto));
    }

    @Override
    @ApiOperation("设备列表带卡包数")
    public R<PageBean<DeviceInfoDto.AlgorithmChargingVo>> algorithmChargingDevice(@RequestBody @Validated DeviceInfoDto.AlgorithmCharging algorithmCharging) {
        PageBean pageBean = algorithmCharging.getPage();
        // 查询设备
        Integer value = SysDictUtils.getValue(EnumDeviceActiveStatus.Code.CODE.getCode(), EnumDeviceActiveStatus.N_1.getCode(), Integer.class);
        LambdaUpdateWrapper<DeviceInfo> lambdaUpdateWrapper = new LambdaUpdateWrapper<DeviceInfo>()
                .eq(DeviceInfo::getMercId, algorithmCharging.getMercId())
                .eq(DeviceInfo::getActiveState, value)
                .in(Emptys.check(algorithmCharging.getDeviceIds()), DeviceInfo::getDeviceId, algorithmCharging.getDeviceIds())
                .orderByDesc(DeviceInfo::getCreateTime);
        IPage<DeviceInfo> page = page(toIPage(pageBean), lambdaUpdateWrapper);
        PageBean<DeviceInfoDto.AlgorithmChargingVo> algorithmChargingVoPageBean = toPageBean(DeviceInfoDto.AlgorithmChargingVo.class, page);
        List<DeviceInfoDto.AlgorithmChargingVo> algorithmChargingVos = algorithmChargingVoPageBean.getRecords();
        if (!Emptys.check(algorithmChargingVos)) {
            return R.ok(algorithmChargingVoPageBean);
        }
        // 查询卡包数量
        List<DeviceAlgorithmChargingDto.CountVo> data = deviceAlgorithmChargingService.count(new DeviceAlgorithmChargingDto.Count()
                .setDeviceIds(new JArrayList<>(algorithmChargingVos).getProperty(DeviceInfoDto.AlgorithmChargingVo::getDeviceId))
                .setMercId(algorithmCharging.getMercId())
        ).getData();
        JMap<Long, DeviceAlgorithmChargingDto.CountVo> cover = new JArrayList<>(data).toMap(DeviceAlgorithmChargingDto.CountVo::getDeviceId).cover();
        algorithmChargingVos.forEach(algorithmChargingVo -> {
            DeviceAlgorithmChargingDto.CountVo countVo = cover.get(algorithmChargingVo.getDeviceId());
            Beans.copy(algorithmChargingVo, countVo);
        });
        return R.ok(algorithmChargingVoPageBean);
    }

    @Override
    @ApiOperation("设备在线数查询")
    public R<Map<Long, DeviceInfoDto.NetStateCountVo>> netStateCount(DeviceInfoDto.NetStateCountDto dto) {
        List<DeviceInfoDto.NetStateCount> netStateCountList = baseMapper.netStateCount(dto.getMercIdList());
        List<Long> netStateCountMercList = netStateCountList.stream().map(DeviceInfoDto.NetStateCount::getMercId).distinct().collect(Collectors.toList());
        Map<Long, DeviceInfoDto.NetStateCountVo> voMap = new HashMap<>();
        List<Long> mercIdList = Emptys.check(dto.getMercIdList()) ? dto.getMercIdList() : netStateCountMercList;
        if (!Emptys.check(mercIdList)) {
            return R.ok();
        }
        for (Long mercId : mercIdList) {
            Integer count = 0;
            Integer offline = 0;
            DeviceInfoDto.NetStateCountVo vo = new DeviceInfoDto.NetStateCountVo().setMercId(mercId);
            for (DeviceInfoDto.NetStateCount i : netStateCountList) {
                if (Objects.equals(i.getMercId(), mercId)) {
                    count += i.getCount();
                    if ("1".equals(i.getNetState())) {
                        vo.setOnLineCount(i.getCount());
                    } else {
                        offline += i.getCount();
                    }
                }
            }
            vo.setCount(count).setOffLineCount(offline);
            voMap.put(mercId, vo);
        }
        return R.ok(voMap);
    }

    @Override
    @ApiOperation("分页")
    public R<PageBean<DeviceInfoDto.Vo>> pageSingle(DeviceInfoDto.PageSingle dto) {
        PageBean pageBean = dto.getPage();
        LambdaQueryWrapper<DeviceInfo> lqw = new MybatisPlusQuery().eqWrapper(dto, DeviceInfo.class)
                .build();
        IPage<DeviceInfo> iPage = page(toIPage(pageBean), lqw);
        return R.ok(toPageBean(DeviceInfoDto.Vo.class, iPage));
    }

    @Override
    @ApiOperation("设备流水统计分页")
    public R<PageBean<DeviceDataDto.DeviceFlowCountVO>> deviceFlowCountPage(@RequestBody @Valid DeviceDataDto.DeviceFlowCountDTO dto) {
        Long curMercId = dto.getCurMercId();
        PageBean pageBean = dto.getPage();
        List<Long> mercIds = new ArrayList<>();
        Long chooseMercId = dto.getChooseMercId();
        if (chooseMercId == null) {
            // 未指定商户 查下级商户含自身
            mercIds = R.feignCheckData(mercService.getAllSubMercIds(new MercDto.QuerySubDTO().setParentMercID(curMercId)));
        } else {
            mercIds.add(chooseMercId);

        }
        LambdaQueryWrapper<DeviceInfo> lqw = new MybatisPlusQuery().eqWrapper(dto, DeviceInfo.class)
                .ge(DeviceInfo::getActiveTime, dto.getActiveStartTime())
                .le(DeviceInfo::getActiveTime, dto.getActiveEndStartTime())
                .build().in(DeviceInfo::getMercId, mercIds).eq(DeviceInfo::getActiveState, DeviceActiveStateEnum.TRUE.getCode());
        IPage<DeviceInfo> iPage = page(toIPage(pageBean), lqw);
        PageBean<DeviceDataDto.DeviceFlowCountVO> dataPage = toPageBean(DeviceDataDto.DeviceFlowCountVO.class, iPage);

        List<DeviceDataDto.DeviceFlowCountVO> records = dataPage.getRecords();
        if (CollUtil.isNotEmpty(records)) {

            List<Long> placeIds = records.stream().filter(s -> s.getPlaceId() != null).map(DeviceDataDto.DeviceFlowCountVO::getPlaceId).distinct().collect(Collectors.toList());

            List<MercPlaceDto.Vo> merPlaceList = R.feignCheckData(mercPlaceService.list(new MercPlaceDto.ListDto().setIds(placeIds)));
            if (Emptys.check(merPlaceList)) {
                Map<Long, MercPlaceDto.Vo> placeMap = merPlaceList.stream().collect(Collectors.toMap(MercPlaceDto.Vo::getId, p -> p));

                for (DeviceDataDto.DeviceFlowCountVO record : records) {
                    Long placeId = record.getPlaceId();
                    if (placeId == null) {
                        continue;
                    }
                    // 点位 区域 反显
                    MercPlaceDto.Vo placeVo = placeMap.get(placeId);
                    if (placeVo != null) {
                        record.setDistrictName(placeVo.getRegionName());
                        record.setPlaceName(placeVo.getPlaceName());
                    }
                }
                dataPage.setRecords(records);
            }
        }
        return R.ok(dataPage);
    }


    /**
     * 按商户分页查设备
     *
     * @param dto
     * @return {@link PageBean}<{@link DeviceInfoDto.Vo}>
     */
    public PageBean<DeviceInfoDto.Vo> pageByMerc(DeviceInfoDto.PageByMercDto dto) {
        PageBean pageBean = dto.getPage();
        String deviceSearch = dto.getDeviceSearch();
        List<Long> myDeviceIds = dto.getMyDeviceIds();
        LambdaQueryWrapper<DeviceInfo> lqw = new MybatisPlusQuery().eqWrapper(dto, DeviceInfo.class)
                .build();
        List<Long> deviceIds = dto.getDeviceIds();
        Boolean choosed = dto.getChoosed();


        if (BooleanUtil.isFalse(choosed)) {
            // 未选择 排除已选择
            if (CollUtil.isNotEmpty(deviceIds)) {
                lqw.notIn(DeviceInfo::getDeviceId, deviceIds);
            } else {
                if (CollUtil.isEmpty(myDeviceIds)) {
                    return new PageBean<>();
                }
                lqw.in(DeviceInfo::getDeviceId, myDeviceIds);
            }
        } else {
            // 已选择
            // 指定设备 已选择
            if (CollUtil.isNotEmpty(deviceIds)) {
                lqw.in(DeviceInfo::getDeviceId, deviceIds);
            } else {
                return new PageBean<>();
            }
        }

        // 设备搜索
        lqw.and(StrUtil.isNotEmpty(deviceSearch),
                wrapper -> wrapper
                        .likeRight(DeviceInfo::getDeviceName, deviceSearch)
                        .or()
                        .likeRight(DeviceInfo::getDeviceId, deviceSearch)
        );
        IPage<DeviceInfo> iPage = page(toIPage(pageBean), lqw);
        return toPageBean(DeviceInfoDto.Vo.class, iPage);
    }

    @Override
    @ApiOperation("对象查询")
    public R<DeviceInfoDto.Vo> obj(DeviceInfoDto.Obj obj) {
        // 设备信息
        LambdaQueryWrapper<DeviceInfo> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(obj, DeviceInfo.class).build();
        List<DeviceInfo> list = list(lambdaQueryWrapper);
        if (!Emptys.check(list)) {
            return R.ok();
        }
        DeviceInfoDto.Vo deviceInfo = copy(DeviceInfoDto.Vo.class, list.get(0));
        int num = 0;
        if (obj.getIsSysinfo()) {
            num++;
        }
        if (obj.getIsStatus()) {
            num++;
        }
        if (obj.getIsRegister()) {
            num++;
        }
        if (num > 0) {
            ThreadPoolUtils.Execute execute = ThreadPoolUtils.excPoll(DeviceThreadPoolConfig.DEVICE_COMMON_POLL, num);
            if (obj.getIsSysinfo()) {
                execute.execute(() -> {
                    // 系统信息
                    DeviceSysinfoDto.Vo deviceSysinfo = deviceSysinfoService.get(new DeviceSysinfoDto.Vo().setDeviceId(deviceInfo.getDeviceId())).getData();
                    deviceInfo.setDeviceSysinfo(deviceSysinfo);
                });
            }
            if (obj.getIsStatus()) {
                execute.execute(() -> {
                    // 状态信息
                    DeviceStatusDto.Vo deviceStatus = deviceStatusService.obj(new DeviceStatusDto.Vo().setDeviceId(deviceInfo.getDeviceId())).getData();
                    deviceInfo.setDeviceStatus(deviceStatus);
                });
            }
            if (obj.getIsRegister()) {
                execute.execute(() -> {
                    // 注册信息
                    DeviceRegisterDto.Vo deviceRegister = deviceRegisterService.obj(new DeviceRegisterDto.Vo().setDeviceId(deviceInfo.getDeviceId())).getData();
                    deviceInfo.setDeviceRegister(deviceRegister);
                });
            }
            execute.end();
        }
        return R.ok(deviceInfo);
    }

    @ApiOperation("反显设备名称")
    @Override
    public R<Map<Long, String>> getDeviceNameList(DeviceInfoDto.DeviceIdDto dto) {
        LambdaQueryWrapper<DeviceInfo> lqw = new LambdaQueryWrapper<DeviceInfo>()
                .in(DeviceInfo::getDeviceId, dto.getDeviceId())
                .select(DeviceInfo::getDeviceId, DeviceInfo::getDeviceName);
        List<DeviceInfo> deviceInfoList = list(lqw);
        return R.ok(deviceInfoList.stream().collect(Collectors.toMap(DeviceInfo::getDeviceId, i -> Optional.ofNullable(i.getDeviceName()).orElse(""))));
    }

    @Override
    public R<DeviceSysinfoDto.Vo> getDeviceSysinfo(DeviceSysinfoDto.DeviceSysInfo dto) {
        String dtoDeviceSN = dto.getDeviceSN();
        DeviceSysinfo one = deviceSysinfoService.getOne(Wrappers.<DeviceSysinfo>lambdaQuery().eq(DeviceSysinfo::getDeviceSn, dtoDeviceSN));
        return R.ok(BeanUtil.copyProperties(one, DeviceSysinfoDto.Vo.class));
    }

    @Override
    @ApiOperation("设备访问历史添加")
    public R history(DeviceInfoDto.Obj obj) {
        // 获取字典
        SysDictRedis sysDictRedis = SysDictUtils.get(EnumDataClearSize.Code.CODE.getCode(), EnumDataClearSize.DEVICE_HISTORY_TWIG.getCode());
        Integer value = Integer.valueOf(sysDictRedis.getValue());
        // 获取redis
        String key = keyPrefix + AuthorizeUtils.getLoginId(Long.class);
        List<String> list = redisService.getList(key);
        list.add(0, String.valueOf(obj.getDeviceId()));
        // 去重
        List<String> redisList = new ArrayList<>();
        JList<String> comparing = new JArrayList<>(list).comparing();
        if (comparing.size() > value) {
            for (int i = 0; i < value; i++) {
                redisList.add(comparing.get(i));
            }
        } else {
            redisList = comparing;
        }
        redisService.removeList(key);
        redisService.setList(key, redisList);
        return R.ok();
    }

    @Override
    @ApiOperation("开门检测")
    public R<DeviceInfoDto.Vo> checkOpenDoor(DeviceInfoDto.Obj obj) {
        DeviceInfoDto.Vo deviceInfo = obj(new DeviceInfoDto.Obj()
                .setDeviceId(obj.getDeviceId())
                .setIsStatus(true)
        ).getData();
        if (deviceInfo == null) {
            return R.fail("设备不存在");
        }
        DeviceStatusDto.Vo deviceStatus = deviceInfo.getDeviceStatus();
        SysDictRedis qualitySets = SysDictUtils.get(EnumQualityMercSets.Code.CODE.getCode(), EnumQualityMercSets.MERC_CODE.getCode());
        List<String> qualitySetList = Arrays.asList(qualitySets.getValue().split(","));
        // 设备当前商户是质检时不检查
        if (!qualitySetList.contains(deviceInfo.getMercCode())) {
            check(deviceInfo.getActiveState(), 2, "设备未激活");
            check(deviceInfo.getFreezeStatus(), 2, "设备已冻结");
            check(obj.getDoor() != null && obj.getDoor() == 1 ? deviceStatus.getLockStateR() : deviceStatus.getLockStateL(), 2, "设备已锁机");
            check(deviceStatus.getIsPay(), Boolean.FALSE, "设备不可交易");
            check(deviceInfo.getBusyState(), 2, "设备已停运");
            SysDictRedis sysDictRedis = SysDictUtils.get(EnumDeviceFaultLevelPayThreshold.Code.CODE.getCode(), EnumDeviceFaultLevelPayThreshold.NOT_PAY.getCode());
            check(deviceInfo.getFaultLevel(), sysDictRedis.getValue(), "设备故障");
        }
        check(deviceStatus.getNetState(), 2, "设备已离线");
        check(obj.getDoor() != null && obj.getDoor() == 1 ? deviceStatus.getDoorStateR() : deviceStatus.getDoorStateL(), 1, "设备正在使用中，请稍后");
        return R.ok(deviceInfo);
    }

    @PostMapping("historyList")
    @ApiOperation("设备访问历史查询")
    public R<List<DeviceInfoDto.Vo>> historyList() {
        // 获取redis
        String key = keyPrefix + AuthorizeUtils.getLoginId(Long.class);
        List<String> deviceIds = redisService.getList(key);
        if (!Emptys.check(deviceIds)) {
            return R.ok();
        }
        // 查询数据库
        List<DeviceInfo> list = list(new LambdaQueryWrapper<DeviceInfo>().in(DeviceInfo::getDeviceId, deviceIds));
        return R.ok(copy(DeviceInfoDto.Vo.class, list));
    }

    @ApiOperation("根据点位ID更新坐标")
    public R updateLonByPlaceId(@RequestBody @Validated DeviceInfoDto.UpdateLonByPlaceId dto) {
        LambdaUpdateWrapper<DeviceInfo> luw = new LambdaUpdateWrapper<DeviceInfo>()
                .eq(DeviceInfo::getPlaceId, dto.getPlaceId())
                .set(DeviceInfo::getLon, dto.getLon())
                .set(DeviceInfo::getLat, dto.getLat());
        update(luw);
        return R.ok();
    }

    @ApiOperation("修改")
    @PostMapping("update")
    public R update(@RequestBody @Validated DeviceInfoDto.Update update) {
        DeviceInfo deviceInfo = getById(update.getDeviceId());
        // 判断是否更新了点位
        if (Emptys.check(update.getPlaceId()) && !Objects.equals(deviceInfo.getPlaceId(), update.getPlaceId())) {
            // 把设备绑定给点位管理员
            MercUserBindDeviceDto.BindByDeviceUpdate bindByPlaceIdDTto = new MercUserBindDeviceDto.BindByDeviceUpdate();
            bindByPlaceIdDTto.setDeviceId(update.getDeviceId())
                    .setPlaceId(update.getPlaceId())
                    .setMercId(deviceInfo.getMercId());
            mercUserBindDeviceService.bindByDeviceUpdate(bindByPlaceIdDTto);
            // 更新坐标为点位的坐标
            MercPlaceDto.ObjVo place = mercPlaceService.obj(new MercPlaceDto.Obj().setId(update.getPlaceId())).getData();
            update.setLat(place.getLat()).setLon(place.getLon());
        }
        DeviceInfo deviceSave = copy(DeviceInfo.class, update)
                .setUpdateTime(LocalDateTime.now());
        updateById(deviceSave);


        Long deviceId = deviceSave.getDeviceId();
        DeviceInfoDto.Vo device = R.feignCheckData(this.obj(new DeviceInfoDto.Obj().setDeviceId(deviceId).setIsSysinfo(true)));
        Long algorithmId = update.getAlgorithmId();
        //切换算法逻辑处理
        if (algorithmId != null && !Objects.equals(deviceInfo.getAlgorithmId(), algorithmId)) {
            //1、删除设备商品
            GoodsDeviceDto.DelByDeviceId delByDeviceId = new GoodsDeviceDto.DelByDeviceId();
            delByDeviceId.setDeviceId(deviceId);
//            delByDeviceId.setMercId(delByDeviceId.getMercId());
            R.feignCheckData(goodsDeviceService.delByDeviceId(delByDeviceId));
        }
        if (algorithmId != null && Objects.equals(AlgorithmTypeEnum.CLOUD.getId(), algorithmId)) {
            // 云从算法
            boolean b = cloudWalkApiService.checkDeviceExist(deviceId);
            if (!b) {
                // 货柜不存在，新增
                cloudWalkApiService.containerAdd(new ContainerAddDTO().setContainerCode(String.valueOf(deviceId)));
            }
        } else if (algorithmId != null && Objects.equals(AlgorithmTypeEnum.TY.getId(), algorithmId)) {
            // 拓元算法
            // 查询登记设备
            DeviceQueryVO deviceQueryVO = tyApiService.deviceQuery(new DeviceQueryDTO().setCpuId(String.valueOf(deviceId)));
            Integer status = deviceQueryVO.getStatus();
            if (status != null && 1000 == status.intValue()) {
                // 未知的设备CPUID,进行登记
                tyApiService.deviceReg(new DeviceRegDTO()
                        .setCpuId(String.valueOf(deviceId))
                        .setDeviceNumber(String.valueOf(deviceId))
                );
            }
        }
        Integer deviceType = device.getDeviceType();
        if (ObjectUtil.equals(DeviceTypeEnum.TYPE5.getCode(), deviceType)) {
            DeviceUpdateDTO deviceUpdateDTO = new DeviceUpdateDTO();
            if (StrUtil.isNotEmpty(update.getDeviceName())) {
                deviceUpdateDTO.setDeviceName(update.getDeviceName());
            }
            String deviceSn = device.getDeviceSysinfo().getDeviceSn();
            if (deviceUpdateDTO != null) {
                deviceUpdateDTO.setTerminalId(String.valueOf(update.getDeviceId()));
                deviceUpdateDTO.setBoardSn(deviceSn);

                alipayDeviceService.deviceUpdate(deviceUpdateDTO);
            }
        }
        return R.ok();
    }

    @Override
    @ApiOperation("批量修改")
    public R updateBatch(@RequestBody List<DeviceInfoDto.Update> updates) {
        LocalDateTime now = LocalDateTime.now();
        List<DeviceInfo> deviceInfos = new ArrayList<>(updates.size());
        updates.forEach(update -> deviceInfos.add(copy(DeviceInfo.class, update).setUpdateTime(now)));
        updateBatchById(deviceInfos);
        return R.ok();
    }

    @Override
    @ApiOperation("激活数量统计")
    public R<Integer> activationCount(DeviceInfoDto.ActivationCount activationCount) {
        LambdaQueryWrapper<DeviceInfo> lambdaQueryWrapper = new LambdaQueryWrapper<DeviceInfo>()
                .eq(DeviceInfo::getMercId, activationCount.getMercId())
                .eq(DeviceInfo::getActiveState, 1)
                .ne(Emptys.check(activationCount.getNotDeviceType()), DeviceInfo::getDeviceType, activationCount.getNotDeviceType())
                .between(Emptys.check(activationCount.getBeginTime()), DeviceInfo::getActiveTime, activationCount.getBeginTime(), activationCount.getEndTime());
        long count = count(lambdaQueryWrapper);
        return R.ok((int) count);
    }

    @Override
    @ApiOperation("更新商户线路")
    public R updateLine(@RequestBody @Validated DeviceInfoDto.UpdateLine updateLine) {
        LambdaUpdateWrapper<DeviceInfo> luw = new LambdaUpdateWrapper<DeviceInfo>().eq(DeviceInfo::getMercId, updateLine.getMercId());
        // 绑定线路,更换线路
        if (DeviceInfoDto.UPDATE.equals(updateLine.getType())) {
            DeviceInfo deviceInfo = new DeviceInfo();
            deviceInfo.setPlaceLineId(updateLine.getPlaceLineId());
            luw.in(DeviceInfo::getDeviceId, updateLine.getDeviceIds());
            baseMapper.update(deviceInfo, luw);
        }
        // 删除线路
        if (DeviceInfoDto.DEL.equals(updateLine.getType())) {
            luw.eq(DeviceInfo::getPlaceLineId, updateLine.getWherePlaceLineId());
            luw.set(DeviceInfo::getPlaceLineId, null);
            baseMapper.update(null, luw);
        }
        // 解绑线路 设置线路ID为null
        if (DeviceInfoDto.CLEAR.equals(updateLine.getType())) {
            luw.in(DeviceInfo::getDeviceId, updateLine.getDeviceIds());
            luw.set(DeviceInfo::getPlaceLineId, null);
            baseMapper.update(null, luw);
        }
        return R.ok();
    }


    @Override
    @ApiOperation("更新商户点位")
    public R updatePlace(@RequestBody @Validated DeviceInfoDto.UpdatePlace updatePlace) {
        LambdaUpdateWrapper<DeviceInfo> luw = new LambdaUpdateWrapper<DeviceInfo>().eq(DeviceInfo::getMercId, updatePlace.getMercId());
        // 绑定点位,更换点位
        if (DeviceInfoDto.UPDATE.equals(updatePlace.getType())) {
            DeviceInfo deviceInfo = new DeviceInfo();
            deviceInfo.setPlaceId(updatePlace.getPlaceId());
            luw.in(DeviceInfo::getDeviceId, updatePlace.getDeviceIds());
            baseMapper.update(deviceInfo, luw);
        }
        // 删除点位
        if (DeviceInfoDto.DEL.equals(updatePlace.getType())) {
            luw.eq(DeviceInfo::getPlaceId, updatePlace.getWherePlaceId());
            luw.set(DeviceInfo::getPlaceId, null);
            baseMapper.update(null, luw);
        }
        // 解绑点位 设置点位ID为null
        if (DeviceInfoDto.CLEAR.equals(updatePlace.getType())) {
            luw.in(DeviceInfo::getDeviceId, updatePlace.getDeviceIds());
            luw.set(DeviceInfo::getPlaceId, null);
            baseMapper.update(null, luw);
        }
        return R.ok();
    }

    @ApiOperation("设备分页查询")
    @Override
    public R<PageBean<DeviceInfoDto.Vo2>> page(@RequestBody DeviceInfoDto.Page page) {
        return R.ok(queryPage(page));
    }

    @ApiOperation("导出设备列表")
    @PostMapping("exportDevices")
    public void exportDevices(HttpServletResponse response, @RequestBody @Valid DeviceInfoDto.Page page) throws IOException {
        PageBean<DeviceInfoDto.Vo2> pageBean = queryPage(page);
        List<DeviceInfoDto.Vo2> records = pageBean.getRecords();
        List<DeviceInfoDto.DeviceExcelVO> deviceExcelVOS = BeanUtil.copyToList(records, DeviceInfoDto.DeviceExcelVO.class);
        // 输出
        ExcelUtils.write(response, "设备列表.xls", "设备列表", DeviceInfoDto.DeviceExcelVO.class, deviceExcelVOS);
    }


    @ApiOperation("导出设备(异步)")
    @PostMapping("exportDevices/async")
    public void exportDevicesAsync(@RequestBody @Validated DeviceInfoDto.Page page) {
        PageBean<DeviceInfoDto.Vo2> pageBean = queryPage(page);
        List<DeviceInfoDto.Vo2> records = pageBean.getRecords();
        List<DeviceInfoDto.DeviceExcelVO> deviceExcelVOS = BeanUtil.copyToList(records, DeviceInfoDto.DeviceExcelVO.class);
        // 异步导出参数封装
        ExcelDTO<DeviceInfoDto.DeviceExcelVO> excelDTO = new ExcelDTO<>();
        excelDTO.setData(deviceExcelVOS);
        excelDTO.setHead(DeviceInfoDto.DeviceExcelVO.class);
        excelDTO.setSheetName(FileExportType.DEVICE_INFO.getDescription());
        excelDTO.setFileExportType(FileExportType.DEVICE_INFO);
        // 执行导出
        fileExportService.exportExcelAsync(excelDTO);
    }

    @PostMapping("nearbyPage")
    @ApiOperation("附近设备分页查询")
    public R<PageBean<DeviceInfoDto.Vo2>> nearbyPage(@RequestBody DeviceInfoDto.Page page) {
        if (!Emptys.check(page.getLon()) || !Emptys.check(page.getLat())) {
            throw new CommRuntimeException("经纬度不能为空");
        }
        return R.ok(queryPage(page));
    }

    @LogOperate(logType = LogEnum.LogType.V2, optType = LogEnum.OptType.V2, logContent = "商户设备授权")
    @ApiOperation(value = "商户设备授权", hidden = true)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> mercDeviceAuth(DeviceInfoDto.MercDeviceAuthDto auth) {
        //检查
        FunctionUtils.NoParamsNoResult check = () -> {
            //商户二维码
            MercDeviceQrConfigDto.Vo mercDeviceQrConfig = mercDeviceQrConfigService.obj(new MercDeviceQrConfigDto.Vo().setMercId(auth.getMercId())).getData();
            //设备ID号生成记录
            List<DeviceCreateIds> list = deviceCreateIdsService.list(new LambdaQueryWrapper<DeviceCreateIds>()
                    .in(DeviceCreateIds::getDeviceId, auth.getDeviceIds())
            );
            JList<DeviceCreateIds> deviceCreateIds = new JArrayList<>(list);
            //授权商户有专用码
            if (Emptys.check(mercDeviceQrConfig.getMercId())) {
                //设备无专用商户
                JList<DeviceCreateIds> isNullDeviceQrMercIds = deviceCreateIds.filter().isNull(DeviceCreateIds::getDeviceQrMercId).list();
                isNullDeviceQrMercIds.addAll(deviceCreateIds.filter().eq(DeviceCreateIds::getDeviceQrMercId, -1).list());
                if (Emptys.check(isNullDeviceQrMercIds)) {
                    throw new CommRuntimeException("请授权专用设备给该商户");
                }
                //有专用商户的设备数量低于需要授权的数量
                JList<DeviceCreateIds> isNotNullDeviceQrMercIds = deviceCreateIds.filter()
                        .isNotNull(DeviceCreateIds::getDeviceQrMercId)
                        .ne(DeviceCreateIds::getDeviceQrMercId, -1)
                        .list();
                if (isNotNullDeviceQrMercIds.size() < auth.getDeviceIds().size()) {
                    throw new CommRuntimeException("请授权专用设备给该商户");
                }
                //循环比对授权商户专用码编码是否和设备专用商户专用码编码是否一致
                List<MercDeviceQrConfigDto.Vo> mercDeviceQrConfigs = mercDeviceQrConfigService.list(new MercDeviceQrConfigDto.SelectList().setMercId(isNotNullDeviceQrMercIds.getProperty(DeviceCreateIds::getDeviceQrMercId))).getData();
                for (MercDeviceQrConfigDto.Vo deviceQrConfig : mercDeviceQrConfigs) {
                    if (!deviceQrConfig.getCode().equals(mercDeviceQrConfig.getCode())) {
                        throw new CommRuntimeException("请授权专用设备给该商户");
                    }
                }
            }
            //授权商户无专用码
            if (!Emptys.check(mercDeviceQrConfig.getMercId())) {
                //设备有专用商户
                JList<DeviceCreateIds> isNotNullDeviceQrMercIds = deviceCreateIds.filter()
                        .isNotNull(DeviceCreateIds::getDeviceQrMercId)
                        .ne(DeviceCreateIds::getDeviceQrMercId, -1)
                        .list();
                if (Emptys.check(isNotNullDeviceQrMercIds)) {
                    throw new CommRuntimeException("专用设备无法授权给该商户");
                }
            }
        };
        //新增设备年费
        FunctionUtils.ParamsNoResult<List<DeviceInfo>> deviceAnnualFee = deviceInfos -> {
            Map<String, SysDictRedis> stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee");
            if (stringSysDictRedisMap.isEmpty()) {
                return;
            }
            JList<String> mercIds = new JHashMap<>(stringSysDictRedisMap).getValues().getProperty(SysDictRedis::getCode);
            if (!mercIds.contains(String.valueOf(auth.getMercId()))) {
                return;
            }
            LocalDateTime now = LocalDateTime.now();
            deviceInfos.forEach(deviceInfo -> {
                deviceAnnualFeeService.update(new LambdaUpdateWrapper<DeviceAnnualFee>()
                        .set(DeviceAnnualFee::getIsThis, false)
                        .eq(DeviceAnnualFee::getDeviceId, deviceInfo.getDeviceId())
                );
                DeviceAnnualFee saveOrUpdateInfo = deviceAnnualFeeService.getOne(new LambdaQueryWrapper<DeviceAnnualFee>()
                        .eq(DeviceAnnualFee::getDeviceId, deviceInfo.getDeviceId())
                        .eq(DeviceAnnualFee::getMercId, auth.getMercId())
                );
                if (saveOrUpdateInfo != null) {
                    saveOrUpdateInfo.setIsThis(true);
                } else {
                    saveOrUpdateInfo = new DeviceAnnualFee()
                            .setId(YitIdHelper.nextId())
                            .setMercId(auth.getMercId())
                            .setDeviceId(deviceInfo.getDeviceId())
                            .setDeviceType(deviceInfo.getDeviceType())
                            .setTimeout(now)
                            .setCreateTime(now)
                            .setIsThis(true);
                }
                deviceAnnualFeeService.saveOrUpdate(saveOrUpdateInfo);
            });
        };
        //商家费用分账模式设备初始化
        FunctionUtils.ParamsNoResult<List<DeviceInfo>> mercDeviceCost = deviceInfos -> {
            MercDeviceCostConfigDto.Vo mercDeviceCostConfig = mercDeviceCostConfigService.query(new MercDeviceCostConfigDto.Query()
                    .setMercId(auth.getMercId())
                    .setType(1)
            ).getData();
            if (mercDeviceCostConfig == null) {
                return;
            }
            deviceInfos.forEach(deviceInfo -> mercDeviceCostConfigService.set(new MercDeviceCostConfigDto.Set()
                    .setMercId(auth.getMercId())
                    .setType(2)
                    .setDeviceId(deviceInfo.getDeviceId())
                    .setDayAmount(mercDeviceCostConfig.getDayAmount())
                    .setSingleOrdersAmount(mercDeviceCostConfig.getSingleOrdersAmount())
            ));
        };
        check.run();
        Long mercId = auth.getMercId();
        String mercCode = auth.getMercCode();
        Long algorithmId = auth.getAlgorithmId();
        String mercName = auth.getMercName();
        // 商户最终设备列表
        List<Long> deviceIds = auth.getDeviceIds();
        List<DeviceInfo> devices = getDevicesByMercId(mercId);
        // 取消商户设备授权
        if (CollUtil.isEmpty(deviceIds)) {
            if (CollUtil.isEmpty(devices)) {
                return R.ok(Boolean.TRUE);
            }
        }
        // 更新商户设备授权
        List<DeviceInfo> deviceInfos = this.listByIds(deviceIds);
        // 绑定前，先进行旧的用户设备关联表清理
        R.feignCheckData(mercService.unBindSubMercDevice(new MiniDeviceInfoDto.MercDeviceUnBindDto().setDeviceIds(deviceIds)));
        List<DeviceStatus> deviceStatuses = new ArrayList<>();
        for (DeviceInfo deviceInfo : deviceInfos) {
            Integer deviceType = deviceInfo.getDeviceType();
            Long deviceId = deviceInfo.getDeviceId();
            // 清理点位
            deviceInfo.setPlaceId(-1L);

            if (ObjectUtil.equals(deviceType, DeviceTypeEnum.TYPE5.getCode())) {
                // 支付宝设备算法
                algorithmId = AlgorithmTypeEnum.ALIPAY.getId();
            } else {
                // 非支付宝算法
                if (AlgorithmTypeEnum.CLOUD.getId() == algorithmId) {
                    // 云从算法
                    boolean b = cloudWalkApiService.checkDeviceExist(deviceId);
                    if (!b) {
                        // 货柜不存在，新增
                        cloudWalkApiService.containerAdd(new ContainerAddDTO().setContainerCode(String.valueOf(deviceId)));
                    }
                } else if (AlgorithmTypeEnum.TY.getId() == algorithmId) {
                    // 拓元算法
                    // 查询登记设备
                    DeviceQueryVO deviceQueryVO = tyApiService.deviceQuery(new DeviceQueryDTO().setCpuId(String.valueOf(deviceId)));
                    Integer status = deviceQueryVO.getStatus();
                    if (status != null && 1000 == status.intValue()) {
                        // 未知的设备CPUID,进行登记
                        tyApiService.deviceReg(new DeviceRegDTO()
                                .setCpuId(String.valueOf(deviceId))
                                .setDeviceNumber(String.valueOf(deviceId))
                        );
                    }
                }
            }
            int refMercId = deviceInfo.getMercId().intValue();
            String refMercCode = deviceInfo.getMercCode();

            if (BooleanUtil.isFalse(auth.getMercOperate())) {
                // 非商户操作
                //  只有解绑后，才能给顶级商户授权
                if (refMercId != -1 && refMercId != mercId.intValue()) {
                    StrBuilder sb = StrBuilder.create();
                    String errMsg = sb.append("设备[")
                            .append(deviceInfo.getDeviceId())
                            .append("]")
                            .append("已被商户[")
                            .append(deviceInfo.getMercName())
                            .append("]绑定，请先进行解绑！")
                            .toString();
                    // 非质检商户需要进行判断，质检商户跳过
                    if (!QA_MERC_CODE.equals(refMercCode)) {
                        // 已关联别商户
                        return R.fail(errMsg, Boolean.FALSE);
                    }

                }
            } else {
                // 商户操作直接转移给子商户
            }

            // 绑定关系
            deviceInfo.setMercId(mercId).setMercCode(mercCode).setAlgorithmId(algorithmId).setMercName(mercName);
            // 标记机器可交易
            DeviceStatus deviceStatus = new DeviceStatus()
                    .setDeviceId(deviceInfo.getDeviceId())
                    .setIsPay(true);
            deviceStatuses.add(deviceStatus);
        }
        saveOrUpdateBatch(deviceInfos);
        if (Emptys.check(deviceStatuses)) {
            deviceStatusService.updateBatchById(deviceStatuses);
        }
        deviceAnnualFee.run(deviceInfos);
        mercDeviceCost.run(deviceInfos);
        String log = String.format("商户设备授权：授权商户：%s,授权设备：%s", auth.getMercId() + "," + auth.getMercName(), deviceIds.size() == 0 ? "取消所有设备授权" : CollUtil.join(deviceIds, ","));
        return R.ok(Boolean.TRUE).setLogMsg(log);
    }

    /**
     * 解绑机器 回收
     *
     * @param dto
     * @return
     */
    @Override
    @LogOperate(logType = LogEnum.LogType.V2, optType = LogEnum.OptType.V2, logContent = "解绑商户")
    public R<Boolean> unBindMercDevice(DeviceInfoDto.MercDeviceUnBindDto dto) {
        List<DeviceInfo> deviceInfos = this.listByIds(dto.getDeviceIds());
        List<MercDto.Vo> mercs = mercService.list(new MercDto.SelectList()
                .setMercIds(new JArrayList<>(deviceInfos).getProperty(DeviceInfo::getMercId).comparing())
        ).getData();
        StringJoiner mercIds = new StringJoiner(",");
        StringJoiner mercNames = new StringJoiner(",");
        JMap<Long, MercDto.Vo> mercMaps = new JArrayList<>(mercs).toMap(MercDto.Vo::getId).cover();
        for (DeviceInfo deviceInfo : deviceInfos) {
            MercDto.Vo vo = mercMaps.get(deviceInfo.getMercId());
            mercIds.add(String.valueOf(deviceInfo.getMercId()));
            mercNames.add(vo.getName());
        }
        String log = String.format("解绑商户：被解绑商户：%s,解绑设备：%s", mercIds + "," + mercNames, CollUtil.isEmpty(dto.getDeviceIds()) ? "" : CollUtil.join(dto.getDeviceIds(), ","));
        return R.ok(removeMerRefDevicesToTopMerc(deviceInfos)).setLogMsg(log);
    }

    /**
     * 商户解绑设备 --商家端
     *
     * @param dto
     * @return {@link Boolean}
     */
    public Boolean unBindDeviceByMerc(DeviceInfoDto.MercDeviceUnBindDto dto) {
        List<DeviceInfo> deviceInfos = this.listByIds(dto.getDeviceIds());
        if (CollUtil.isNotEmpty(deviceInfos)) {
            List<Long> mercIds = deviceInfos.stream().map(DeviceInfo::getMercId).collect(Collectors.toList());
            mercIds.forEach(mercId -> {
                if (mercId.longValue() == dto.getMercId().longValue()) {
                    throw new CommRuntimeException("只能选择子商户设备进行解绑，请检查是否选择了当前商户下的设备！");
                }
            });
        }
        return removeMerDevicesByDeviceIds(dto, deviceInfos);
    }

    private Boolean removeMerDevicesByDeviceIds(DeviceInfoDto.MercDeviceUnBindDto dto, List<DeviceInfo> deviceInfos) {
        if (CollUtil.isNotEmpty(deviceInfos)) {
            deviceInfos.forEach(deviceInfo -> {
                // 回收 到当前操作商户
                deviceInfo.setMercId(dto.getMercId());
                deviceInfo.setMercDeviceCode(StrUtil.EMPTY);
                // 清理点位信息
                deviceInfo.setPlaceId(-1L);
                deviceInfo.setMercName(dto.getMercName());
                deviceInfo.setMercCode(dto.getMercCode());
            });

            // 用户设备关联表清理
            List<Long> deviceIds = deviceInfos.stream().map(DeviceInfo::getDeviceId).collect(Collectors.toList());
            R.feignCheckData(mercService.unBindSubMercDevice(new MiniDeviceInfoDto.MercDeviceUnBindDto().setDeviceIds(deviceIds)));
            // 批量更新
            return updateBatchById(deviceInfos);
        }

        return Boolean.FALSE;

    }

    /**
     * 回收机器
     *
     * @param deviceInfos
     * @return
     */
    private Boolean removeMerRefDevicesToTopMerc(List<DeviceInfo> deviceInfos) {
        MercDto.Vo mercCheck = R.feignCheckData(mercFeignService.obj(new MercDto.ListDTO().setMercCode(QA_MERC_CODE)));
        MercDto.Vo mercJlsQaCheck = R.feignCheckData(mercFeignService.obj(new MercDto.ListDTO().setMercCode(JLS_QA_MERC_CODE)));
        if (CollUtil.isNotEmpty(deviceInfos)) {
            deviceInfos.forEach(deviceInfo -> {
                String mercCode = deviceInfo.getMercCode();
                deviceInfo.setMercDeviceCode(StrUtil.EMPTY);

                if (mercCode.startsWith(JLS_MERC_CODE)) {
                    //聚邻社的机器，回收到，聚邻社的质检
                    deviceInfo.setMercId(mercJlsQaCheck.getId());
                    deviceInfo.setMercName(mercJlsQaCheck.getName());
                    deviceInfo.setMercCode(JLS_QA_MERC_CODE);
                } else {
                    //兴元质检
                    deviceInfo.setMercId(mercCheck.getId());
                    deviceInfo.setMercName(mercCheck.getName());
                    deviceInfo.setMercCode(QA_MERC_CODE);
                }
                // 清理点位
                deviceInfo.setPlaceId(-1L);
                //删除设备商品
                GoodsDeviceDto.DelByDeviceId delByDeviceId = new GoodsDeviceDto.DelByDeviceId();
                delByDeviceId.setDeviceId(deviceInfo.getDeviceId());
                R.feignCheckData(goodsDeviceService.delByDeviceId(delByDeviceId));
            });
            // 批量更新
            return updateBatchById(deviceInfos);
        }

        return Boolean.FALSE;

    }

    /**
     * 批量移除商户设备绑定关系
     *
     * @param deviceInfos
     * @return
     */
    private Boolean removeMerRefDevices(List<DeviceInfo> deviceInfos, Long parentId) {
        if (CollUtil.isNotEmpty(deviceInfos) && parentId != null) {
            deviceInfos.forEach(deviceInfo -> {
                // 非顶级兴元商户，解绑后，机器归父商户
                if (parentId != 1) {
                    MercDto.Vo mercParent = R.feignCheckData(mercFeignService.obj(new MercDto.ListDTO().setId(parentId)));
                    if (mercParent != null) {
                        deviceInfo.setMercId(mercParent.getId());
                        deviceInfo.setMercName(mercParent.getName());
                        deviceInfo.setMercCode(mercParent.getMercCode());
                    } else {
                        deviceInfo.setMercId(-1L);
                        deviceInfo.setMercName(StrUtil.EMPTY);
                        deviceInfo.setMercCode(StrUtil.EMPTY);
                    }

                }
                // 一级商户，解绑后，直接释放
                if (parentId == 0) {
                    MercDto.Vo mercCheck = R.feignCheckData(mercFeignService.obj(new MercDto.ListDTO().setMercCode(QA_MERC_CODE)));
                    if (mercCheck != null) {
                        deviceInfo.setMercId(mercCheck.getId());
                        deviceInfo.setMercName(mercCheck.getName());
                        deviceInfo.setMercCode(mercCheck.getMercCode());
                    } else {
                        deviceInfo.setMercId(-1L);
                        deviceInfo.setMercName(StrUtil.EMPTY);
                        deviceInfo.setMercCode(StrUtil.EMPTY);
                    }

                }

            });
            // 批量更新
            return updateBatchById(deviceInfos);
        }

        return Boolean.FALSE;

    }


    @ApiOperation("集合查询")
    @Override
    public R<List<DeviceInfoDto.Vo>> list(DeviceInfoDto.ListDto dto) {
        List<DeviceInfo> list = list(new LambdaQueryWrapper<DeviceInfo>()
                .eq(Emptys.check(dto.getFreezeStatus()), DeviceInfo::getFreezeStatus, dto.getFreezeStatus())
                .in(CollUtil.isNotEmpty(dto.getDeviceIds()), DeviceInfo::getDeviceId, dto.getDeviceIds())
                .eq(Emptys.check(dto.getMercId()), DeviceInfo::getMercId, dto.getMercId())
        );
        return R.ok(copy(DeviceInfoDto.Vo.class, list));
    }

    @Override
    @ApiOperation("通用集合查询")
    public R<List<DeviceInfoDto.Vo>> listCommon(DeviceInfoDto.ListCommon dto) {
        String deviceSearch = dto.getDeviceSearch();
        DeviceInfoDto.Vo vo = dto.getVo();
        QueryWrapper<DeviceInfo> queryWrapper = new MybatisPlusQuery().eqWrapper(vo == null ? new DeviceInfoDto.Vo() : vo, DeviceInfo.class).buildQW();
//        List<Long> placeLineIds = dto.getPlaceLineIds();
        List<Long> deviceIds = dto.getDeviceIds();
        List<Long> mercIds = dto.getMercIds();
        List<String> columnList = dto.getColumnList();
//        if (CollUtil.isNotEmpty(placeLineIds)) {
//            queryWrapper.in(LambdaUtils.getUnderlineCaseName(DeviceInfo::getPlaceLineId), placeLineIds);
//        }

        // fixed
        if (StrUtil.isNotEmpty(deviceSearch)) {
            queryWrapper.and(wrapper -> wrapper.likeRight(LambdaUtils.getUnderlineCaseName(DeviceInfo::getDeviceName), deviceSearch).or()
                    .eq(LambdaUtils.getUnderlineCaseName(DeviceInfo::getDeviceId), deviceSearch));
        }


        if (CollUtil.isNotEmpty(columnList)) {
            queryWrapper.select(columnList.stream().toArray(String[]::new));
        }
        if (CollUtil.isNotEmpty(deviceIds)) {
            queryWrapper.in(LambdaUtils.getUnderlineCaseName(DeviceInfo::getDeviceId), deviceIds);
        }
        if (CollUtil.isNotEmpty(mercIds)) {
            queryWrapper.in(LambdaUtils.getUnderlineCaseName(DeviceInfo::getMercId), mercIds);
        }
        return R.ok(copy(DeviceInfoDto.Vo.class, list(queryWrapper)));
    }

    @Override
    @ApiOperation("商户设备算法列表")
    public R<List<Long>> mercAlgorithmIdList(DeviceInfoDto.MercAlgorithmIdListDto dto) {
        String algorithmIdStr = LambdaUtils.getProperty(DeviceInfo::getAlgorithmId);
        String mercStr = StringTools.humpToLine(LambdaUtils.getProperty(DeviceInfo::getMercId));
        QueryWrapper<DeviceInfo> lqw = new QueryWrapper<DeviceInfo>()
                .isNotNull(StringTools.humpToLine(algorithmIdStr))
                .eq(mercStr, dto.getMercId())
                .select(String.format("DISTINCT (%s) as %s", StringTools.humpToLine(algorithmIdStr), algorithmIdStr));
        List<Long> list = listObjs(lqw, (MapperFunction<Object, Long>) o -> (Long) o);
        return R.ok(list);
    }

    @Override
    @ApiOperation("小程序-商户设备首页统计")
    public R<DeviceInfoDto.MercHomeStatisticalVO> mercHomeStatistical(DeviceInfoDto.MercHomeQueryDTO dto) {
        Long mercId = dto.getMercId();
        List<Long> myDeviceIds = dto.getMyDeviceIds();
        // 初始化数据
        DeviceInfoDto.MercHomeStatisticalVO mercHomeStatisticalVO = new DeviceInfoDto.MercHomeStatisticalVO()
                .setClosedNum(0).setOfflineNum(0)
                .setOnlineNum(0).setOperatingNum(0).setNeedToFillNum(0);
        if (CollUtil.isEmpty(myDeviceIds)) {
            return R.ok(mercHomeStatisticalVO);
        }

        List<DeviceInfo> mercDevices = listByIds(myDeviceIds);
        if (CollUtil.isEmpty(mercDevices)) {
            return R.ok(mercHomeStatisticalVO);
        }


        // 在线、离线
        List<DeviceStatus> deviceStatuses = deviceStatusService.listByIds(myDeviceIds);
        // 分组统计
        Map<Integer, Long> countNetstateMap = deviceStatuses.stream().collect(Collectors
                .groupingBy(DeviceStatus::getNetState, Collectors.counting()));
        Integer onlineDictValue = SysDictUtils.getValue(EnumDeviceOnlineStatus.Code.CODE.getCode(), EnumDeviceOnlineStatus.CONNECTED.getCode(), Integer.class);
        Integer offlineDictValue = SysDictUtils.getValue(EnumDeviceOnlineStatus.Code.CODE.getCode(), EnumDeviceOnlineStatus.DISCONNECT.getCode(), Integer.class);

        int onlineNum = countNetstateMap.get(onlineDictValue) == null ? 0 : countNetstateMap.get(onlineDictValue).intValue();
        int offlineNum = countNetstateMap.get(offlineDictValue) == null ? 0 : countNetstateMap.get(offlineDictValue).intValue();
        mercHomeStatisticalVO.setOnlineNum(onlineNum);
        mercHomeStatisticalVO.setOfflineNum(offlineNum);

        // 锁机、未锁机
//        Map<Integer, Long> countLockLstateMap = deviceStatuses.stream().collect(Collectors
//                .groupingBy(DeviceStatus::getLockStateL, Collectors.counting()));
//        Long lockLStateNum = countLockLstateMap.get(DeviceLockState.LOCK.getCode());
//        Long unLockLStateNum = countLockLstateMap.get(DeviceLockState.UN_LOCK.getCode());
        Map<Integer, Long> countLockLstateMap = mercDevices.stream().collect(Collectors
                .groupingBy(DeviceInfo::getBusyState, Collectors.counting()));
        // 运营
        Long operatingNum = countLockLstateMap.get(DeviceBusySateType.OPERATING.getCode());
        // 停运
        Long suspendedNum = countLockLstateMap.get(DeviceBusySateType.SUSPENDED.getCode());
        mercHomeStatisticalVO.setOperatingNum(operatingNum == null ? 0 : operatingNum.intValue());
        mercHomeStatisticalVO.setClosedNum(suspendedNum == null ? 0 : suspendedNum.intValue());
        // 待补货
        Integer deviceNum = R.feignCheckData(goodsDeviceService.countOutOfStockDevice(new GoodsDeviceDto.CountOutOfStockDevice().setMercId(mercId).setDeviceIds(myDeviceIds)));
        mercHomeStatisticalVO.setNeedToFillNum(deviceNum);
        return R.ok(mercHomeStatisticalVO);
    }

    @Override
    @ApiOperation("小程序-商户设备首页列表")
    public R<List<DeviceInfoDto.MercHomeListVO>> mercHomeList(DeviceInfoDto.MercHomeQueryDTO dto) {
        String searchKey = dto.getSearchKey();
        Long mercId = dto.getMercId();
        String deviceName = dto.getDeviceName();
        Long deviceId = dto.getDeviceId();
        List<Long> searchPlaceIdList = new ArrayList<>();
        // 根据管理员名字查询点位ID列表
        if (Emptys.check(dto.getAdminName())) {
            MercPlaceDto.ListDto placeDto = new MercPlaceDto.ListDto();
            placeDto.setMercId(mercId).setAdminName(dto.getAdminName());
            List<MercPlaceDto.Vo> searchPlaceList = mercPlaceService.list(placeDto).getData();
            if (Emptys.check(searchPlaceList)) {
                searchPlaceIdList = searchPlaceList.stream().map(MercPlaceDto.Vo::getId).distinct().collect(Collectors.toList());
            } else {
                return R.ok();
            }
        }

        boolean isQa = BooleanUtil.isTrue(dto.getIsQaMode());

        List<Long> searchDeviceIds = new ArrayList<>();
        if (StrUtil.isNotEmpty(searchKey) || StrUtil.isNotEmpty(dto.getAdminName())) {
            // 名称或者编号搜索设备
            LambdaQueryWrapper<DeviceInfo> deviceLqw = Wrappers.<DeviceInfo>lambdaQuery()
                    .eq(DeviceInfo::getMercId, mercId)
                    .in(Emptys.check(searchPlaceIdList), DeviceInfo::getPlaceId, searchPlaceIdList);
            if (Emptys.check(searchKey)) {
                deviceLqw.and(wrapper -> wrapper
                        .eq(Emptys.check(searchKey), DeviceInfo::getDeviceId, searchKey)
                        .or()
                        .like(Emptys.check(searchKey), DeviceInfo::getDeviceName, searchKey)
                );
            }

            List<DeviceInfo> list = list(deviceLqw);
            if (CollUtil.isEmpty(list)) {
                return R.ok(new ArrayList<>());
            }
            searchDeviceIds = list.stream().map(DeviceInfo::getDeviceId).collect(Collectors.toList());
        }


        List<Long> myDeviceIds = dto.getDeviceIdList();

        Integer busyStatus = dto.getBusyStatus();
        Integer onlineStatus = dto.getOnlineStatus();
        Integer deviceType = dto.getDeviceType();
        // 条件查询 在线状态，运营状态，设备类型，
        List<Long> deviceIdList = new ArrayList<>();
        if (deviceId != null && myDeviceIds.indexOf(deviceId) > 0) {
            deviceIdList.add(deviceId);
        }

        if (CollUtil.isNotEmpty(myDeviceIds) && deviceId == null) {
            deviceIdList.addAll(myDeviceIds);
        }


        if (CollUtil.isNotEmpty(deviceIdList)) {
            dto.setDeviceIdList(deviceIdList);
            List<Long> queryDeviceIds = new ArrayList<>();
            if (CollUtil.isNotEmpty(searchDeviceIds)) {
                for (Long id : searchDeviceIds) {
                    int index = deviceIdList.indexOf(id);
                    if (index >= 0) {
                        queryDeviceIds.add(id);
                    }
                }
                // 无符合权限的搜索设备，返空
                if (CollUtil.isEmpty(queryDeviceIds)) {
                    return R.ok(new ArrayList<>());
                } else {
                    deviceIdList = queryDeviceIds;
                    dto.setDeviceIdList(queryDeviceIds);
                }
            }

        } else {
            return R.ok(new ArrayList<>());
        }


        if (onlineStatus != null) {
            List<DeviceStatus> deviceStatusList = deviceStatusService.list(Wrappers.<DeviceStatus>lambdaQuery()
                    .eq(DeviceStatus::getNetState, onlineStatus)
                    .in(CollUtil.isNotEmpty(myDeviceIds), DeviceStatus::getDeviceId, deviceIdList)
            );
            if (CollUtil.isNotEmpty(deviceStatusList)) {
                List<Long> dbDeviceIdList = deviceStatusList.stream().map(DeviceStatus::getDeviceId).collect(Collectors.toList());
                deviceIdList = dbDeviceIdList;
            } else {
                return R.ok(new ArrayList<>());
            }
        }

        List<DeviceInfoDto.MercHomeCountVO> list = this.baseMapper.merHomeCountList(dto);
        if (CollUtil.isEmpty(list)) {
            return R.ok(Collections.emptyList());
        }
        List<DeviceInfoDto.MercHomeListVO> dataList = new ArrayList<>(list.size());

        LambdaQueryWrapper<DeviceInfo> lqw = new LambdaQueryWrapper<>();
        // 非质检商户才需要激活
        lqw.eq(!isQa, DeviceInfo::getActiveState, DeviceActiveStateEnum.TRUE.getCode());
        lqw.eq(mercId != null, DeviceInfo::getMercId, mercId);
        lqw.eq(busyStatus != null, DeviceInfo::getBusyState, busyStatus);
        lqw.eq(deviceType != null, DeviceInfo::getDeviceType, deviceType);
        lqw.in(CollUtil.isNotEmpty(deviceIdList), DeviceInfo::getDeviceId, deviceIdList);
        lqw.like(StrUtil.isNotEmpty(deviceName), DeviceInfo::getDeviceName, deviceName).orderByAsc(true, DeviceInfo::getDeviceName, DeviceInfo::getDeviceId);
        List<DeviceInfoDto.Vo> deviceInfoList = copy(DeviceInfoDto.Vo.class, this.list(lqw));

        // 根据点位ID查询管理员名字
        List<Long> placeIdList = deviceInfoList.stream().map(DeviceInfoDto.Vo::getPlaceId).distinct().filter(Objects::nonNull).collect(Collectors.toList());
        List<MercPlaceDto.Vo> mercPlaceList = mercPlaceService.list(new MercPlaceDto.ListDto().setIds(placeIdList)).getData();
        if (Emptys.check(mercPlaceList)) {
            // Map<Long, String> mercPlaceMap = mercPlaceList.stream().collect(Collectors.toMap(i -> i.getId(), i -> i.getAdminName()));
            Map<Long, String> mercPlaceMap = mercPlaceList.stream().collect(HashMap::new, (map, item) -> map.put(item.getId(), item.getAdminName()), HashMap::putAll);
            deviceInfoList.forEach(i -> i.setAdminName(mercPlaceMap.get(i.getPlaceId())));
        }
        // 没有管理员的的设置默认值
        deviceInfoList.stream().filter(s -> s.getAdminName() == null).forEach(s -> s.setAdminName("未分配管理员"));
        List<String> adminNameList = deviceInfoList.stream().map(DeviceInfoDto.Vo::getAdminName).distinct().collect(Collectors.toList());
        List<Long> dIds = deviceInfoList.stream().map(DeviceInfoDto.Vo::getDeviceId).distinct().collect(Collectors.toList());
        List<DeviceSysinfoDto.Vo> deviceSysList = R.feignCheckData(deviceSysinfoService.list(new DeviceSysinfoDto.SelectList().setDeviceIds(dIds)));
        Map<Long, DeviceSysinfoDto.Vo> deviceSysInfoMap = new HashMap<>();
        if (CollUtil.isNotEmpty(deviceSysList)) {
            deviceSysInfoMap = deviceSysList.stream().collect(Collectors.toMap(DeviceSysinfoDto.Vo::getDeviceId, i -> i));
        }

        // 根据管理员名字分组
        Map<String, List<DeviceInfoDto.Vo>> deviceMap = deviceInfoList.stream().collect(Collectors.groupingBy(DeviceInfoDto.Vo::getAdminName));

        DateTime date = DateTime.now();
        DateTime start = DateUtil.beginOfDay(date);
        DateTime end = DateUtil.endOfDay(date);
        for (String adminName : adminNameList) {
            DeviceInfoDto.MercHomeListVO vo = new DeviceInfoDto.MercHomeListVO();
            vo.setAdminName(adminName);
            // 区域下的设备列表
            List<DeviceInfoDto.MercHomeDeviceVo> deviceInfos = BeanUtil.copyToList(deviceMap.get(adminName), DeviceInfoDto.MercHomeDeviceVo.class);
            vo.setDeviceNum(deviceInfos.size());
            if (CollUtil.isEmpty(deviceInfos)) {
                continue;
            }

            // 设备销售统计
            List<Long> deviceIds = deviceInfos.stream().map(DeviceInfoDto.MercHomeDeviceVo::getDeviceId).collect(Collectors.toList());


            // 设备状态查询
            List<DeviceStatusDto.Vo> deviceStatusList = deviceStatusService.list(new DeviceStatusDto.SelectList().setDeviceIds(deviceIds)).getData();
            Map<Long, DeviceStatusDto.Vo> datdeviceStatusMap = new HashMap<>();
            if (CollUtil.isNotEmpty(deviceStatusList)) {
                datdeviceStatusMap = deviceStatusList.stream().collect(Collectors.toMap(DeviceStatusDto.Vo::getDeviceId, d -> d));
            }
            List<GoodsDeviceDto.Vo> goodsDeviceList = R.feignCheckData(goodsService.queryGoodsDeviceInfo(new GoodsDto.QueryGoodsDeviceInfo().setMercId(mercId).setDeviceIds(deviceIds)));
            Map<Long, List<GoodsDeviceDto.Vo>> deviceIdGoodsMap = goodsDeviceList.stream()
                    .collect(Collectors.groupingBy(GoodsDeviceDto.Vo::getDeviceId));
            List<AlgorithmDto.ListNameId> algorithmList = R.feignCheckData(algorithmService.ListNameId());
            Map<Long, String> algorithmListMap = algorithmList.stream().collect(Collectors.toMap(AlgorithmDto.ListNameId::getId, AlgorithmDto.ListNameId::getAlias));

            for (DeviceInfoDto.MercHomeDeviceVo device : deviceInfos) {
                Long dId = device.getDeviceId();
                // 设备类型 反显
                SysDictRedis dictDeviceType = SysDictUtils.get(DictConsts.DEVICE_TYPE, String.valueOf(device.getDeviceType()));
                device.setDeviceTypeName(dictDeviceType.getMsg());
                // 运营状态 反显
                SysDictRedis dictBusyState = SysDictUtils.get(DictConsts.DEVICE_BUSY_STATUS, String.valueOf(device.getBusyState()));
                device.setBusyStateName(dictBusyState.getMsg());

                Integer zero = new Integer(0);

                CountDto.OrderByCreateTimeAndMercId orderByCreateTimeAndMercId = new CountDto.OrderByCreateTimeAndMercId()
                        .setMerdId(mercId).setBeginTime(start).setEndTime(end).setDeviceIds(CollUtil.newArrayList(dId));
                log.info("设备订单统计：{}", JSONUtil.toJsonPrettyStr(orderByCreateTimeAndMercId));
                // 完成订单
                CountDto.SuccessVo successVo = R.feignCheckData(countApiService.orderBySuccess(orderByCreateTimeAndMercId));

                // 今日销售、库存情况 反显
                device.setDayOrderNum(successVo != null ? successVo.getOrdersSize() : zero);
                device.setDaySalesPrice(successVo != null ? successVo.getOrderTotalMoney() : zero);
                DeviceStatusDto.Vo deviceStatus = datdeviceStatusMap.get(device.getDeviceId());
                if (CollUtil.isNotEmpty(deviceIdGoodsMap)) {
                    List<GoodsDeviceDto.Vo> goodsDevice = deviceIdGoodsMap.get(dId);
                    if (CollUtil.isNotEmpty(goodsDevice)) {
                        //过滤商品id为1 和 2的商品
                        goodsDevice = goodsDevice.stream().filter(g -> g.getGoodsId() != 1 && g.getGoodsId() != 2).collect(Collectors.toList());
                        device.setOnSaleNum(goodsDevice.stream().mapToInt(GoodsDeviceDto.Vo::getStock).sum());
                        device.setFillNum(goodsDevice.stream().mapToInt(GoodsDeviceDto.Vo::getFillCount).sum());
                        device.setCapacity(goodsDevice.stream().mapToInt(GoodsDeviceDto.Vo::getCapacity).sum());
                    }
                }
                // 温控仪 反显
                DeviceSysinfoDto.Vo dSysInfo = deviceSysInfoMap.get(dId);
                if (dSysInfo == null) {
                    device.setIsHaveTemp(false);
                } else {
                    device.setIsHaveTemp(BooleanUtil.isTrue(dSysInfo.getIsHaveTemp()));
                }

                // 算法類型
                Long algorithmId = device.getAlgorithmId();
                if (algorithmId != null) {
                    String name = algorithmListMap.get(algorithmId);
                    device.setAlgorithmAlias(name);
                }

                // 联网状态
                Integer netState = deviceStatus == null ? DeviceNetSateType.DISCONNECT.getCode() : deviceStatus.getNetState();
                device.setNetState(netState);
                if (netState == null) {
                    device.setNetStateName(StrUtil.EMPTY);
                } else {
                    device.setNetStateName(DeviceNetSateType.getEnumByCode(netState).getDescription());
                }

                if (deviceStatus != null) {
                    Integer deviceStateL = deviceStatus.getLockStateL();
                    Integer deviceStateR = deviceStatus.getLockStateR();
                    device.setTempValue(deviceStatus.getTempValue());
                    device.setDeviceStateL(deviceStateL);
                    device.setDeviceStateR(deviceStateR);
                    Boolean isUseBattery = deviceStatus.getIsUseBattery();
                    if (BooleanUtil.isTrue(isUseBattery)) {
                        // 使用电池。即断电状态
                        device.setSysPower(2);
                    } else {
                        device.setSysPower(1);
                    }

                    DeviceLockState deviceLockStateL = DeviceLockState.getEnumByCode(deviceStateL);
                    DeviceLockState deviceLockStateR = DeviceLockState.getEnumByCode(deviceStateR);
                    device.setDeviceStateRName(deviceLockStateR == null ? "未知" : deviceLockStateR.getDescription());
                    device.setDeviceStateLName(deviceLockStateL == null ? "未知" : deviceLockStateL.getDescription());
                }


            }

            // 名称排序
            if (CollUtil.isNotEmpty(deviceInfos)) {
                deviceInfos = ListUtil.sortByProperty(deviceInfos, LambdaUtils.getProperty(DeviceInfoDto.MercHomeDeviceVo::getDeviceName));
                deviceInfos = ListUtil.sortByProperty(deviceInfos, LambdaUtils.getProperty(DeviceInfoDto.MercHomeDeviceVo::getDeviceId));
            }
            vo.setDeviceInfos(deviceInfos);

            dataList.add(vo);
        }
        DeviceInfoDto.MercHomeListVO orderTemp = null;
        for (int i = 0; i < dataList.size(); i++) {
            if ("未分配管理员".equals(dataList.get(i).getAdminName())) {
                orderTemp = dataList.get(i);
                dataList.remove(i);
                dataList.add(orderTemp);
                break;
            }
        }
        return R.ok(dataList);
    }

    /**
     * 获取商户设备列表
     *
     * @param mercId
     * @return
     */
    private List<DeviceInfo> getDevicesByMercId(Long mercId) {
        return list(Wrappers.<DeviceInfo>lambdaQuery().eq(DeviceInfo::getMercId, mercId).eq(DeviceInfo::getActiveState, DeviceActiveStateEnum.TRUE.getCode()));
    }

    @Override
    @ApiOperation("通用商户设备搜索组件")
    public R<PageBean<DeviceInfoDto.SimpleDeviceSearchPageVo>> simpleDeviceSearchPage(@RequestBody DeviceInfoDto.SimpleDeviceSearchPageDto dto) {
        LambdaQueryWrapper<DeviceInfo> lqw = new LambdaQueryWrapper<DeviceInfo>()
                .select(DeviceInfo::getDeviceId, DeviceInfo::getDeviceName)
                .and(Emptys.check(dto.getSearchKey()), wrapper -> {
                    wrapper.like(DeviceInfo::getDeviceId, dto.getSearchKey())
                            .or()
                            .like(DeviceInfo::getDeviceName, dto.getSearchKey());
                })
                .in(Emptys.check(dto.getMyDeviceIds()), DeviceInfo::getDeviceId, dto.getMyDeviceIds())
                .eq(DeviceInfo::getActiveState, DeviceActiveStateEnum.TRUE.getCode())
                .eq(Emptys.check(dto.getMercId()), DeviceInfo::getMercId, dto.getMercId());
        IPage page = page(toIPage(dto.getPage()), lqw);
        PageBean<DeviceInfoDto.SimpleDeviceSearchPageVo> simpleDeviceSearchPageVoPageBean = toPageBean(DeviceInfoDto.SimpleDeviceSearchPageVo.class, page);
        return R.ok(simpleDeviceSearchPageVoPageBean);
    }

    /**
     * 小程序商户设备搜索
     *
     * @param page
     * @return
     */
    @Override
    public R<PageBean<DeviceInfoDto.MerHomeSearchVO>> mercDeviceSearchPage(@RequestBody DeviceInfoDto.Page page) {
        PageBean<DeviceInfoDto.MerHomeSearchVO> pageData = new PageBean<>();
        // 小程序独有查询字段 缺货状态：stockStatus ，是否查故障设备：fault
        Boolean fault = page.getFault();
        Long mercId = page.getMercId();
        List<Long> myDeviceIds = page.getMyDeviceIds();
        if (CollUtil.isEmpty(myDeviceIds)) {
            // 无设备
            return R.ok(pageData);
        }
        if (BooleanUtil.isTrue(fault)) {
            // 查询故障设备
            List<DeviceEventMsg> deviceEventMsgs = deviceEventMsgService.list(Wrappers.<DeviceEventMsg>lambdaQuery()
                    .eq(mercId != null, DeviceEventMsg::getMercId, page.getMercId())
                    .in(DeviceEventMsg::getDeviceId, myDeviceIds));
            List<Long> deviceIdList = deviceEventMsgs.stream().map(DeviceEventMsg::getDeviceId).collect(Collectors.toList());
            if (CollUtil.isEmpty(deviceIdList)) {
                return R.ok(new PageBean<>());
            }
            page.setDeviceIdList(deviceIdList);
        } else {
            page.setDeviceIdList(myDeviceIds);
        }

        PageBean<DeviceInfoDto.Vo2> pageBean = queryPage(page);
        List<DeviceInfoDto.Vo2> records = pageBean.getRecords();
        if (CollUtil.isEmpty(records)) {
            return R.ok(pageData);
        }
        List<DeviceInfoDto.MerHomeSearchVO> merHomeSearchRecords = new ArrayList<>();

        BeanUtil.copyProperties(pageBean, pageData);
        if (CollUtil.isNotEmpty(records)) {
            List<Long> deviceIds = records.stream().map(DeviceInfoDto.Vo2::getDeviceId).collect(Collectors.toList());
            String type = EnumDeviceDataType.DAY.getCode();
            String todayDate = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
            // 查询当天
            DeviceDataDto.ListDTO dto = new DeviceDataDto.ListDTO()
                    .setDeviceIds(deviceIds).setType(type).setDateValue(Integer.valueOf(todayDate)).setMercId(mercId);
            List<DeviceDataDto.Vo> deviceDataList = deviceDataService.list(dto);
            Map<Long, DeviceDataDto.Vo> dataMap = MapUtil.newHashMap();
            // 统计数据反显
            if (CollUtil.isNotEmpty(deviceDataList)) {
                dataMap = deviceDataList.stream().collect(Collectors.toMap(DeviceDataDto.Vo::getDeviceId, d -> d));
            }
            for (DeviceInfoDto.Vo2 v : records) {
                DeviceInfoDto.MerHomeSearchVO merHomeSearchVO = new DeviceInfoDto.MerHomeSearchVO();
                Long deviceId = v.getDeviceId();
                DeviceDataDto.Vo vo = dataMap.get(deviceId);
                if (vo != null) {
                    // 今日订单数
                    v.setDayOrderNum(vo != null ? vo.getSalesCount() : 0);
                    v.setDaySalesPrice(vo != null ? vo.getSalesMoney() : 0);
                    BeanUtil.copyProperties(vo, merHomeSearchVO);
                }
                DeviceSysinfoDto.Vo deviceSysinfo = v.getDeviceSysinfo();
                DeviceStatusDto.Vo deviceStatus = v.getDeviceStatus();
                merHomeSearchVO.setAppUpmVersion(deviceSysinfo.getAppUpmVersion());
                merHomeSearchVO.setTempValue(deviceStatus.getTempValue());
                merHomeSearchVO.setNetDbm(deviceStatus.getNetDbm());
                Integer deviceStateL = deviceStatus.getLockStateL();
                Integer deviceStateR = deviceStatus.getLockStateR();
                merHomeSearchVO.setDeviceStateL(deviceStateL);
                merHomeSearchVO.setDeviceStateR(deviceStateR);
                DeviceLockState deviceLockStateL = DeviceLockState.getEnumByCode(deviceStateL);
                DeviceLockState deviceLockStateR = DeviceLockState.getEnumByCode(deviceStateR);
                merHomeSearchVO.setDeviceStateRName(deviceLockStateR == null ? "未知" : deviceLockStateR.getDescription());
                merHomeSearchVO.setDeviceStateLName(deviceLockStateL == null ? "未知" : deviceLockStateL.getDescription());
                merHomeSearchRecords.add(merHomeSearchVO);
            }
            pageData.setRecords(merHomeSearchRecords);
        }
        return R.ok(pageData);
    }

    /**
     * 小程序商户设备详情-数据统计
     *
     * @param dto
     * @return
     */
    @Override
    @ApiOperation("商户设备详情-数据统计")
    public R<DeviceInfoDto.DeviceDataCountVO> dataCount(DeviceInfoDto.DeviceDataCountDTO dto) {
// 统计类型（1=经营数据，2=经营图表，3=温度图表，4=信号图表，5=商品管理）
        Integer type = dto.getType();
        switch (type) {
            case 1:
                // 经营数据
                return R.ok(dataCount1(dto));
            case 2:
                // 经营图表
                return R.ok(dataCount2(dto));
            case 3:
                // 温度图表
                return R.ok(dataCount3(dto));
            case 4:
                // 信号图表
                return R.ok(dataCount4(dto));
            case 5:
                // 商品管理
                return R.ok(dataCount5(dto));
            default:
                break;
        }
        return R.ok();
    }

    /**
     * 经营数据
     *
     * @param dto
     * @return
     */
    private DeviceInfoDto.DeviceDataCountVO dataCount1(DeviceInfoDto.DeviceDataCountDTO dto) {
        Long deviceId = dto.getDeviceId();
        Long mercId = dto.getMercId();
        DeviceInfoDto.DeviceDataCountVO vo = new DeviceInfoDto.DeviceDataCountVO();
        // 当天


        DateTime date = DateTime.now();
        DateTime start = DateUtil.beginOfDay(date);
        DateTime end = DateUtil.endOfDay(date);
        List<Long> deviceIds = CollUtil.newArrayList(deviceId);
        CountDto.OrderByCreateTimeAndMercId orderByCreateTimeAndMercId = new CountDto.OrderByCreateTimeAndMercId()
                .setMerdId(mercId).setBeginTime(start).setEndTime(end).setDeviceIds(deviceIds);

        // 完成订单
        CountDto.SuccessVo successVo = R.feignCheckData(countApiService.orderBySuccess(orderByCreateTimeAndMercId));

        DeviceDataDto.Vo dayData = new DeviceDataDto.Vo();
        if (successVo != null) {
            dayData.setSalesMoney(successVo.getOrderTotalMoney());
            dayData.setRefundMoney(successVo.getRefundMoney());
            dayData.setSalesCount(successVo.getOrdersSize());
            dayData.setProfit(successVo.getProfit());
        }


        // 当月
        DeviceDataDto.Vo monthData = new DeviceDataDto.Vo();

        // 月度统计
        DateTime startM = DateUtil.beginOfMonth(date);
        DateTime endM = DateUtil.endOfMonth(date);

        CountDto.OrderByCreateTimeAndMercId monthOrder = new CountDto.OrderByCreateTimeAndMercId()
                .setMerdId(mercId).setBeginTime(startM).setEndTime(endM).setDeviceIds(deviceIds);
        // 完成订单
        CountDto.SuccessVo successVoM = R.feignCheckData(countApiService.orderBySuccess(monthOrder));


        if (monthData != null) {
            monthData.setRefundMoney(successVoM.getRefundMoney());
            monthData.setSalesMoney(successVoM.getOrderTotalMoney());
            monthData.setSalesCount(successVoM.getOrdersSize());
            monthData.setProfit(successVoM.getProfit());
        }
        vo.setDayBusinessData(copy(DeviceInfoDto.BusinessData.class, dayData));
        vo.setMonthBusinessData(copy(DeviceInfoDto.BusinessData.class, monthData));
        return vo;
    }

    /**
     * 经营图表
     *
     * @param dto
     * @return
     */
    private DeviceInfoDto.DeviceDataCountVO dataCount2(DeviceInfoDto.DeviceDataCountDTO dto) {
        Long deviceId = dto.getDeviceId();
        // 近一个月 （销售额，订单数，退款金额，退款数）
        DeviceInfoDto.DeviceDataCountVO vo = new DeviceInfoDto.DeviceDataCountVO();
        DeviceInfoDto.BusinessChart businessChart = new DeviceInfoDto.BusinessChart();
        List<String> categories = DataTime.dayListByLastDay(30);
        List<Integer> dateList = new ArrayList<>(30);
        businessChart.setCategories(categories);
        categories.forEach(d -> {
            dateList.add(Integer.valueOf(d.replaceAll(StrUtil.DASHED, StrUtil.EMPTY)));
        });
        Integer startDay = dateList.get(0);
        Integer endDay = dateList.get(categories.size() - 1);


        List<DeviceDataDto.Vo> listByDay = deviceDataService.getListByDay(deviceId, startDay, endDay);
        if (CollUtil.isEmpty(listByDay)) {
            return vo;
        }

        // 每天的数据
        Map<Integer, DeviceDataDto.Vo> dataDayMap = listByDay.stream().collect(Collectors.toMap(DeviceDataDto.Vo::getDateValue, i -> i));

        List<DeviceInfoDto.MyChartSeries3> series = new ArrayList<>();

        String[] names = {"销售额", "订单数", "退款金额", "退款数"};
        for (int i = 0; i < names.length; i++) {
            DeviceInfoDto.MyChartSeries3 myChartSeries = new DeviceInfoDto.MyChartSeries3();
            // 某个类型每天的数据
            List<String> data = new ArrayList<>();
            if (i == 0) {
                // 销售额
                dateList.forEach(d -> {
                    // 每日数据填充
                    DeviceDataDto.Vo deviceData = dataDayMap.get(d);

                    if (deviceData == null) {
                        data.add(String.valueOf(BigDecimal.ZERO));
                    } else {
                        Integer salesMoney = deviceData.getSalesMoney() == null ? 0 : deviceData.getSalesMoney();
                        BigDecimal day = BigDecimal.valueOf(salesMoney).divide(BigDecimal.valueOf(100));
                        data.add(String.valueOf(day));
                    }
                });
            } else if (i == 1) {
                // 订单数
                dateList.forEach(d -> {
                    // 每日数据填充
                    DeviceDataDto.Vo deviceData = dataDayMap.get(d);
                    String value = "0";
                    if (deviceData == null) {
                        data.add(value);
                    } else {
                        data.add(deviceData == null ? value : String.valueOf(deviceData.getSalesCount()));
                    }


                });
            } else if (i == 2) {
                // 退款金额
                dateList.forEach(d -> {
                    // 每日数据填充
                    DeviceDataDto.Vo deviceData = dataDayMap.get(d);
                    if (deviceData == null) {
                        data.add(String.valueOf(BigDecimal.ZERO));
                    } else {
                        Integer refundMoney = deviceData.getRefundMoney() == null ? 0 : deviceData.getRefundMoney();
                        BigDecimal day = BigDecimal.valueOf(refundMoney).divide(BigDecimal.valueOf(100));
                        data.add(String.valueOf(day));
                    }

                });
            } else if (i == 3) {
                // 退款数
                dateList.forEach(d -> {
                    // 每日数据填充
                    DeviceDataDto.Vo deviceData = dataDayMap.get(d);
                    if (deviceData == null) {
                        data.add("0");
                    } else {
                        data.add(deviceData == null ? "0" : String.valueOf(deviceData.getRefundCount()));
                    }

                });
            }
            myChartSeries.setName(names[i]);
            myChartSeries.setData(data);
            series.add(myChartSeries);
        }
        businessChart.setSeries(series);
        vo.setBusinessChart(businessChart);
        return vo;
    }

    /**
     * 温度图表
     *
     * @param dto
     * @return
     */
    private DeviceInfoDto.DeviceDataCountVO dataCount3(DeviceInfoDto.DeviceDataCountDTO dto) {
        DeviceInfoDto.DeviceDataCountVO vo = new DeviceInfoDto.DeviceDataCountVO();
        Long deviceId = dto.getDeviceId();
        String choosDate = dto.getChoosDate();
        if (StrUtil.isEmpty(choosDate)) {
            choosDate = DateUtil.formatDate(new Date());
        }
        String startTime = choosDate + " 00:00:00";
        String endTime = choosDate + " 23:59:59";
        // 查询选定日期的温度数据
        List<DeviceTempRecords> deviceTempRecords = deviceTempRecordsService.list(Wrappers.<DeviceTempRecords>lambdaQuery()
                .eq(DeviceTempRecords::getDeviceId, deviceId).between(DeviceTempRecords::getCreateTime, startTime, endTime)
                .orderBy(true, true, DeviceTempRecords::getCreateTime));
        if (CollUtil.isEmpty(deviceTempRecords)) {
            return vo;
        }

        DeviceInfoDto.TemperatureChart temperatureChart = new DeviceInfoDto.TemperatureChart();
        List<String> categories = new ArrayList<>();


        String name = "温度";
        List<DeviceInfoDto.MyChartSeries2> series = new ArrayList<>();
        List<BigDecimal> data = new ArrayList<>();
        deviceTempRecords.forEach(d -> {
            Integer tempValue = d.getTempValue();
            LocalDateTime createTime = d.getCreateTime();
            String time = DateUtil.format(createTime, "HH:mm:ss");
            categories.add(time);
            data.add(tempValue == null ? null : NumberUtil.toBigDecimal(tempValue));
        });
        DeviceInfoDto.MyChartSeries2 myChartSeries = new DeviceInfoDto.MyChartSeries2();
        myChartSeries.setName(name);
        myChartSeries.setData(data);
        series.add(myChartSeries);
        temperatureChart.setSeries(series);
        temperatureChart.setCategories(categories);
        vo.setTemperatureChart(temperatureChart);
        return vo;
    }

    /**
     * 信号图表
     *
     * @param dto
     * @return
     */
    private DeviceInfoDto.DeviceDataCountVO dataCount4(DeviceInfoDto.DeviceDataCountDTO dto) {
        DeviceInfoDto.DeviceDataCountVO vo = new DeviceInfoDto.DeviceDataCountVO();
        Long deviceId = dto.getDeviceId();
        String choosDate = dto.getChoosDate();
        if (StrUtil.isEmpty(choosDate)) {
            choosDate = DateUtil.formatDate(new Date());
        }
        String startTime = choosDate + " 00:00:00";
        String endTime = choosDate + " 23:59:59";
        // 查询选定日期的温度数据
        List<DeviceNetRecord> deviceNetRecords = deviceNetRecordService.list(Wrappers.<DeviceNetRecord>lambdaQuery()
                .eq(DeviceNetRecord::getDeviceId, deviceId).between(DeviceNetRecord::getCreateTime, startTime, endTime)
                .orderBy(true, true, DeviceNetRecord::getCreateTime));
        if (CollUtil.isEmpty(deviceNetRecords)) {
            return vo;
        }

        DeviceInfoDto.SignalChart signalChart = new DeviceInfoDto.SignalChart();
        List<String> categories = new ArrayList<>();


        String name = "信号";
        List<DeviceInfoDto.MyChartSeries> series = new ArrayList<>();
        List<Integer> data = new ArrayList<>();
        deviceNetRecords.forEach(d -> {
            Integer value = d.getSimDbm();
            LocalDateTime createTime = d.getCreateTime();
            String time = DateUtil.format(createTime, "HH:mm:ss");
            categories.add(time);
            data.add(value);

        });
        DeviceInfoDto.MyChartSeries myChartSeries = new DeviceInfoDto.MyChartSeries();
        myChartSeries.setName(name);
        myChartSeries.setData(data);
        series.add(myChartSeries);
        signalChart.setSeries(series);
        signalChart.setCategories(categories);
        vo.setSignalChart(signalChart);
        return vo;
    }

    /**
     * 商品管理
     *
     * @param dto
     * @return
     */
    private DeviceInfoDto.DeviceDataCountVO dataCount5(DeviceInfoDto.DeviceDataCountDTO dto) {
        Long deviceId = dto.getDeviceId();
        DeviceInfoDto.DeviceDataCountVO vo = new DeviceInfoDto.DeviceDataCountVO();
        DeviceInfoDto.GoodsData goodsData = new DeviceInfoDto.GoodsData();
        // 在售商品种类
        // 根据设备ID查商品id
        GoodsDeviceDto.SelectList selectList = new GoodsDeviceDto.SelectList();
        selectList.setDeviceIds(CollUtil.newArrayList(deviceId));
        selectList.setMercId(dto.getMercId());
        R<List<GoodsDeviceDto.Vo>> r = R.feignCheck(goodsDeviceService.list(selectList));
        List<GoodsDeviceDto.Vo> goodsDeviceList = r.getData();
        Integer stock = 0;
        Integer afterFillStock = 0;
        if (CollUtil.isNotEmpty(goodsDeviceList)) {
            //goodsDeviceList 过滤商品id=1和商品id=2的商品
            goodsDeviceList = goodsDeviceList.stream().filter(g -> g.getGoodsId() != 1 && g.getGoodsId() != 2).collect(Collectors.toList());
            stock = goodsDeviceList.stream().mapToInt(GoodsDeviceDto.Vo::getStock).sum();
            afterFillStock = goodsDeviceList.stream().mapToInt(GoodsDeviceDto.Vo::getFillCount).sum();
        }
        goodsData.setAfterFillStock(afterFillStock);
        goodsData.setStock(stock);
        goodsData.setCategoryNum(CollUtil.isNotEmpty(goodsDeviceList) ? goodsDeviceList.size() : new Integer(0));
        vo.setGoodsData(goodsData);
        return vo;
    }


    /**
     * 设备统计PC端
     *
     * @param page
     * @return
     */
    public DeviceInfoDto.PageCount pageCountForPc(DeviceInfoDto.Page page) {
        DeviceInfoDto.PageCount count = new DeviceInfoDto.PageCount().setActiveNum(0).setOffLineNum(0).setTotal(0).setOnlineNum(0).setNoActiveNum(0);

        List<Long> myDeviceIds = page.getMyDeviceIds();
        if (CollUtil.isEmpty(myDeviceIds)) {
            return count;
        }
        List<DeviceStatus> deviceStatuses = deviceStatusService.listByIds(page.getMyDeviceIds());
        //分组统计
        Map<Integer, Long> countNetstateMap = deviceStatuses.stream().collect(Collectors
                .groupingBy(DeviceStatus::getNetState, Collectors.counting()));
        Integer onlineDictValue = SysDictUtils.getValue(EnumDeviceOnlineStatus.Code.CODE.getCode(), EnumDeviceOnlineStatus.CONNECTED.getCode(), Integer.class);
        Integer offlineDictValue = SysDictUtils.getValue(EnumDeviceOnlineStatus.Code.CODE.getCode(), EnumDeviceOnlineStatus.DISCONNECT.getCode(), Integer.class);

        int onlineNum = countNetstateMap.get(onlineDictValue) == null ? 0 : countNetstateMap.get(onlineDictValue).intValue();
        int offlineNum = countNetstateMap.get(offlineDictValue) == null ? 0 : countNetstateMap.get(offlineDictValue).intValue();
        count.setOnlineNum(onlineNum);
        count.setOffLineNum(offlineNum);
        List<DeviceInfo> mercDevices = listByIds(page.getMyDeviceIds());
        int total = mercDevices.size();
        //待激活数量
        Long unActiveNum = mercDevices.stream()
                .filter(device -> device.getActiveState() == 2)
                .count();
        count.setNoActiveNum(unActiveNum.intValue());
        count.setActiveNum(total - unActiveNum.intValue());
        count.setTotal(total);
        return count;
    }

    public PageBean<DeviceInfoDto.Vo2> queryPage(DeviceInfoDto.Page page) {
        IPage<DeviceInfoQueryPage> iPage = baseMapper.queryPage(toIPage(page.getPage()), page);
        PageBean<DeviceInfoDto.Vo2> pageBean = toPageBean(DeviceInfoDto.Vo2.class, iPage);
        List<DeviceInfoDto.Vo2> records = pageBean.getRecords();
        JMap<Long, List<DevicePart>> cover;
        if (Emptys.check(records)) {
            JMap<Long, DeviceInfoQueryPage> queryPageJMap = new JArrayList<>(iPage.getRecords()).toMap(DeviceInfoQueryPage::getDeviceId).cover();
            records.forEach(vo2 -> {
                DeviceInfoQueryPage deviceInfoQueryPage = queryPageJMap.get(vo2.getDeviceId());
                DeviceSysinfoDto.Vo deviceSysinfo = deviceInfoQueryPage.getDeviceSysinfo();
                vo2.setSimIsp(deviceSysinfo.getSimIsp()).setSimIccid(deviceSysinfo.getSimIccid());
            });
            List<DevicePart> deviceParts = devicePartService.list(new LambdaQueryWrapper<DevicePart>()
                    .in(DevicePart::getDeviceId, new JArrayList<>(records).getProperty(DeviceInfoDto.Vo2::getDeviceId))
                    .orderByDesc(DevicePart::getCode)
            );
            if (Emptys.check(deviceParts)) {
                cover = new JArrayList<>(deviceParts).toMap(DevicePart::getDeviceId).group();
                records.forEach(vo2 -> {
                    List<DevicePart> devicePartss = cover.get(vo2.getDeviceId());
                    if (Emptys.check(devicePartss)) {
                        vo2.setDeviceParts(copy(DevicePartDto.Vo.class, devicePartss));
                    }
                });
            }
            List<Long> placeIds = records.stream().filter(s -> s.getPlaceId() != null).map(DeviceInfoDto.Vo2::getPlaceId).distinct().collect(Collectors.toList());

            List<MercPlaceDto.Vo> merPlaceList = R.feignCheckData(mercPlaceService.list(new MercPlaceDto.ListDto().setIds(placeIds)));
            if (Emptys.check(merPlaceList)) {
                Map<Long, MercPlaceDto.Vo> placeMap = merPlaceList.stream().collect(Collectors.toMap(MercPlaceDto.Vo::getId, p -> p));

                for (DeviceInfoDto.Vo2 record : records) {
                    Long placeId = record.getPlaceId();
                    if (placeId == null) {
                        continue;
                    }
                    // 点位 区域 反显
                    MercPlaceDto.Vo placeVo = placeMap.get(placeId);
                    if (placeVo != null) {
                        record.setDistrictName(placeVo.getRegionName());
                        record.setPlaceImgs(placeVo.getImgs());
//                        record.setPlaceName(placeVo.getPlaceName());
                    }
                }
                pageBean.setRecords(records);
            }
        }
        return pageBean;
    }

    private <T> void check(T value, T value2, String msg) {
        if (value.toString().equals(value2.toString())) {
            throw new CommRuntimeException(msg);
        }
    }

    @Override
    @ApiOperation("商户正常设备列表")
    public R<List<DeviceInfoDto.MercNormalListVo>> mercNormalList(@RequestBody @Validated DeviceInfoDto.MercNormalListDto dto) {
        LambdaQueryWrapper<DeviceInfo> lqw = new LambdaQueryWrapper<DeviceInfo>()
                .select(DeviceInfo::getDeviceId, DeviceInfo::getDeviceName)
                .eq(DeviceInfo::getMercId, dto.getMercId())
                .eq(DeviceInfo::getActiveState, DeviceActiveStateEnum.TRUE.getCode())
                .eq(DeviceInfo::getBusyState, EnumDeviceBusyStatus.N_1.getCode())
                .eq(DeviceInfo::getShowStatus, true)
                .eq(DeviceInfo::getFreezeStatus, EnumDeviceFreezeStatus.N_1.getCode());
        List<DeviceInfo> list = list(lqw);
        if (!Emptys.check(list)) {
            return R.ok(new ArrayList<>());
        }
        List<DeviceInfoDto.MercNormalListVo> copy = copy(DeviceInfoDto.MercNormalListVo.class, list);
        return R.ok(copy);
    }

    @ApiOperation("检查设备是否开启重力配置")
    @Override
    public R<Boolean> checkWeightOpen(DevicePartDto.CheckWeightOpenDTO dto) {
        return R.ok(devicePartService.checkWeightOpen(dto));
    }

    @PostMapping("nfcActive")
    @ApiOperation("nfc设备激活")
    public R<Boolean> nfcActive(@RequestBody @Validated ActiveDeviceDTO dto) {
        String terminalId = dto.getTerminalId();
        DeviceInfo deviceInfo = this.getById(Long.valueOf(terminalId));
        if (deviceInfo == null) {
            throw new CommRuntimeException("设备不存在!");
        }
        String deviceName = deviceInfo.getDeviceName();
        if (StrUtil.isEmpty(deviceName)) {
            deviceName = terminalId;
        }
        dto.setDeviceName(deviceName);

        Boolean b = R.feignCheckData(alipayDeviceService.nfcDeviceActive(dto));
        if (BooleanUtil.isFalse(b)) {
            throw new RuntimeException("NFC设备激活失败!");
        }
        deviceInfo.setActiveState(Integer.valueOf(DeviceActiveStateEnum.TRUE.getCode()));
        deviceInfo.setActiveTime(LocalDateTime.now());
        updateById(deviceInfo);
        return R.ok(true);
    }
}
