package com.xy.service;

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.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.device.EnumAlgorithmPayConfig;
import com.xy.device.EnumDeviceAlgorithmChargingHistoryStatus;
import com.xy.device.EnumDeviceAlgorithmChargingType;
import com.xy.device.EnumDeviceType;
import com.xy.dto.*;
import com.xy.entity.DeviceAlgorithmCharging;
import com.xy.entity.DeviceAlgorithmChargingHistory;
import com.xy.entity.DeviceInfo;
import com.xy.entity.SysDictRedis;
import com.xy.mapper.DeviceAlgorithmChargingMapper;
import com.xy.utils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

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


/**
 * <p>
 * 设备算法计费表 服务实现类
 * </p>
 *
 * @author lijin
 * @since 2023-06-29
 */
@Slf4j
@Service
@AllArgsConstructor(onConstructor_ = @Lazy)
@Api(tags = "设备算法计费表")
public class DeviceAlgorithmChargingServiceImpl extends ServiceImpl<DeviceAlgorithmChargingMapper, DeviceAlgorithmCharging> implements DeviceAlgorithmChargingService {

    private DeviceAlgorithmChargingHistoryServiceImpl deviceAlgorithmChargingHistoryService;

    private DeviceInfoServiceImpl deviceInfoService;

    private DeviceSysinfoServiceImpl deviceSysinfoService;

    private AlipayDeviceService alipayDeviceService;

    private MercDeviceAlgorithmChargingService mercDeviceAlgorithmChargingService;

    @Override
    @ApiOperation("有效条数统计")
    public R<Integer> validCount(@RequestBody @Validated DeviceAlgorithmChargingDto.ValidCount validCount) {
        Integer time = Integer.valueOf(DataTime.getSring("yyyyMMdd"));
        LambdaQueryWrapper<DeviceAlgorithmCharging> lambdaQueryWrapper = new LambdaQueryWrapper<DeviceAlgorithmCharging>()
                .select(DeviceAlgorithmCharging::getDeviceId)
                .eq(DeviceAlgorithmCharging::getMercId, validCount.getMercId())
                .gt(DeviceAlgorithmCharging::getUnusedSize, 0)
                .le(DeviceAlgorithmCharging::getBeginTime, time)
                .and(deviceAlgorithmChargingLambdaQueryWrapper -> deviceAlgorithmChargingLambdaQueryWrapper
                        .eq(DeviceAlgorithmCharging::getTimeout, -1)
                        .or()
                        .ge(DeviceAlgorithmCharging::getTimeout, time)
                ).groupBy(DeviceAlgorithmCharging::getDeviceId);
        List<DeviceAlgorithmCharging> deviceAlgorithmChargings = list(lambdaQueryWrapper);
        return R.ok(deviceAlgorithmChargings.size());
    }

