package com.xy.service;

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
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.dto.CommandMqtt;
import com.xy.dto.DeviceAnnualFeeDto;
import com.xy.dto.MqttUserDto;
import com.xy.dto.be.MercDto;
import com.xy.entity.*;
import com.xy.error.CommRuntimeException;
import com.xy.mapper.DeviceAnnualFeeMapper;
import com.xy.service.be.MercService;
import com.xy.utils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
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 org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.*;

import static com.xy.utils.PlusBeans.toIPage;
import static com.xy.utils.PlusBeans.toPageBean;


/**
 * <p>
 * 设备年费表 服务实现类
 * </p>
 *
 * @author lijin
 * @since 2025-02-28
 */
@Slf4j
@Service
@AllArgsConstructor(onConstructor_ = @Lazy)
@Api(tags = "设备年费表")
public class DeviceAnnualFeeServiceImpl extends ServiceImpl<DeviceAnnualFeeMapper, DeviceAnnualFee> implements DeviceAnnualFeeService {

    private DeviceCreateIdsServiceImpl deviceCreateIdsService;

    private DeviceRegisterServiceImpl deviceRegisterService;

    private DeviceInfoServiceImpl deviceInfoService;

    private DeviceSysinfoServiceImpl deviceSysinfoService;

    private DeviceStatusServiceImpl deviceStatusService;

    private DevicePartServiceImpl devicePartService;

    private MqttUserServiceImpl mqttUserService;

    private MercService mercService;

    private MqttService mqttService;

    @PostMapping("check")
    @ApiOperation("检查")
    public R<String> check(@RequestBody @Validated DeviceAnnualFeeDto.Check check) {
        log.info("独立部署检查：{}，{}，运行环境：{}", check.getDeviceId(), check.getSn(), check.getRunPlatform());
        Long deviceId = check.getDeviceId();
        String sn = check.getSn();
        if (!Emptys.check(deviceId) && !Emptys.check(sn)) {
            throw new CommRuntimeException("deviceId和sn必须传入一个");
        }
        if (!Emptys.check(deviceId)) {
            DeviceSysinfo deviceSysinfo = deviceSysinfoService.getOne(new LambdaQueryWrapper<DeviceSysinfo>()
                    .eq(DeviceSysinfo::getDeviceSn, sn)
            );
            if (deviceSysinfo == null) {
                throw new CommRuntimeException("sn未查找到设备");
            }
            deviceId = deviceSysinfo.getDeviceId();
        }
        DeviceAnnualFee deviceAnnualFee = getOne(new LambdaQueryWrapper<DeviceAnnualFee>()
                .eq(DeviceAnnualFee::getDeviceId, deviceId)
                .eq(DeviceAnnualFee::getIsThis, true)
        );
        if (deviceAnnualFee == null) {
            return R.fail("设备未分配到年费商家");
        }
        deviceAnnualFee.setRunPlatform(check.getRunPlatform());
        updateById(deviceAnnualFee);
        String timeout = DataTime.toString(deviceAnnualFee.getTimeout());
        int i = DataTime.stringContrast(timeout, DataTime.getSring());
        if (i < 0) {
            return R.fail("已欠费");
        }
        return R.ok(timeout);
    }