    @PostMapping("count")
    @ApiOperation("查询条数")
    public R<List<DeviceAlgorithmChargingDto.CountVo>> count(@RequestBody @Validated DeviceAlgorithmChargingDto.Count count) {
        //默认值
        JList<Long> deviceIds = new JArrayList<>(count.getDeviceIds());
        JList<DeviceAlgorithmChargingDto.CountVo> countVos = new JArrayList<>(deviceIds.size());
        deviceIds.forEach(deviceId -> {
            DeviceAlgorithmChargingDto.CountVo countVo = new DeviceAlgorithmChargingDto.CountVo()
                    .setDeviceId(deviceId)
                    .setUnusedCount(0)
                    .setUnusedSize(0)
                    .setMakeSize(0)
                    .setArrearageBalance(0);
            countVos.add(countVo);
        });
        JMap<Long, DeviceAlgorithmChargingDto.CountVo> cover = countVos.toMap(DeviceAlgorithmChargingDto.CountVo::getDeviceId).cover();
        //查询全部
        JList<JList<Long>> partition = deviceIds.partition(100);
        JList<DeviceAlgorithmCharging> deviceAlgorithmChargings = new JArrayList<>();
        partition.forEach(longs -> {
            List<DeviceAlgorithmCharging> list = list(new LambdaQueryWrapper<DeviceAlgorithmCharging>()
                    .select(DeviceAlgorithmCharging::getDeviceId, DeviceAlgorithmCharging::getMakeSize, DeviceAlgorithmCharging::getUnusedSize, DeviceAlgorithmCharging::getTimeout, DeviceAlgorithmCharging::getBeginTime)
                    .in(DeviceAlgorithmCharging::getDeviceId, longs)
                    .eq(DeviceAlgorithmCharging::getMercId, count.getMercId())
            );
            deviceAlgorithmChargings.addAll(list);
        });
        JMap<Long, List<DeviceAlgorithmCharging>> allGroup = deviceAlgorithmChargings.toMap(DeviceAlgorithmCharging::getDeviceId).group();
        //查询未使用
        String thisTime = DataTime.getSring("yyyyMMdd");
        JList<DeviceAlgorithmCharging> unuseds = deviceAlgorithmChargings.filter()
                .gt(DeviceAlgorithmCharging::getUnusedSize, 0)
                .eq(DeviceAlgorithmCharging::getTimeout, -1)
                .le(DeviceAlgorithmCharging::getBeginTime, thisTime)
                .list();
        JList<DeviceAlgorithmCharging> unuseds2 = deviceAlgorithmChargings.filter()
                .gt(DeviceAlgorithmCharging::getUnusedSize, 0)
                .ge(DeviceAlgorithmCharging::getTimeout, thisTime)
                .le(DeviceAlgorithmCharging::getBeginTime, thisTime)
                .list();
        if (Emptys.check(unuseds2)) {
            unuseds.addAll(unuseds2);
        }
        JMap<Long, List<DeviceAlgorithmCharging>> unusedGroup = unuseds.toMap(DeviceAlgorithmCharging::getDeviceId).group();
        //统计设备欠费金额
        List<MercDeviceAlgorithmChargingDto.CountArrearageBalanceVo> countArrearageBalanceVos = mercDeviceAlgorithmChargingService.countArrearageBalance(new MercDeviceAlgorithmChargingDto.CountArrearageBalance()
                .setDeviceIds(deviceIds)
                .setMercId(count.getMercId())
        ).getData();
        JMap<Long, MercDeviceAlgorithmChargingDto.CountArrearageBalanceVo> countArrearageBalanceVosJMaps = new JArrayList<>(countArrearageBalanceVos).toMap(MercDeviceAlgorithmChargingDto.CountArrearageBalanceVo::getDeviceId).cover();
        //封装数据
        cover.forEach((deviceId, countVo) -> {
            //未使用
            List<DeviceAlgorithmCharging> deviceAlgorithmChargings1 = unusedGroup.get(deviceId);
            if (Emptys.check(deviceAlgorithmChargings1)) {
                countVo.setUnusedCount(deviceAlgorithmChargings1.size());
                int unusedSize = 0;
                for (DeviceAlgorithmCharging deviceAlgorithmCharging : deviceAlgorithmChargings1) {
                    unusedSize += deviceAlgorithmCharging.getUnusedSize();
                }
                countVo.setUnusedSize(unusedSize);
            }
            //已使用
            List<DeviceAlgorithmCharging> deviceAlgorithmChargings2 = allGroup.get(deviceId);
            if (Emptys.check(deviceAlgorithmChargings2)) {
                int makeSize = 0;
                for (DeviceAlgorithmCharging deviceAlgorithmCharging : deviceAlgorithmChargings2) {
                    makeSize += deviceAlgorithmCharging.getMakeSize();
                }
                countVo.setMakeSize(makeSize);
            }
            //欠费金额
            MercDeviceAlgorithmChargingDto.CountArrearageBalanceVo countArrearageBalanceVo = countArrearageBalanceVosJMaps.get(deviceId);
            if (Emptys.check(countArrearageBalanceVo)) {
                countVo.setArrearageBalance(countArrearageBalanceVo.getArrearageBalance());
            }
        });
        return R.ok(countVos);
    }

    @PostMapping("page")
    @ApiOperation("分页查询")
    public R<PageBean<DeviceAlgorithmChargingDto.Vo>> page(@RequestBody @Validated DeviceAlgorithmChargingDto.Page page) {
        LambdaQueryWrapper<DeviceAlgorithmCharging> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(page, DeviceAlgorithmCharging.class)
                .build()
                .orderByDesc(DeviceAlgorithmCharging::getTimeout, DeviceAlgorithmCharging::getUnusedSize);
        IPage<DeviceAlgorithmCharging> iPage = page(PlusBeans.toIPage(page.getPage()), lambdaQueryWrapper);
        return R.ok(toPageBean(DeviceAlgorithmChargingDto.Vo.class, iPage));
    }

    @PostMapping("pageCountVo")
    @ApiOperation("分页查询带统计数")
    public R<DeviceAlgorithmChargingDto.PageCountVo> pageCountVo(@RequestBody @Validated DeviceAlgorithmChargingDto.Page page) {
        //查询数据
        PageBean<DeviceAlgorithmChargingDto.Vo> pageBean = page(page).getData();
        //查询统计
        QueryWrapper<DeviceAlgorithmCharging> queryWrapper = new MybatisPlusQuery().eqWrapper(page, DeviceAlgorithmCharging.class)
                .buildQW()
                .select("sum(make_size) makeSize", "sum(unused_size) unusedSize");
        DeviceAlgorithmCharging deviceAlgorithmCharging = getOne(queryWrapper);
        //封装结果
        DeviceAlgorithmChargingDto.PageCountVo pageCountVo = new DeviceAlgorithmChargingDto.PageCountVo()
                .setData(pageBean)
                .setMakeSize(deviceAlgorithmCharging == null ? 0 : deviceAlgorithmCharging.getMakeSize())
                .setUnusedSize(deviceAlgorithmCharging == null ? 0 : deviceAlgorithmCharging.getUnusedSize());
        return R.ok(pageCountVo);
    }

    @PostMapping("check")
    @ApiOperation("检查")
    public R check(@RequestBody @Validated DeviceAlgorithmChargingDto.Check check) {
        int yyyyMMdd = Integer.valueOf(DataTime.getSring("yyyyMMdd"));
        DeviceInfoDto.Vo deviceInfo = deviceInfoService.obj(new DeviceInfoDto.Obj().setDeviceId(check.getDeviceId())).getData();
        List<DeviceAlgorithmCharging> deviceAlgorithmChargings = list(new LambdaQueryWrapper<DeviceAlgorithmCharging>()
                .eq(DeviceAlgorithmCharging::getMercId, deviceInfo.getMercId())
                .eq(DeviceAlgorithmCharging::getDeviceId, deviceInfo.getDeviceId())
                .eq(DeviceAlgorithmCharging::getAlgorithmId, deviceInfo.getAlgorithmId())
                .gt(DeviceAlgorithmCharging::getUnusedSize, 0)
                .and(deviceAlgorithmChargingLambdaQueryWrapper -> deviceAlgorithmChargingLambdaQueryWrapper
                        .ge(DeviceAlgorithmCharging::getTimeout, yyyyMMdd)
                        .or()
                        .eq(DeviceAlgorithmCharging::getTimeout, -1)
                )
                .ge(DeviceAlgorithmCharging::getBeginTime, yyyyMMdd)
        );
        if (!Emptys.check(deviceAlgorithmChargings)) {
            return R.fail(R.Enum.ALGORITHM_CHARGING.getCode(), R.Enum.ALGORITHM_CHARGING.getMsg());
        }
        return R.ok();
    }