    @PostMapping("page")
    @ApiOperation("分页查询")
    public R<PageBean<DeviceAnnualFeeDto.Vo>> page(@RequestBody DeviceAnnualFeeDto.Page page) {
        LambdaQueryWrapper<DeviceAnnualFee> lambdaQueryWrapper = new MybatisPlusQuery()
                .eqWrapper(page, DeviceAnnualFee.class)
                .ge(DeviceAnnualFee::getTimeout, page.getBeginTime())
                .le(DeviceAnnualFee::getTimeout, page.getEndTime())
                .build();
        IPage<DeviceAnnualFee> iPage = page(toIPage(page.getPage()), lambdaQueryWrapper);
        PageBean<DeviceAnnualFeeDto.Vo> pageBean = toPageBean(DeviceAnnualFeeDto.Vo.class, iPage);
        List<DeviceAnnualFeeDto.Vo> records = pageBean.getRecords();
        if (Emptys.check(records)) {
            List<MercDto.Vo> mercs = mercService.list(new MercDto.SelectList().setMercIds(new JArrayList<>(records).getProperty(DeviceAnnualFeeDto.Vo::getMercId).comparing())).getData();
            JMap<Long, MercDto.Vo> cover = new JArrayList<>(mercs).toMap(MercDto.Vo::getId).cover();
            Map<String, SysDictRedis> stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee");
            for (DeviceAnnualFeeDto.Vo record : records) {
                MercDto.Vo merc = cover.get(record.getMercId());
                SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(record.getMercId()));
                JSONObject jsonObject = JSONUtil.parseObj(sysDictRedis.getValue());
                record.setMoney(jsonObject.getInt("money"))
                        .setMercName(merc.getName());
            }
        }
        return R.ok(pageBean);
    }

    @PostMapping("mercPage")
    @ApiOperation("商家分页查询（无token）")
    public R<PageBean<DeviceAnnualFeeDto.Vo>> mercPage(@RequestBody DeviceAnnualFeeDto.Page page) {
        if (!Emptys.check(page.getCode())) {
            return R.fail("code不能为空");
        }
        Map<String, SysDictRedis> stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee");
        SysDictRedis sysDictRedis = stringSysDictRedisMap.get(page.getCode());
        if (sysDictRedis == null) {
            return R.fail("code未配置商家");
        }
        page.setMercId(Long.valueOf(sysDictRedis.getValue())).setRegisterStatus(true);
        return page(page);
    }

    @SneakyThrows
    @ApiOperation("导出年费模版")
    @PostMapping("download")
    public void download(HttpServletResponse response) {
        InputStream inputStream = IoUtils.inputStream("device_annual_fee_templet.xlsx").get();
        response.setHeader("Content-Disposition", "attachment; filename=" + "device_annual_fee_templet.xlsx");
        response.setContentType("application/xlsx");
        byte[] buffer = new byte[1024];
        int bytesRead;
        OutputStream outputStream = response.getOutputStream();
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        inputStream.close();
        outputStream.close();
    }

    @SneakyThrows
    @ApiOperation("导入年费数据")
    @PostMapping("upload")
    public R upload(@RequestParam("file") MultipartFile file) {
        EasyExcel.read(file.getInputStream(), UploadInfo.class, new UploadListener(this)).sheet().doRead();
        return R.ok();
    }

    @PostMapping("sendDeviceInfo")
    @ApiOperation("推送设备到商家平台（无token）")
    public R sendDeviceInfo(@RequestBody @Validated DeviceAnnualFeeDto.SendDeviceInfo sendDeviceInfo) {
        List<Long> deviceIds = sendDeviceInfo.getDeviceIds();
        //设备年费信息
        List<DeviceAnnualFee> deviceAnnualFees = list(new LambdaQueryWrapper<DeviceAnnualFee>()
                .in(DeviceAnnualFee::getDeviceId, deviceIds)
                .eq(DeviceAnnualFee::getIsThis, true)
        );
        JMap<Long, DeviceAnnualFee> deviceAnnualFeesJMaps = new JArrayList<>(deviceAnnualFees).toMap(DeviceAnnualFee::getDeviceId).cover();
        //设备id创建信息
        List<DeviceCreateIds> deviceCreateIdss = deviceCreateIdsService.listByIds(deviceIds);
        JMap<Long, DeviceCreateIds> deviceCreateIdsJMaps = new JArrayList<>(deviceCreateIdss).toMap(DeviceCreateIds::getDeviceId).cover();
        //设备注册信息
        List<DeviceRegister> deviceRegisters = deviceRegisterService.listByIds(deviceIds);
        JMap<Long, DeviceRegister> deviceRegistersJMaps = new JArrayList<>(deviceRegisters).toMap(DeviceRegister::getDeviceId).cover();
        //设备信息
        List<DeviceInfo> deviceInfos = deviceInfoService.listByIds(deviceIds);
        JMap<Long, DeviceInfo> deviceInfosJMaps = new JArrayList<>(deviceInfos).toMap(DeviceInfo::getDeviceId).cover();
        //设备系统信息
        List<DeviceSysinfo> deviceSysinfos = deviceSysinfoService.listByIds(deviceIds);
        JMap<Long, DeviceSysinfo> deviceSysinfosJMaps = new JArrayList<>(deviceSysinfos).toMap(DeviceSysinfo::getDeviceId).cover();
        //设备状态信息
        List<DeviceStatus> deviceStatuses = deviceStatusService.listByIds(deviceIds);
        JMap<Long, DeviceStatus> deviceStatusesJMaps = new JArrayList<>(deviceStatuses).toMap(DeviceStatus::getDeviceId).cover();
        //设备配件信息
        List<DevicePart> devicePartss = devicePartService.list(new LambdaQueryWrapper<DevicePart>()
                .in(DevicePart::getDeviceId, deviceIds)
        );
        JMap<Long, List<DevicePart>> devicePartsJMaps = new JHashMap<>();
        if (Emptys.check(devicePartss)) {
            devicePartsJMaps = new JArrayList<>(devicePartss).toMap(DevicePart::getDeviceId).group();
        }
        JMap<Long, List<DevicePart>> finalDevicePartsJMaps = devicePartsJMaps;
        //mqtt连接信息
        List<MqttUser> mqttUsers = mqttUserService.list(new LambdaQueryWrapper<MqttUser>()
                .in(MqttUser::getDeviceId, deviceIds)
        );
        JMap<Long, MqttUser> mqttUsersJMaps = new JArrayList<>(mqttUsers).toMap(MqttUser::getDeviceId).cover();
        //字典信息
        Map<String, SysDictRedis> stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee");
        //组装请求对象
        FunctionUtils.NoParamsResult<Map<Long, JSONObject>> getJsonObjects = () -> {
            Map<Long, JSONObject> jsonObjects = new HashMap<>();
            for (Long deviceId : deviceIds) {
                JSONObject jsonObject = new JSONObject()
                        .set("deviceId", deviceId);
                //设备id创建信息
                DeviceCreateIds deviceCreateIds = deviceCreateIdsJMaps.get(deviceId)
                        .setDeviceQrMercId(null);
                JSONObject deviceCreateIdsObj = JSONUtil.parseObj(deviceCreateIds)
                        .set("bindTime", DataTime.toString(deviceCreateIds.getBindTime()))
                        .set("createTime", DataTime.toString(deviceCreateIds.getCreateTime()));
                if (deviceCreateIds.getLastExportQrCodeTime() != null) {
                    deviceCreateIdsObj.set("lastExportQrCodeTime", DataTime.toString(deviceCreateIds.getLastExportQrCodeTime()));
                }
                jsonObject.set("deviceCreateIds", deviceCreateIdsObj);
                //设备注册信息
                DeviceRegister deviceRegister = deviceRegistersJMaps.get(deviceId);
                JSONObject deviceRegisterObj = JSONUtil.parseObj(deviceRegister)
                        .set("createTime", DataTime.toString(deviceRegister.getCreateTime()));
                jsonObject.set("deviceRegister", deviceRegisterObj);
                //设备信息
                DeviceInfo deviceInfo = deviceInfosJMaps.get(deviceId);
                SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(deviceInfo.getMercId()));
                JSONObject obj = JSONUtil.parseObj(sysDictRedis.getValue());
                deviceInfo.setMercId(obj.getLong("target_merc_id")).setMercCode(obj.getStr("target_merc_code"));
                JSONObject deviceInfoObj = JSONUtil.parseObj(deviceInfo)
                        .set("createTime", DataTime.toString(deviceInfo.getCreateTime()));
                if (deviceInfo.getUpdateTime() != null) {
                    deviceInfoObj.set("updateTime", DataTime.toString(deviceInfo.getUpdateTime()));
                }
                if (deviceInfo.getActiveTime() != null) {
                    deviceInfoObj.set("activeTime", DataTime.toString(deviceInfo.getActiveTime()));
                }
                jsonObject.set("deviceInfo", deviceInfoObj);
                //设备系统信息
                DeviceSysinfo deviceSysinfo = deviceSysinfosJMaps.get(deviceId);
                JSONObject deviceSysinfoObj = JSONUtil.parseObj(deviceSysinfo);
                if (deviceSysinfo.getCreateTime() != null) {
                    deviceSysinfoObj.set("createTime", DataTime.toString(deviceSysinfo.getCreateTime()));
                }
                jsonObject.set("deviceSysinfo", deviceSysinfoObj);
                //设备状态信息
                DeviceStatus deviceStatus = deviceStatusesJMaps.get(deviceId);
                JSONObject deviceStatusObj = JSONUtil.parseObj(deviceStatus)
                        .set("netState", 2);
                if (deviceStatus.getUpdateTime() != null) {
                    deviceStatusObj.set("updateTime", DataTime.toString(deviceStatus.getUpdateTime()));
                }
                if (deviceStatus.getSysStartTime() != null) {
                    deviceStatusObj.set("sysStartTime", DataTime.toString(deviceStatus.getSysStartTime()));
                }
                if (deviceStatus.getSysCurTime() != null) {
                    deviceStatusObj.set("sysCurTime", DataTime.toString(deviceStatus.getSysCurTime()));
                }
                jsonObject.set("deviceStatus", deviceStatusObj);
                //设备配件信息
                List<DevicePart> deviceParts = finalDevicePartsJMaps.get(deviceId);
                if (Emptys.check(deviceParts)) {
                    List<JSONObject> devicePartObjs = new ArrayList<>(deviceParts.size());
                    for (DevicePart devicePart : deviceParts) {
                        JSONObject devicePartObj = JSONUtil.parseObj(devicePart)
                                .set("createTime", DataTime.toString(devicePart.getCreateTime()));
                        if (devicePart.getUpdateTime() != null) {
                            devicePartObj.set("updateTime", DataTime.toString(devicePart.getUpdateTime()));
                        }
                        devicePartObjs.add(devicePartObj);
                    }
                    jsonObject.set("deviceParts", devicePartObjs);
                }
                //mqtt连接信息
                MqttUser mqttUser = mqttUsersJMaps.get(deviceId);
                JSONObject mqttUserObj = JSONUtil.parseObj(mqttUser)
                        .set("created", DataTime.toString(mqttUser.getCreated()))
                        .set("id", null);
                jsonObject.set("mqttUser", mqttUserObj);
                jsonObjects.put(deviceId, jsonObject);
            }
            return jsonObjects;
        };
        //发送请求
        FunctionUtils.ParamsNoResult<Map<Long, JSONObject>> sendPost = jsonObjects -> {
            LocalDateTime now = LocalDateTime.now();
            Map<String, SysDictRedis> map = SysDictUtils.get("mqtt_cmd_templet_task");
            String value = map.get("env").getValue();
            ThreadPoolUtils.Execute execute = ThreadPoolUtils.excPoll(DeviceThreadPoolConfig.DEVICE_COMMON_POLL, deviceIds.size());
            //多线程逐条发送，发送集合担心参数体过大
            deviceIds.forEach(deviceId -> execute.execute(() -> {
                                DeviceAnnualFee deviceAnnualFee = deviceAnnualFeesJMaps.get(deviceId);
                                if (deviceAnnualFee.getRegisterStatus()) {
                                    return;
                                }
                                SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(deviceAnnualFee.getMercId()));
                                JSONObject obj = JSONUtil.parseObj(sysDictRedis.getValue());
                                String url = obj.getStr("domain");
                                JSONObject jsonObject = jsonObjects.get(deviceId);
                                JSONObject result = send(url + "/device/device-annual-fee/receiveDeviceInfo", jsonObject, "发送设备数据到商家平台");
                                //修改设备年费注册信息
                                if (result.getInt("code") == 200) {
                                    deviceAnnualFee.setRegisterStatus(true)
                                            .setRegisterTime(now)
                                            .setTimeout(now.plusDays(10));
                                    updateById(deviceAnnualFee);
                                    //发送环境切换指令
                                    JSONObject env = JSONUtil.parseObj(value);
                                    JSONObject data = env.getJSONObject("data")
                                            .set("env", obj.getStr("env"));
                                    env.set("data", data);
                                    CommandMqtt commandMqtt = new CommandMqtt()
                                            .setDeviceId(deviceId)
                                            .setTemplet(env);
                                    mqttService.senCommand(Collections.singletonList(commandMqtt));
                                }
                            })
            );
            execute.end();
        };
        Map<Long, JSONObject> jsonObjects = getJsonObjects.run();
        sendPost.run(jsonObjects);
        return R.ok();
    }

    @PostMapping("receiveDeviceInfo")
    @ApiOperation("接收兴元设备数据（无token）")
    @Transactional(rollbackFor = Exception.class)
    public R receiveDeviceInfo(@RequestBody Map<String, Object> map) {
        //设备id创建信息
        FunctionUtils.NoParamsNoResult deviceCreateIdsFun = () -> {
            JSONObject deviceCreateIdsSJson = JSONUtil.parseObj(map.get("deviceCreateIds"));
            DeviceCreateIds deviceCreateIds = deviceCreateIdsSJson.toBean(DeviceCreateIds.class);
            DeviceCreateIds byId = deviceCreateIdsService.getById(deviceCreateIds.getDeviceId());
            if (byId != null) {
                return;
            }
            deviceCreateIdsService.save(deviceCreateIds);
        };
        //设备注册信息
        FunctionUtils.NoParamsNoResult deviceRegisterFun = () -> {
            JSONObject deviceRegisterJson = JSONUtil.parseObj(map.get("deviceRegister"));
            DeviceRegister deviceRegister = deviceRegisterJson.toBean(DeviceRegister.class);
            DeviceRegister byId = deviceRegisterService.getById(deviceRegister.getDeviceId());
            if (byId != null) {
                return;
            }
            deviceRegisterService.save(deviceRegister);
        };
        //设备信息
        FunctionUtils.NoParamsNoResult deviceInfoFun = () -> {
            JSONObject deviceInfoJson = JSONUtil.parseObj(map.get("deviceInfo"));
            DeviceInfo deviceInfo = deviceInfoJson.toBean(DeviceInfo.class);
            DeviceInfo byId = deviceInfoService.getById(deviceInfo.getDeviceId());
            if (byId != null) {
                return;
            }
            MercDto.Vo merc = mercService.obj(new MercDto.Vo().setId(deviceInfo.getMercId())).getData();
            deviceInfo.setMercName(merc.getName());
            deviceInfoService.save(deviceInfo);
        };
        //设备系统信息
        FunctionUtils.NoParamsNoResult deviceSysinfoFun = () -> {
            JSONObject deviceSysinfoJson = JSONUtil.parseObj(map.get("deviceSysinfo"));
            DeviceSysinfo deviceSysinfo = deviceSysinfoJson.toBean(DeviceSysinfo.class);
            DeviceSysinfo byId = deviceSysinfoService.getById(deviceSysinfo.getDeviceId());
            if (byId != null) {
                return;
            }
            deviceSysinfoService.save(deviceSysinfo);
        };
        //设备状态信息
        FunctionUtils.NoParamsNoResult deviceStatusFun = () -> {
            JSONObject deviceStatusJson = JSONUtil.parseObj(map.get("deviceStatus"));
            DeviceStatus deviceStatus = deviceStatusJson.toBean(DeviceStatus.class);
            DeviceStatus byId = deviceStatusService.getById(deviceStatus.getDeviceId());
            if (byId != null) {
                return;
            }
            deviceStatusService.save(deviceStatus);
        };
        //设备配件信息
        FunctionUtils.NoParamsNoResult devicePartsFun = () -> {
            JSONArray array = JSONUtil.parseArray(map.get("deviceParts"));
            List<DevicePart> deviceParts = new ArrayList<>();
            for (Object o : array) {
                JSONObject devicePartsJson = JSONUtil.parseObj(o);
                DevicePart devicePart = devicePartsJson.toBean(DevicePart.class)
                        .setId(YitIdHelper.nextId());
                DevicePart one = devicePartService.getOne(new LambdaQueryWrapper<DevicePart>()
                        .eq(DevicePart::getDeviceId, devicePart.getDeviceId())
                        .eq(DevicePart::getCode, devicePart.getCode())
                );
                if (one == null) {
                    deviceParts.add(devicePart);
                }
            }
            if (Emptys.check(deviceParts)) {
                devicePartService.saveBatch(deviceParts);
            }
        };
        //mqtt连接信息
        FunctionUtils.NoParamsNoResult mqttUserFun = () -> {
            JSONObject mqttUserJson = JSONUtil.parseObj(map.get("mqttUser"));
            MqttUser mqttUser = mqttUserJson.toBean(MqttUser.class);
            MqttUser one = mqttUserService.getOne(new LambdaQueryWrapper<MqttUser>()
                    .eq(MqttUser::getDeviceId, mqttUser.getDeviceId())
                    .eq(MqttUser::getSn, mqttUser.getSn())
            );
            if (one != null) {
                return;
            }
            MqttUserDto.Save mqttUserInfo = Beans.copy(MqttUserDto.Save.class, mqttUser);
            mqttUserService.save(mqttUserInfo);
        };
        long deviceId = Long.parseLong(map.get("deviceId").toString());
        return new LockUtils2().name("receiveDeviceInfo_" + deviceId)
                .success(() -> {
                    deviceCreateIdsFun.run();
                    deviceRegisterFun.run();
                    deviceInfoFun.run();
                    deviceSysinfoFun.run();
                    deviceStatusFun.run();
                    devicePartsFun.run();
                    mqttUserFun.run();
                    return R.ok();
                });
    }

    @PostMapping("evn")
    @ApiOperation("切换环境（无token）")
    public R evn(@RequestBody @Validated DeviceAnnualFeeDto.Evn evn) {
        DeviceInfo deviceInfo = deviceInfoService.getById(evn.getDeviceId());
        Map<String, SysDictRedis> stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee");
        SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(deviceInfo.getMercId()));
        JSONObject obj = JSONUtil.parseObj(sysDictRedis.getValue());
        //切换到商家系统
        if (evn.getType() == 2) {
            Map<String, SysDictRedis> map = SysDictUtils.get("mqtt_cmd_templet_task");
            //发送环境切换指令
            JSONObject env = JSONUtil.parseObj(map.get("env").getValue());
            JSONObject data = env.getJSONObject("data")
                    .set("env", obj.getStr("env"));
            env.set("data", data);
            CommandMqtt commandMqtt = new CommandMqtt()
                    .setDeviceId(evn.getDeviceId())
                    .setTemplet(env);
            mqttService.senCommand(Collections.singletonList(commandMqtt));
        }
        //切换到兴元系统
        if (evn.getType() == 1) {
            String url = obj.getStr("domain");
            HttpUtils.send(url + "/device/device-annual-fee/evnXy", new JSONObject().set("deviceId", deviceInfo.getDeviceId()), "切换到兴元系统");
        }
        return R.ok();
    }

    @PostMapping("evnXy")
    @ApiOperation("切换环境到兴元（无token）")
    public R evnXy(@RequestBody DeviceAnnualFeeDto.Evn evn) {
        //发送环境切换指令
        Map<String, SysDictRedis> map = SysDictUtils.get("mqtt_cmd_templet_task");
        JSONObject env = JSONUtil.parseObj(map.get("env").getValue());
        JSONObject data = env.getJSONObject("data")
                .set("env", "prod");
        env.set("data", data);
        CommandMqtt commandMqtt = new CommandMqtt()
                .setDeviceId(evn.getDeviceId())
                .setTemplet(env);
        mqttService.senCommand(Collections.singletonList(commandMqtt));
        return R.ok();
    }

    private JSONObject send(String url, JSONObject params, String msg) {
        return send(url, params, msg, 5000);
    }

    private JSONObject send(String url, JSONObject params, String msg, int timeout) {
        try {
            String body = params.toString();
            log.info("{}请求参数：{}", msg, body);
            String result = HttpRequest.post(url)
                    .body(body)
                    .timeout(timeout)
                    .execute()
                    .body();
            log.info("{}响应参数：{}", msg, result);
            return JSONUtil.parseObj(result);
        } catch (Exception e) {
            log.error("", e);
            return new JSONObject().set("code", 500);
        }
    }


    @Slf4j
    @RequiredArgsConstructor
    public static class UploadListener implements ReadListener<UploadInfo> {

        private final DeviceAnnualFeeServiceImpl deviceAnnualFeeService;

        private JList<UploadInfo> uploadInfos = new JArrayList<>();

        /**
         * 这个每一条数据解析都会来调用
         *
         * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
         * @param context
         */
        @Override
        public void invoke(UploadInfo data, AnalysisContext context) {
            uploadInfos.add(data);
        }

        /**
         * 所有数据解析完成了 都会来调用
         *
         * @param context
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            if (!Emptys.check(uploadInfos)) {
                throw new RuntimeException("导入不能为空");
            }
            List<DeviceAnnualFee> deviceAnnualFees = deviceAnnualFeeService.list(new LambdaQueryWrapper<DeviceAnnualFee>()
                    .in(DeviceAnnualFee::getDeviceId, uploadInfos.getProperty(UploadInfo::getDeviceId))
                    .eq(DeviceAnnualFee::getIsThis, true)
            );
            if (!Emptys.check(deviceAnnualFees)) {
                throw new RuntimeException("设备不存在");
            }
            JMap<Long, DeviceAnnualFee> cover = new JArrayList<>(deviceAnnualFees).toMap(DeviceAnnualFee::getDeviceId).cover();
            LocalDateTime now = LocalDateTime.now();
            List<DeviceAnnualFee> updates = new ArrayList<>(uploadInfos.size());
            for (UploadInfo uploadInfo : uploadInfos) {
                DeviceAnnualFee deviceAnnualFee = cover.get(uploadInfo.getDeviceId());
                if (deviceAnnualFee == null) {
                    throw new RuntimeException("设备" + uploadInfo.getDeviceId() + "不存在");
                }
                deviceAnnualFee.setTimeout(DataTime.toLocal(uploadInfo.getTimeout() + " 23:59:59"));
                if (deviceAnnualFee.getFirstTime() == null) {
                    deviceAnnualFee.setFirstTime(now);
                }
                updates.add(deviceAnnualFee);
            }
            deviceAnnualFeeService.updateBatchById(updates);
        }
    }

    @Data
    public static class UploadInfo {

        @ExcelProperty(value = "设备ID")
        private Long deviceId;

        @ExcelProperty(value = "到期时间（yyyy-MM-dd）")
        private String timeout;
    }
}