    @Override
    @ApiOperation("购买")
    public R pay(DeviceAlgorithmChargingDto.Pay pay) {
        JList<DeviceAlgorithmChargingDto.Pay.DeviceAlgorithmCharging> deviceAlgorithmChargings = new JArrayList<>(pay.getDeviceAlgorithmChargings());
        LocalDateTime now = LocalDateTime.now();
        List<DeviceAlgorithmChargingHistory> deviceAlgorithmChargingHistories = new ArrayList<>(deviceAlgorithmChargings.size());
        deviceAlgorithmChargings.forEach(deviceAlgorithmCharging -> {
            DeviceAlgorithmChargingHistory deviceAlgorithmChargingHistory = new DeviceAlgorithmChargingHistory()
                    .setId(YitIdHelper.nextId())
                    .setMercId(pay.getMercId())
                    .setOrderId(pay.getOrderId())
                    .setDeviceId(deviceAlgorithmCharging.getDeviceId())
                    .setAlgorithmId(deviceAlgorithmCharging.getAlgorithmId())
                    .setChargingSize(deviceAlgorithmCharging.getChargingSize())
                    .setChargingMoney(deviceAlgorithmCharging.getMoney())
                    .setType(deviceAlgorithmCharging.getType())
                    .setName(deviceAlgorithmCharging.getName())
                    .setChargingType(pay.getPayType())
                    .setCreateTime(now)
                    .setUpdateTime(now)
                    .setNote(pay.getNote());
            deviceAlgorithmChargingHistories.add(deviceAlgorithmChargingHistory);
        });
        //添加设备算法计费历史表
        if (Emptys.check(deviceAlgorithmChargingHistories)) {
            deviceAlgorithmChargingHistoryService.saveBatch(deviceAlgorithmChargingHistories);
        }
        return R.ok();
    }

    @Override
    @ApiOperation("购买回调")
    public R payNotice(DeviceAlgorithmChargingDto.PayNotice payNotice) {
        List<DeviceAlgorithmChargingHistory> list = deviceAlgorithmChargingHistoryService.list(new LambdaQueryWrapper<DeviceAlgorithmChargingHistory>().eq(DeviceAlgorithmChargingHistory::getOrderId, payNotice.getOrderId()));
        if (!Emptys.check(list)) {
            return R.ok();
        }
        JList<DeviceAlgorithmChargingHistory> deviceAlgorithmChargingHistories = new JArrayList<>(list);
        JList<DeviceAlgorithmChargingHistory> infs = new JArrayList<>();
        JList<DeviceAlgorithmChargingHistory> gives = new JArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        int yyyyMMdd = Integer.valueOf(DataTime.getSring("yyyyMMdd"));
        //查询设备信息
        List<DeviceInfo> deviceInfos = deviceInfoService.list(new LambdaQueryWrapper<DeviceInfo>().in(DeviceInfo::getDeviceId, deviceAlgorithmChargingHistories.getProperty(DeviceAlgorithmChargingHistory::getDeviceId)));
        JMap<Long, DeviceInfo> deviceInfoJMap = new JArrayList<>(deviceInfos).toMap(DeviceInfo::getDeviceId).cover();
        //查询设备系统信息
        List<DeviceSysinfoDto.Vo> deviceSysinfos = deviceSysinfoService.list(new DeviceSysinfoDto.SelectList().setDeviceIds(deviceAlgorithmChargingHistories.getProperty(DeviceAlgorithmChargingHistory::getDeviceId))).getData();
        JMap<Long, DeviceSysinfoDto.Vo> deviceSysinfosJMaps = new JArrayList<>(deviceSysinfos).toMap(DeviceSysinfoDto.Vo::getDeviceId).cover();
        //获取算法包月字典
        Map<String, SysDictRedis> algorithmMoonConfigMaps = SysDictUtils.get(EnumAlgorithmPayConfig.Code.CODE.getCode());
        //获取类型字典
        Map<String, SysDictRedis> deviceAlgorithmChargingTypeMaps = SysDictUtils.get(EnumDeviceAlgorithmChargingType.Code.CODE.getCode());
        JList<DeviceInfoDto.Update> updateDeviceInfos = new JArrayList<>();
        JList<Tuple.Tuple2<BindDeviceDTO, BindActiveDTO>> ailiActive = new JArrayList<>();
        int aliDeviceType = SysDictUtils.getValue(EnumDeviceType.Code.CODE.getCode(), EnumDeviceType.N_5.getCode(), Integer.class);
        //处理有效期
        deviceAlgorithmChargingHistories.forEach(deviceAlgorithmChargingHistory -> {
            //解冻设备 激活设备
            if (deviceAlgorithmChargingHistory.getChargingType() != 100) {
                DeviceInfoDto.Update updateDeviceInfo = new DeviceInfoDto.Update()
                        .setDeviceId(deviceAlgorithmChargingHistory.getDeviceId());
                updateDeviceInfo.setFreezeStatus(1);
                updateDeviceInfo.setBusyState(1);
                DeviceInfo deviceInfo = deviceInfoJMap.get(deviceAlgorithmChargingHistory.getDeviceId());
                if (deviceInfo.getActiveState() == 2) {
                    updateDeviceInfo.setActiveState(1);
                }
                if (!Emptys.check(deviceInfo.getActiveTime())) {
                    updateDeviceInfo.setActiveTime(now);
                }
                updateDeviceInfos.add(updateDeviceInfo);
            }
            //支付宝设备
            DeviceInfo deviceInfo = deviceInfoJMap.get(deviceAlgorithmChargingHistory.getDeviceId());
            if (deviceInfo.getDeviceType() == aliDeviceType) {
                if (deviceAlgorithmChargingHistory.getChargingType() != 100 && deviceInfo.getActiveState() == 2) {
                    //支付宝绑定
                    DeviceSysinfoDto.Vo deviceSysinfo = deviceSysinfosJMaps.get(deviceAlgorithmChargingHistory.getDeviceId());
                    BindDeviceDTO bindDeviceDTO = new BindDeviceDTO().setTerminalId(String.valueOf(deviceInfo.getDeviceId()))
                            .setBoardSn(deviceSysinfo.getDeviceSn());
                    //支付宝激活
                    BindActiveDTO bindActiveDTO = new BindActiveDTO()
                            .setTerminalId(String.valueOf(deviceInfo.getDeviceId()))
                            .setBoardSn(deviceSysinfo.getDeviceSn())
                            .setDeviceName(StrUtil.isNotEmpty(deviceInfo.getDeviceName()) ? deviceInfo.getDeviceName() : String.valueOf(deviceInfo.getDeviceId()));
                    ailiActive.add(new Tuple.Tuple2<>(bindDeviceDTO, bindActiveDTO));
                }
            }
            Integer type = deviceAlgorithmChargingHistory.getType();
            SysDictRedis deviceAlgorithmChargingType2 = deviceAlgorithmChargingTypeMaps.get(EnumDeviceAlgorithmChargingType.N_2.getCode());
            if (type.equals(Integer.valueOf(deviceAlgorithmChargingType2.getValue()))) {
                infs.add(deviceAlgorithmChargingHistory);
                return;
            }
            SysDictRedis deviceAlgorithmChargingType3 = deviceAlgorithmChargingTypeMaps.get(EnumDeviceAlgorithmChargingType.N_3.getCode());
            if (type.equals(Integer.valueOf(deviceAlgorithmChargingType3.getValue()))) {
                gives.add(deviceAlgorithmChargingHistory);
                return;
            }
            SysDictRedis algorithmMoonConfigDict = algorithmMoonConfigMaps.get(deviceAlgorithmChargingHistory.getAlgorithmId().toString());
            DeviceAlgorithmChargingDto.AlgorithmMoonConfig algorithmMoonConfig = JSONUtil.parseObj(algorithmMoonConfigDict.getValue()).toBean(DeviceAlgorithmChargingDto.AlgorithmMoonConfig.class);
            DeviceAlgorithmChargingDto.AlgorithmMoonConfig.Moon moon = algorithmMoonConfig.getMoon();
            Integer chargingSize = deviceAlgorithmChargingHistory.getChargingSize();
            for (Integer integer = 0; integer < chargingSize; integer++) {
                //查询生效时间最大的算法计费
                LambdaQueryWrapper<DeviceAlgorithmCharging> lambdaQueryWrapper = new LambdaQueryWrapper<DeviceAlgorithmCharging>()
                        .eq(DeviceAlgorithmCharging::getDeviceId, deviceAlgorithmChargingHistory.getDeviceId())
                        .eq(DeviceAlgorithmCharging::getAlgorithmId, deviceAlgorithmChargingHistory.getAlgorithmId())
                        .eq(DeviceAlgorithmCharging::getType, type)
                        .orderByDesc(DeviceAlgorithmCharging::getBeginTime)
                        .last("limit 1");
                DeviceAlgorithmCharging deviceAlgorithmCharging = getOne(lambdaQueryWrapper);
                int beginTime;
                int timeout;
                if (deviceAlgorithmCharging == null) {
                    beginTime = Integer.valueOf(DataTime.toString(deviceAlgorithmChargingHistory.getCreateTime(), "yyyyMMdd"));
                    String stringAround = DataTime.getStringAround(0, 1, 0, 0, 0, 0, DataTime.toString(deviceAlgorithmChargingHistory.getCreateTime()));
                    timeout = Integer.valueOf(DataTime.toString(DataTime.toLocal(stringAround), "yyyyMMdd"));
                } else {
                    LocalDateTime localDateTime = DataTime.toLocal(deviceAlgorithmCharging.getBeginTime() + "01000000", "yyyyMMddHHmmss");
                    String s = DataTime.toString(localDateTime, "yyyy-MM-dd HH:mm:ss");
                    String stringAround = DataTime.getStringAround(0, 1, 0, 0, 0, 0, s);
                    beginTime = Integer.valueOf(DataTime.toString(DataTime.toLocal(stringAround), "yyyyMMdd"));
                    String stringAround1 = DataTime.getStringAround(0, 1, 0, 0, 0, 0, stringAround);
                    timeout = Integer.valueOf(DataTime.toString(DataTime.toLocal(stringAround1), "yyyyMMdd"));
                }
                DeviceAlgorithmCharging deviceAlgorithmChargingInfo = new DeviceAlgorithmCharging()
                        .setMercId(deviceAlgorithmChargingHistory.getMercId())
                        .setDeviceId(deviceAlgorithmChargingHistory.getDeviceId())
                        .setAlgorithmId(deviceAlgorithmChargingHistory.getAlgorithmId())
                        .setUnusedSize(moon.getSize())
                        .setType(type)
                        .setName(deviceAlgorithmChargingHistory.getName())
                        .setTimeout(timeout)
                        .setBeginTime(beginTime)
                        .setCreateTime(now)
                        .setUpdateTime(now);
                save(deviceAlgorithmChargingInfo);
            }
        });
        //处理无期限
        infs.forEach(deviceAlgorithmChargingHistory -> {
            DeviceAlgorithmCharging deviceAlgorithmChargingInfo = new DeviceAlgorithmCharging()
                    .setMercId(deviceAlgorithmChargingHistory.getMercId())
                    .setDeviceId(deviceAlgorithmChargingHistory.getDeviceId())
                    .setAlgorithmId(deviceAlgorithmChargingHistory.getAlgorithmId())
                    .setUnusedSize(deviceAlgorithmChargingHistory.getChargingSize())
                    .setType(deviceAlgorithmChargingHistory.getType())
                    .setName(deviceAlgorithmChargingHistory.getName())
                    .setTimeout(-1)
                    .setBeginTime(yyyyMMdd)
                    .setCreateTime(now)
                    .setUpdateTime(now);
            save(deviceAlgorithmChargingInfo);
        });
        //处理赠送
        gives.forEach(deviceAlgorithmChargingHistory -> {
            DeviceAlgorithmCharging deviceAlgorithmChargingInfo = new DeviceAlgorithmCharging()
                    .setMercId(deviceAlgorithmChargingHistory.getMercId())
                    .setDeviceId(deviceAlgorithmChargingHistory.getDeviceId())
                    .setAlgorithmId(deviceAlgorithmChargingHistory.getAlgorithmId())
                    .setUnusedSize(deviceAlgorithmChargingHistory.getChargingSize())
                    .setType(deviceAlgorithmChargingHistory.getType())
                    .setName(deviceAlgorithmChargingHistory.getName())
                    .setTimeout(payNotice.getTimeout())
                    .setBeginTime(yyyyMMdd)
                    .setCreateTime(now)
                    .setUpdateTime(now);
            save(deviceAlgorithmChargingInfo);
        });
        //修改设备算法计费历史
        if (Emptys.check(deviceAlgorithmChargingHistories)) {
            Integer status = SysDictUtils.getValue(EnumDeviceAlgorithmChargingHistoryStatus.Code.CODE.getCode(), EnumDeviceAlgorithmChargingHistoryStatus.N_2.getCode(), Integer.class);
            deviceAlgorithmChargingHistories.forEach(deviceAlgorithmChargingHistory -> deviceAlgorithmChargingHistory.setStatus(status));
            deviceAlgorithmChargingHistoryService.updateBatchById(deviceAlgorithmChargingHistories);
        }
        //修改设备
        if (Emptys.check(updateDeviceInfos)) {
            deviceInfoService.updateBatch(updateDeviceInfos);
        }
        //支付宝刷脸柜激活请求
        if (Emptys.check(ailiActive)) {
            ThreadPoolUtils.Execute execute = ThreadPoolUtils.excPoll(DeviceThreadPoolConfig.ALI_DEVICE_ACTIVE, ailiActive.size());
            ailiActive.forEach(bindDeviceDTOBindActiveDTOTuple2 -> execute.execute(() -> {
                //调用支付宝绑定
                R.feignCheckData(alipayDeviceService.deviceBind(bindDeviceDTOBindActiveDTOTuple2.getV1()));
                //调用支付宝激活
                R.feignCheckData(alipayDeviceService.deviceActive(bindDeviceDTOBindActiveDTOTuple2.getV2()));
            }));
        }
        return R.ok();
    }

    @Override
    @ApiOperation("扣费")
    public R<Map<Long, Integer>> charging(List<DeviceAlgorithmChargingDto.Charging> chargings) {
        //封装默认返回值
        Map<Long, Integer> map = new JHashMap<>(chargings.size());
        chargings.forEach(charging -> map.put(charging.getMercDeviceAlgorithmChargingId(), charging.getSize()));
        //查询数据
        List<DeviceAlgorithmCharging> queryList = new ArrayList<>(chargings.size());
        for (DeviceAlgorithmChargingDto.Charging charging : chargings) {
            DeviceAlgorithmCharging deviceAlgorithmCharging = new DeviceAlgorithmCharging()
                    .setMercId(charging.getMercId())
                    .setDeviceId(charging.getDeviceId())
                    .setAlgorithmId(charging.getAlgorithmId())
                    .setBeginTime(charging.getAlgorithmDate());
            queryList.add(deviceAlgorithmCharging);
        }
        List<DeviceAlgorithmCharging> deviceAlgorithmChargingsList = baseMapper.chargingQuery(queryList);
        if (!Emptys.check(deviceAlgorithmChargingsList)) {
            return R.ok(map);
        }
        JList<DeviceAlgorithmCharging> deviceAlgorithmChargings = new JArrayList<>(deviceAlgorithmChargingsList);
        //查询计费类型字典
        Map<String, SysDictRedis> deviceAlgorithmChargingTypeMap = SysDictUtils.get(EnumDeviceAlgorithmChargingType.Code.CODE.getCode());
        SysDictRedis sysDictRedis1 = deviceAlgorithmChargingTypeMap.get(EnumDeviceAlgorithmChargingType.N_1.getCode());
        SysDictRedis sysDictRedis2 = deviceAlgorithmChargingTypeMap.get(EnumDeviceAlgorithmChargingType.N_2.getCode());
        SysDictRedis sysDictRedis3 = deviceAlgorithmChargingTypeMap.get(EnumDeviceAlgorithmChargingType.N_3.getCode());
        //循环扣费
        chargings.forEach(charging -> {
            //应扣数量
            int size = charging.getSize();
            //查找对应算法
            JList<DeviceAlgorithmCharging> deviceAlgorithmChargingJlist = deviceAlgorithmChargings.filter()
                    .eq(DeviceAlgorithmCharging::getDeviceId, charging.getDeviceId())
                    .eq(DeviceAlgorithmCharging::getAlgorithmId, charging.getAlgorithmId())
                    .list();
            if (!Emptys.check(deviceAlgorithmChargingJlist)) {
                return;
            }
            JMap<Integer, List<DeviceAlgorithmCharging>> deviceAlgorithmChargingJMap = deviceAlgorithmChargingJlist.toMap(DeviceAlgorithmCharging::getType).group();
            //递归扣费 扣费顺序为：赠送 -> 有期限 -> 无期限
            int size2 = charging(size, deviceAlgorithmChargingJMap, new JArrayList<SysDictRedis>().set(sysDictRedis3).set(sysDictRedis1).set(sysDictRedis2));
            map.put(charging.getMercDeviceAlgorithmChargingId(), size2);
        });
        return R.ok(map);
    }

    /**
     * 递归扣费
     *
     * @return 未扣条数
     */
    private int charging(int size, JMap<Integer, List<DeviceAlgorithmCharging>> deviceAlgorithmChargingJMap, List<SysDictRedis> sysDictRediss) {
        if (size > 0 && Emptys.check(sysDictRediss)) {
            SysDictRedis sysDictRedis = sysDictRediss.remove(0);
            LocalDateTime now = LocalDateTime.now();
            List<DeviceAlgorithmCharging> deviceAlgorithmChargings = deviceAlgorithmChargingJMap.get(Integer.valueOf(sysDictRedis.getValue()));
            if (Emptys.check(deviceAlgorithmChargings)) {
                for (DeviceAlgorithmCharging deviceAlgorithmCharging : deviceAlgorithmChargings) {
                    int unusedSize = deviceAlgorithmCharging.getUnusedSize();
                    if (unusedSize > 0) {
                        int newUnusedSize = unusedSize - size;
                        int makeSize;
                        //扣除后=负数则算法条数不足
                        if (newUnusedSize < 0) {
                            size = (~(newUnusedSize - 1));
                            newUnusedSize = 0;
                            makeSize = unusedSize;
                        } else {
                            makeSize = size;
                            size = 0;
                        }
                        DeviceAlgorithmCharging updateDeviceAlgorithmCharging = new DeviceAlgorithmCharging()
                                .setId(deviceAlgorithmCharging.getId())
                                .setUnusedSize(newUnusedSize)
                                .setMakeSize(deviceAlgorithmCharging.getMakeSize() + makeSize)
                                .setUpdateTime(now);
                        updateById(updateDeviceAlgorithmCharging);
                        deviceAlgorithmCharging.setUnusedSize(updateDeviceAlgorithmCharging.getUnusedSize())
                                .setMakeSize(updateDeviceAlgorithmCharging.getMakeSize());
                    }
                }
            }
            if (size > 0 && Emptys.check(sysDictRediss)) {
                return charging(size, deviceAlgorithmChargingJMap, sysDictRediss);
            }
        }
        return size;
    }

    /**
     * 解析商户编码，获取上级及自身编码
     *
     * @return
     */
    public void getMercCodes(String mercCode, List<String> mercCodes) {
        mercCodes.add(mercCode);
        int length = mercCode.length();
        if (length == 5) {
            return;
        }
        String parentCode = mercCode.substring(0, length - 3);
        getMercCodes(parentCode, mercCodes);
    }
}