/*
 * Author: wangjian
 * 生成ServiceImpl
 */
package com.tofly.watermeter.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tofly.common.core.entity.ResultRespone;
import com.tofly.common.core.util.Date8Util;
import com.tofly.common.core.util.StringUtil;
import com.tofly.common.core.util.io.ExcelUtil;
import com.tofly.common.oauth.exception.ToflyDeniedException;
import com.tofly.common.oauth.util.SecurityUtils;
import com.tofly.watermeter.config.ComConstans;
import com.tofly.watermeter.custom.dto.ForeignYhQyQdDto;
import com.tofly.watermeter.custom.entity.meterRead.*;
import com.tofly.watermeter.custom.mapper.SynchronousAboutMapper;
import com.tofly.watermeter.custom.service.meterBusiness.export.ExportCommonCodeService;
import com.tofly.watermeter.custom.vo.ForeignSynchroYhQyQdVo;
import com.tofly.watermeter.custom.vo.ForeignYhQyQdVo;
import com.tofly.watermeter.custom.vo.YhQyQdInfoVo;
import com.tofly.watermeter.entity.*;
import com.tofly.watermeter.mapper.YhbingqyMapper;
import com.tofly.watermeter.service.CbqydbmService;
import com.tofly.watermeter.service.QfkhjbxxService;
import com.tofly.watermeter.service.ReadmeterhisService;
import com.tofly.watermeter.service.YhbingqyService;
import com.tofly.watermeter.util.CallAPIUtil;
import com.tofly.watermeter.util.ThreadPoolUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户编号与区域、区域段绑定中间表
 *
 * @author wangjieliu
 * @date Mon Jun 07 00:00:00 CST 2021
 */
@Slf4j
@Service("yhbingqyService")
public class YhbingqyServiceImpl extends ServiceImpl<YhbingqyMapper, Yhbingqy> implements YhbingqyService {
    @Autowired
    private QfkhjbxxService qfkhjbxxService;
    @Autowired
    private ReadmeterhisService readmeterhisService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ExportCommonCodeService exportCommonCodeService;
    @Autowired
    private CbqydbmService cbqydbmService;
    @Autowired
    private SynchronousAboutMapper synchronousAboutMapper;

    @Override
    public IPage<Map> getNoAllocationTwo(Page page, Allocation allocation) {
        String cbb = allocation.getCbb();
        String pqbm = allocation.getPqbm();
        String sbcj = allocation.getSbcj();
        List<String> sbcjList = null;
        List<String> cbbList = null;
        List<String> pqList = null;
        if (StringUtil.isNotEmpty(cbb)) {
            cbbList = Arrays.asList(cbb.split(","));
        }
        if (StringUtil.isNotEmpty(pqbm)) {
            pqList = Arrays.asList(pqbm.split(","));
        }
        if (StringUtil.isNotEmpty(sbcj)) {
            sbcjList = Arrays.asList(sbcj.split(","));
        }
        return baseMapper.getNoAllocationTwo(page, allocation, pqList, cbbList, sbcjList);
    }

    @Override
    public boolean allocation(Allocation allocation) {
        String qydbm = allocation.getQydbm();
        String qybm = allocation.getQybm();
        String fpType = allocation.getFpType();
        //分配是由同一个区域段的
        if (StringUtil.isNull(qydbm)) {
            throw new ToflyDeniedException("请选择区域段");
        } else if (StringUtil.isNull(qybm)) {
            throw new ToflyDeniedException("请选择区域");
        }
        List<String> fpYhbh = null;
        //如果是全部分配；则不用前端传递具体用户编号；使用未分配查询参数；然后再查出来统一绑定
        //全部分配方式
        if ("1".equals(fpType)) {
            String cbb = allocation.getCbb();
            String pqbm = allocation.getPqbm();
            String sbcj = allocation.getSbcj();
            List<String> sbcjList = null;
            List<String> cbbList = null;
            List<String> pqList = null;
            if (StringUtil.isNotEmpty(cbb)) {
                cbbList = Arrays.asList(cbb.split(","));
            }
            if (StringUtil.isNotEmpty(pqbm)) {
                pqList = Arrays.asList(pqbm.split(","));
            }
            if (StringUtil.isNotEmpty(sbcj)) {
                sbcjList = Arrays.asList(sbcj.split(","));
            }
            //得到应该全部分配的用户编号
            fpYhbh = baseMapper.getallFpYh(allocation, pqList, cbbList, sbcjList);
        }
        //选择具体用户进行分配的方式
        else {
            if (StringUtil.isEmpty(allocation.getYhbh())) {
                throw new ToflyDeniedException("请选择分配的用户");
            }
            fpYhbh = Arrays.asList(allocation.getYhbh().split(","));
        }
        if (StringUtil.isNotNull(fpYhbh) && fpYhbh.size() > 0) {
            //如果yhbhList长度大于1000，则无法使用in查询出来，会大于in的最大限制，所以需要进行分段
            int size = fpYhbh.size();
            //向上取整，获得次数
            int count = (int) Math.ceil(Double.valueOf(size) / 1000d);
            //这里循环次数不多，估计就两三次
            for (int i = 0; i < count; i++) {
                //将上月的数据保存到抄表历史数据表中
                int startIndex = i * 1000;
                int endIndex = 1000 + i * 1000;
                List<String> subYhbhList = new ArrayList<>();
                //判断是否截取的最后一次数据，如果是最后一次数据，则不能使用endIndex这个参数去截取了，有可能会存在endIndex参数值超过索引最大值的情况
                if (i < count - 1) {
                    //分段采取数据 0~999 ； 1000~1999 , 2000~2999 .....//这里subList方法无法截取最后一位，所以需要多写一位索引
                    subYhbhList = fpYhbh.subList(startIndex, endIndex);
                } else {
                    //分段采取最后的数据
                    subYhbhList = fpYhbh.subList(startIndex, fpYhbh.size());
                }
                //如果分配的已经分配过了，则不能分配了
                List<Yhbingqy> existYhList = this.list(Wrappers.<Yhbingqy>lambdaQuery().in(Yhbingqy::getYhbh, subYhbhList).select(Yhbingqy::getYhbh));
                if (StringUtil.isNotNull(existYhList) && existYhList.size() > 0) {
                    throw new ToflyDeniedException("以下用户：" + existYhList.stream().map(Yhbingqy::getYhbh)
                            .collect(Collectors.joining(",")) + "已经绑定，不能重复绑定");
                }
            }
            //不能因为重新分配用户，就将原有的抄表序号给打乱了
            //查询已经绑定了的用户最大序号
            Integer cbxh = baseMapper.getMaxCbxh(qydbm);
            if (StringUtil.isNotNull(cbxh)) {
                cbxh = cbxh + 1;
            } else {
                cbxh = 1;
            }
            //对数据进行排序
            List<Yhbingqy> yhbingqyList = new ArrayList<>();
            for (int i = 0; i < fpYhbh.size(); i++) {
                Yhbingqy yhbingqy = new Yhbingqy();
                yhbingqy.setCbxh(cbxh + i);
                yhbingqy.setYhbh(fpYhbh.get(i));
                yhbingqy.setQybm(qybm);
                yhbingqy.setQydbm(qydbm);
                yhbingqy.setUpdateFlag("1");
                yhbingqyList.add(yhbingqy);
            }
            this.saveBatch(yhbingqyList);
        }
        return true;
    }

    /**
     * 分配时加锁
     *
     * @param paramHashCode
     * @return
     */
    private boolean generateBatchNo(String paramHashCode) {

        while (true) {
            //redis找锁并且存放key和值，可以避免大量数据同时请求的时候，因为redis中有，就不会往下走了，当redis中没有这个key才可以进入if这个语句中，后面必须要删除
            if (redisTemplate.opsForValue().setIfAbsent(paramHashCode, UUID.randomUUID().toString(), 500, TimeUnit.MILLISECONDS)) {
//                //redis的自增，为什么会自增呢？同一个datakey，会记录在redis嘛？记录之前已经获取了的自增数？
//                sequence = redisTemplate.opsForValue().increment(datakey, 1);
//                if (sequence == 1) {
//                    redisTemplate.delete(redisTemplate.keys(CommonConstants.MESSAGE_BATCH_SEQUENCE + "_" + "*"));
//                    sequence = redisTemplate.opsForValue().increment(datakey, 1);
//                }
                //这里做删除，那么上面的判断（锁）就会刷新了
                redisTemplate.opsForValue().getOperations().delete(paramHashCode);
                break;
            }

        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean allocationManager(Allocation allocation) {

        //synchronized
        String yhbh = allocation.getYhbh();
        String twoType = allocation.getTwoType();
        if (StringUtil.isEmpty(yhbh)) {
            throw new ToflyDeniedException("请选择用户");
        } else if (StringUtil.isEmpty(twoType)) {
            throw new ToflyDeniedException("请传递操作类型");
        }
        List<String> selectYh = Arrays.asList(yhbh.split(","));
        if (!selectYh.isEmpty()) {
//            Connection connection =
            //获取选择用户的区域段信息(用于修改这些区段的顺序号)，并且删除选择用户的绑定信息
            Set<String> bingQyd = getSelectYhQyd(selectYh);

            //分配操作
            if ("0".equals(twoType)) {
                //目标区段
                String qydbm = allocation.getQydbm();
                String qybm = allocation.getQybm();
                //区域段编码是唯一的
                while (true) {
                    //加锁
                    if (redisTemplate.opsForValue().setIfAbsent(qydbm, UUID.randomUUID().toString(), 300, TimeUnit.MILLISECONDS)) {
                        if (StringUtil.isNull(qydbm)) {
                            throw new ToflyDeniedException("请选择区域段");
                        } else if (StringUtil.isNull(qybm)) {
                            throw new ToflyDeniedException("请选择区域");
                        }
                        bingQyd.add(qydbm);
                        //不能因为重新分配用户，就将原有的抄表序号给打乱了
                        //查询已经绑定了的用户最大序号
                        Integer cbxh = baseMapper.getMaxCbxh(qydbm);
                        if (StringUtil.isNotNull(cbxh)) {
                            cbxh = cbxh + 1;
                        } else {
                            cbxh = 1;
                        }
                        //对数据进行排序
                        List<Yhbingqy> yhbingqyList = new ArrayList<>();
                        for (int i = 0; i < selectYh.size(); i++) {
                            Yhbingqy yhbingqy = new Yhbingqy();
                            yhbingqy.setCbxh(cbxh + i);
                            yhbingqy.setYhbh(selectYh.get(i));
                            yhbingqy.setQybm(qybm);
                            yhbingqy.setQydbm(qydbm);
                            yhbingqy.setUpdateFlag("1");
                            //子顺序号默认为0，用于之后的排序使用
                            yhbingqy.setChildSort(0);
                            yhbingqy.setUpdateUser(SecurityUtils.getUserId());
                            yhbingqy.setUpdateDate(new Date());
                            yhbingqyList.add(yhbingqy);
                        }
                        //在插入前，查询是否已经有当前区段的这些用户已经落库了，如果已经存在了，则不能添加，但是我们这里是连续两次提交，两次进行的数据不是同一事务，上一个事务和下一个事务的数据是相互隔离不受影响的；好像需要设置隔离级别？
                        this.saveBatch(yhbingqyList);
                        //释放锁
                        redisTemplate.opsForValue().getOperations().delete(qydbm);
                        break;
                    }
                }
            } else if (!"1".equals(twoType)) {
                throw new ToflyDeniedException("请正确操作");
            }
            //修改所有涉及到的区段序号
            aginSort(bingQyd);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized boolean orderManager(YhbhAndCbxhList yhbhAndCbxhList) {
        //需要排序的用户信息
        List<Yhbingqy> yhbhAndCbxhs = yhbhAndCbxhList.getYhbhAndCbxhs();
        //标识往前往后
        String moveFlag = yhbhAndCbxhList.getMoveFlag();
        //目标用户；必须是已分配了的；不然没有顺序号
        String targetYh = yhbhAndCbxhList.getTargetYh();
        if (StringUtil.isEmpty(targetYh)) {
            throw new ToflyDeniedException("请选择目标用户");
        } else if (StringUtil.isEmpty(moveFlag)) {
            throw new ToflyDeniedException("请正确传递排序参数");
        } else if (StringUtil.isEmpty(yhbhAndCbxhs)) {
            throw new ToflyDeniedException("请正确传递排序用户");
        }
        //查询目标用户分配信息
        Yhbingqy targetYhbing = this.getOne(Wrappers.<Yhbingqy>lambdaQuery().eq(Yhbingqy::getYhbh, targetYh));
        if (StringUtil.isNotNull(targetYhbing)) {
            //目标用户的顺序号
            Integer targetCbxh = null;
            //区段
            String qydbm = targetYhbing.getQydbm();
            String qybm = targetYhbing.getQybm();
            //区域段编码是唯一的
            while (true) {
                //加锁
                if (redisTemplate.opsForValue().setIfAbsent(qydbm, UUID.randomUUID().toString(), 300, TimeUnit.MILLISECONDS)) {
                    //选择的用户编号
                    List<String> selectYh = yhbhAndCbxhs.stream().map(Yhbingqy::getYhbh).collect(Collectors.toList());
                    //获取选择用户的区域段信息(用于修改这些区段的顺序号)，并且删除选择用户的绑定信息
                    Set<String> bingQyd = getSelectYhQyd(selectYh);
                    bingQyd.add(qydbm);
                    //往后
                    if ("0".equals(moveFlag)) {
                        targetCbxh = targetYhbing.getCbxh();
                    }
                    //往前
                    else if ("1".equals(moveFlag)) {
                        targetCbxh = targetYhbing.getCbxh() - 1;
                    } else {
                        throw new ToflyDeniedException("请正确操作用户排序");
                    }
                    List<Yhbingqy> yhbingqyList = new ArrayList<>();
                    for (int i = 0; i < selectYh.size(); i++) {
                        Yhbingqy yhbingqy = new Yhbingqy();
                        //目前先设置为与目标一样的顺序号，然后根据子顺序号，再做排序调整
                        yhbingqy.setCbxh(targetCbxh);
                        yhbingqy.setYhbh(selectYh.get(i));
                        yhbingqy.setQybm(qybm);
                        yhbingqy.setQydbm(qydbm);
                        yhbingqy.setUpdateFlag("1");
                        //子顺序号
                        yhbingqy.setChildSort(i + 1);
                        yhbingqyList.add(yhbingqy);
                    }
                    this.saveBatch(yhbingqyList);
                    //修改所有涉及到的区段序号
                    aginSort(bingQyd);
                    //释放锁
                    redisTemplate.opsForValue().getOperations().delete(qydbm);
                    break;
                }
            }
        } else {
            throw new ToflyDeniedException("选择的目标用户暂未分配");
        }
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultRespone orderManagerTask(YhbhAndCbxhList yhbhAndCbxhList) {
        //需要排序的用户
        String yhbh = yhbhAndCbxhList.getOriginalYh();
        //标识往前往后
        String moveFlag = yhbhAndCbxhList.getMoveFlag();
        //目标用户；传递了就代表是跳转，没传就是上移下移，前端懒得传参了
        String targetYh = yhbhAndCbxhList.getTargetYh();
        //区段
        String qydbm = yhbhAndCbxhList.getQydbm();
        if (StringUtil.isEmpty(moveFlag)) {
            throw new ToflyDeniedException("请正确传递排序参数");
        } else if (StringUtil.isEmpty(yhbh)) {
            throw new ToflyDeniedException("请正确传递排序用户");
        } else if (StringUtil.isEmpty(qydbm)) {
            throw new ToflyDeniedException("请正确传递区域段编码");
        }
        //加锁，对相同区域段排序的，需要加锁；while循环表示对请求一直去获取锁，获取到了就往下执行，利用区段加锁，因为只能在区段内才能调序
        while (true) {
            //redis找锁并且存放key和值，可以避免大量数据同时请求的时候，因为redis中有，就不会往下走了，当redis中没有这个key才可以进入if这个语句中，后面必须要删除
            if (redisTemplate.opsForValue().setIfAbsent(qydbm, UUID.randomUUID().toString(), 500, TimeUnit.MILLISECONDS)) {
                //只能调当月的任务计划
                List<String> yhbhList = Arrays.asList(yhbh.split(","));
                String monthPlan = Date8Util.getYearMonth();
                //排序的时候因为一个抄表段的数量大部分只有几百个，上千的抄表段很少，并且排序不会一次性选择1000个以上的用户进行排序（yhbhList）
                List<Readmeterhis> readmeterhisList = readmeterhisService.list(Wrappers.<Readmeterhis>lambdaQuery().in(Readmeterhis::getYhbh, yhbhList)
                        .eq(Readmeterhis::getCbqydBm, qydbm).eq(Readmeterhis::getMonthPlan, monthPlan).orderByAsc(Readmeterhis::getCbxh)
                        .select(Readmeterhis::getYhbh, Readmeterhis::getCbxh, Readmeterhis::getSoleSign));
                //必须是对区段内的抄表任务排序
                if (readmeterhisList.size() != yhbhList.size()) {
                    //这里做删除，那么上面的判断（锁）就会刷新了
                    redisTemplate.opsForValue().getOperations().delete(qydbm);
                    throw new ToflyDeniedException("请仔细选择排序用户");
                }
                //查询目标用户信息
                Long targetXh = null;
                //传了目标用户，则是跳转选择的目标用户
                if (StringUtil.isNotEmpty(targetYh)) {
                    Readmeterhis readmeterhis = readmeterhisService.getOne(Wrappers.<Readmeterhis>lambdaQuery()
                            .eq(Readmeterhis::getYhbh, targetYh).eq(Readmeterhis::getMonthPlan, monthPlan).eq(Readmeterhis::getCbqydBm, qydbm)
                            .select(Readmeterhis::getYhbh, Readmeterhis::getCbxh));
                    //始终用与目标顺序号保持一致的思想，利用ChildSort字段进行最后的排序操作
                    if (readmeterhis != null) {
                        targetXh = readmeterhis.getCbxh();
                        //往前
                        if ("1".equals(moveFlag)) {
                            targetXh = readmeterhis.getCbxh() - 1;
                        }
                    }
                }
                //没有传目标用户，则是单个上移下移
                else {
                    targetXh = readmeterhisList.get(0).getCbxh() + 1;
                    //往前
                    if ("1".equals(moveFlag)) {
                        targetXh = readmeterhisList.get(0).getCbxh() - 2;
                    }
                }
                //修改任务子顺序号
                for (int i = 0; i < readmeterhisList.size(); i++) {
                    //目前先设置为与目标一样的顺序号，然后根据子顺序号，再做排序调整
                    readmeterhisList.get(i).setCbxh(targetXh);
                    readmeterhisList.get(i).setChildSort(i + 1);
                }
                //修改childSort
                readmeterhisService.updateChildSort(readmeterhisList);
                //调整抄表计划中的顺序
                readmeterhisService.updateTaskCbxh(qydbm, monthPlan);


                //以下是还需要修改分配绑定表中的顺序（分配表中的顺序下个月制定计划的时候生效）
                //查询需要分配表中需要排序的用户
                List<Yhbingqy> sortFpyhList = this.list(Wrappers.<Yhbingqy>lambdaQuery().in(Yhbingqy::getYhbh, yhbhList).eq(Yhbingqy::getQydbm, qydbm));
                //如果没有数据，则说明该抄表段中，选择的这几个需要排序的用户已经不存在了，说明已经对分配绑定关系进行了更新操作
                if (StringUtil.isNull(sortFpyhList) || sortFpyhList.size() == 0) {
                    //这里做删除，那么上面的判断（锁）就会刷新了
                    redisTemplate.opsForValue().getOperations().delete(qydbm);
                    return ResultRespone.success("成功；已修改抄表任务顺序，未修改分配顺序");
                }
                Integer fpXh = null;
                if (StringUtil.isNotEmpty(targetYh)) {
                    //查询目标用户分配信息  存在修改抄表任务计划的时候，已经修改了分配数据
                    Yhbingqy targetYhbing = this.getOne(Wrappers.<Yhbingqy>lambdaQuery().eq(Yhbingqy::getYhbh, targetYh).eq(Yhbingqy::getQydbm, qydbm));
                    if (StringUtil.isNull(targetYhbing)) {
                        redisTemplate.opsForValue().getOperations().delete(qydbm);
                        return ResultRespone.success("成功；已修改抄表任务顺序，未修改分配顺序");
                    }
                    fpXh = targetYhbing.getCbxh();
                    //往前
                    if ("1".equals(moveFlag)) {
                        fpXh = targetYhbing.getCbxh() - 1;
                    }
                }
                //分配表上移下移情况；//没有传目标用户，则是单个上移下移
                else {
                    fpXh = sortFpyhList.get(0).getCbxh() + 1;
                    //往前
                    if ("1".equals(moveFlag)) {
                        fpXh = sortFpyhList.get(0).getCbxh() - 2;
                    }
                }
                for (int i = 0; i < sortFpyhList.size(); i++) {
                    //目前先设置为与目标一样的顺序号，然后根据子顺序号，再做排序调整
                    sortFpyhList.get(i).setCbxh(fpXh).setChildSort(i + 1).setUpdateFlag("1");
                }
                baseMapper.updateBingAndXh(sortFpyhList);
                Set<String> bingQyd = new HashSet<>();
                bingQyd.add(qydbm);
                //修改所有涉及到的区段序号
                aginSort(bingQyd);
                //这里做删除，那么上面的判断（锁）就会刷新了
                redisTemplate.opsForValue().getOperations().delete(qydbm);
                return ResultRespone.success("排序修改成功");
            }
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public boolean aginSort(Set<String> bingQyd) {
        try {
            for (String qydbm : bingQyd) {
                baseMapper.aginSort(qydbm);
            }
            //测试异常，抛出去后捕获怎么才算回滚
//            int ii = 1 / 0;qydbm
        } catch (Exception e) {
            throw new ToflyDeniedException("排序出现异常:" + e.getMessage());
        }
        return true;
    }

    /**
     * 获取选择用户的区域段信息(用于修改这些区段的顺序号)，并且删除选择用户的绑定信息
     *
     * @param selectYh 选择的用户编号集合
     * @return 区域段集合
     */
    public Set<String>
    getSelectYhQyd(List<String> selectYh) {
        //存放绑定表中需要修改的区段顺序号
        Set<String> bingQyd = new HashSet<>();
        //如果yhbhList长度大于1000，则无法使用in查询出来，会大于in的最大限制，所以需要进行分段
        int size = selectYh.size();
        //向上取整，获得次数
        int count = (int) Math.ceil(Double.valueOf(size) / 1000d);
        //这里循环次数不多，估计就两三次
        for (int i = 0; i < count; i++) {
            //将上月的数据保存到抄表历史数据表中
            int startIndex = i * 1000;
            int endIndex = 1000 + i * 1000;
            List<String> subYhbhList = new ArrayList<>();
            //判断是否截取的最后一次数据，如果是最后一次数据，则不能使用endIndex这个参数去截取了，有可能会存在endIndex参数值超过索引最大值的情况
            if (i < count - 1) {
                //分段采取数据 0~999 ； 1000~1999 , 2000~2999 .....//这里subList方法无法截取最后一位，所以需要多写一位索引
                subYhbhList = selectYh.subList(startIndex, endIndex);
            } else {
                //分段采取最后的数据
                subYhbhList = selectYh.subList(startIndex, selectYh.size());
            }
            List<Yhbingqy> qydList = this.list(Wrappers.<Yhbingqy>lambdaQuery().in(Yhbingqy::getYhbh, subYhbhList).groupBy(Yhbingqy::getQydbm).select(Yhbingqy::getQydbm));
            if (!qydList.isEmpty()) {
                //因为存在已分配的用户重新分配到目标区段，则需要重新排序的区段
                bingQyd.addAll(qydList.stream().map(Yhbingqy::getQydbm).collect(Collectors.toSet()));
                //删除绑定表中已分配了的用户，因为这些用户也会被重新分配到另外的目标区段
                this.remove(Wrappers.<Yhbingqy>lambdaQuery().in(Yhbingqy::getYhbh, subYhbhList));
            }
        }
        return bingQyd;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean undock(Allocation allocation) {
        String qydbm = allocation.getQydbm();
        String fpType = allocation.getFpType();
        String yhbhS = allocation.getYhbh();
        //必须传递区域段编码
        if (StringUtil.isEmpty(qydbm)) {
            throw new ToflyDeniedException("请选择区域段");
        }
        //全部移除方式
        if ("1".equals(fpType)) {
            //全部移除；根据区域段；删除绑定表中的绑定数据
            this.remove(Wrappers.<Yhbingqy>lambdaQuery().eq(Yhbingqy::getQydbm, qydbm));
        }
        //选择用户移除
        else {
            if (StringUtil.isEmpty(yhbhS)) {
                throw new ToflyDeniedException("请选择移除的用户");
            }
            //需要移除的用户
            List<String> undockYhList = Arrays.asList(yhbhS.split(","));
            //查询区域段下分配好的用户，得到不用移除的用户，根据排序好的顺序查询出来；因为移除后需要重新排序
            List<Yhbingqy> yhbingqyList = this.list(Wrappers.<Yhbingqy>lambdaQuery().eq(Yhbingqy::getQydbm, qydbm)
                    .notIn(Yhbingqy::getYhbh, undockYhList)
                    .select(Yhbingqy::getYhbh).orderByAsc(Yhbingqy::getCbxh));
            //对不用移除的用户重新排序
            for (int i = 0; i < yhbingqyList.size(); i++) {
                //设置排序
                yhbingqyList.get(i).setCbxh(1 + i);
            }
            //移除
            boolean flag = this.remove(Wrappers.<Yhbingqy>lambdaQuery().in(Yhbingqy::getYhbh, undockYhList));
            if (flag) {
                if (yhbingqyList.size() > 0) {
                    baseMapper.updateBingAndXh(yhbingqyList);
                }
            }
        }
        return true;
    }

    @Override
    public IPage<Map> allocationPage(Page<Map> page, String yhbh, String qybm, String qydbm, String yhxm, String pqbm, String cbb, String yhdz, String inputcontent, HttpServletResponse response, String export) {
        List<String> cbbList = null;
        List<String> pqList = null;
        String flag = export;
        if (StringUtil.isNotEmpty(cbb)) {
            cbbList = Arrays.asList(cbb.split(","));
        }
        if (StringUtil.isNotEmpty(pqbm)) {
            pqList = Arrays.asList(pqbm.split(","));
        }

        if ("1".equals(flag)) {
            exportCommonCodeService.exportCode(response, "allocationPageService", baseMapper.allocationPage(yhbh, qybm, qydbm, yhxm, pqList, cbbList, yhdz, inputcontent));
        }

        return baseMapper.allocationPage(page, yhbh, qybm, qydbm, yhxm, pqList, cbbList, yhdz, inputcontent);
    }


    @Override
    @SneakyThrows
    public boolean importFileAllcation(MultipartFile file, String qydbm, String qybm) {
        if (StringUtil.isNull(file)) {
            throw new ToflyDeniedException("请上传Excel文件");
        }
        if (StringUtil.isNull(qydbm)) {
            throw new ToflyDeniedException("请传递抄表区域段");
        }
        if (StringUtil.isNull(qybm)) {
            throw new ToflyDeniedException("请传递抄表区域");
        }

        //文件流
        InputStream inputStream = file.getInputStream();
        //实体类与Excel表格式
        ExcelUtil excel = new ExcelUtil(ImportFileYh.class);
        //Excel中的数据导入到实体类
        List<ImportFileYh> listFile = excel.importExcel("基础表", inputStream);
        List<String> yhbhList = listFile.stream().map(ImportFileYh::getYhbh).collect(Collectors.toList());
        //先验证，该用户是否已经绑定了用户区域段
        List<Yhbingqy> yhbingqyList = this.list(Wrappers.<Yhbingqy>lambdaQuery().in(Yhbingqy::getYhbh, yhbhList).select(Yhbingqy::getYhbh));
        if (StringUtil.isNotNull(yhbingqyList) && yhbingqyList.size() > 0) {
            throw new ToflyDeniedException("用户：" + StringUtil.join(yhbingqyList, ",") + "已经绑定了，请仔细检查");
        }
        //数据库有的用户才绑定
        List<Qfkhjbxx> qfkhjbxxList = qfkhjbxxService.list(Wrappers.<Qfkhjbxx>lambdaQuery()
                .in(Qfkhjbxx::getYhbh, yhbhList).select(Qfkhjbxx::getYhbh));
        if (StringUtil.isNotNull(qfkhjbxxList)) {
            //不能因为重新分配用户，就将原有的抄表序号给打乱了
            //查询已经绑定了的用户最大序号
            Yhbingqy maxCbxh = this.getOne(Wrappers.<Yhbingqy>lambdaQuery().eq(Yhbingqy::getQydbm, qydbm)
                    .orderByDesc(Yhbingqy::getCbxh).apply("ROWNUM = 1").select(Yhbingqy::getCbxh));
            Integer cbxh = 1;
            if (StringUtil.isNotNull(maxCbxh)) {
                cbxh = maxCbxh.getCbxh() + 1;
            }
            //对数据进行排序
            List<Yhbingqy> yhbingqyss = new ArrayList<>();
            for (int i = 0; i < qfkhjbxxList.size(); i++) {
                Yhbingqy yhbingqy = new Yhbingqy();
                yhbingqy.setCbxh(cbxh + i);
                yhbingqy.setYhbh(qfkhjbxxList.get(i).getYhbh());
                yhbingqy.setQybm(qybm);
                yhbingqy.setQydbm(qydbm);
                yhbingqyss.add(yhbingqy);
            }
            this.saveBatch(yhbingqyss);
        }
        return true;
    }

    @Override
    public List<AppRead> getCbryXY(ReadSearch search) {
        Date endDate = search.getEndDate();
        Date startDate = search.getStartDate();
        Long cbry = search.getCbry();
        if (StringUtil.isNull(endDate) || StringUtil.isNull(startDate)) {
            throw new ToflyDeniedException("请选择具体时间查询");
        }
        return baseMapper.getCbryXY(cbry, startDate, endDate);
    }

    @Override
    public List<Map> getQyCllect(String inputcontent) {
        return baseMapper.getQyCllect(inputcontent);
    }

    /**
     * 这个排序太拉了，如果后面要优化就用SQL去做
     *
     * @param yhbhAndCbxhList
     * @return
     */
    @Override
    public boolean cbOrder(YhbhAndCbxhList yhbhAndCbxhList) {
        String moveType = yhbhAndCbxhList.getMoveType();
        String isApp = yhbhAndCbxhList.getIsApp();
        //获取当前年月，主要针对APP端修改抄表数据中的抄表顺序
        String yearMonth = Date8Util.getYearMonth();
        //移动类型：单个上下移动
        if ("1".equals(moveType)) {
            //选择的用户
            Long originalXh = yhbhAndCbxhList.getOriginalXh();
            String originalYh = yhbhAndCbxhList.getOriginalYh();
            String qydbm = yhbhAndCbxhList.getQydbm();
            //移动标识 0：往后；1：往前
            String moveFlag = yhbhAndCbxhList.getMoveFlag();
            if (StringUtil.isEmpty(originalYh)) {
                throw new ToflyDeniedException("请选择用户进行排序");
            } else if (StringUtil.isNull(originalXh)) {
                throw new ToflyDeniedException("请传递用户当前序号");
            } else if (StringUtil.isEmpty(qydbm)) {
                throw new ToflyDeniedException("请传入抄表区域段");
            }
            //APP端对抄表数据排序
            if ("1".equals(isApp)) {
                Readmeterhis readmeterhis = readmeterhisService.getOne(Wrappers.<Readmeterhis>lambdaQuery().eq(Readmeterhis::getMonthPlan, yearMonth)
                        .eq("0".equals(moveFlag), Readmeterhis::getCbxh, originalXh + 1).eq("1".equals(moveFlag), Readmeterhis::getCbxh, originalXh - 1)
                        .select(Readmeterhis::getYhbh, Readmeterhis::getCbxh).apply("ROWNUM = 1"));
                //如果是null，则代表是第一位或者最后一位在设置移动
                if (StringUtil.isNotNull(readmeterhis)) {
                    String queryYh = readmeterhis.getYhbh();
                    Long queryXh = readmeterhis.getCbxh();
                    //不论是上移还是下移，只需要将查询出来的序号和当前需要调节的序号互换即可，因为查询出来的序号就已经判断了上移还是下移的
                    readmeterhisService.update(Wrappers.<Readmeterhis>lambdaUpdate()
                            .eq(Readmeterhis::getMonthPlan, yearMonth).eq(Readmeterhis::getYhbh, originalYh).set(Readmeterhis::getCbxh, queryXh)
                            .set(Readmeterhis::getUpdateDate, new Date()).set(Readmeterhis::getUpdateUser, SecurityUtils.getUserId()));
                    readmeterhisService.update(Wrappers.<Readmeterhis>lambdaUpdate()
                            .eq(Readmeterhis::getMonthPlan, yearMonth).eq(Readmeterhis::getYhbh, queryYh).set(Readmeterhis::getCbxh, originalXh)
                            .set(Readmeterhis::getUpdateDate, new Date()).set(Readmeterhis::getUpdateUser, SecurityUtils.getUserId()));
                }
            } else {
                //查找移动到具体哪个目标用户
                Yhbingqy yhbingqy = this.getOne(Wrappers.<Yhbingqy>lambdaQuery().eq(Yhbingqy::getQydbm, qydbm)
                        .eq("0".equals(moveFlag), Yhbingqy::getCbxh, originalXh + 1).eq("1".equals(moveFlag), Yhbingqy::getCbxh, originalXh - 1)
                        .select(Yhbingqy::getYhbh, Yhbingqy::getCbxh));
                //如果是null，则代表是第一位或者最后一位在设置移动
                if (StringUtil.isNotNull(yhbingqy)) {
                    String queryYh = yhbingqy.getYhbh();
                    Integer queryXh = yhbingqy.getCbxh();
                    //不论是上移还是下移，只需要将查询出来的序号和当前需要调节的序号互换即可，因为查询出来的序号就已经判断了上移还是下移的
                    this.update(Wrappers.<Yhbingqy>lambdaUpdate().eq(Yhbingqy::getYhbh, originalYh).set(Yhbingqy::getCbxh, queryXh)
                            .set(Yhbingqy::getUpdateDate, new Date()).set(Yhbingqy::getUpdateUser, SecurityUtils.getUserId()));
                    this.update(Wrappers.<Yhbingqy>lambdaUpdate().eq(Yhbingqy::getYhbh, queryYh).set(Yhbingqy::getCbxh, originalXh)
                            .set(Yhbingqy::getUpdateDate, new Date()).set(Yhbingqy::getUpdateUser, SecurityUtils.getUserId()));
                }
            }

        }
        //跳转排序
        else if ("2".equals(moveType)) {
            //需要排序的用户信息
            List<Yhbingqy> yhbingqyList = yhbhAndCbxhList.getYhbhAndCbxhs();
            //移动标识 0：往后；1：往前
            String moveFlag = yhbhAndCbxhList.getMoveFlag();
            Integer targetXh = yhbhAndCbxhList.getTargetXh();
            String targetYh = yhbhAndCbxhList.getTargetYh();
            //区域段编码
            String qydbm = yhbhAndCbxhList.getQydbm();
            if (StringUtil.isNull(yhbingqyList)) {
                throw new ToflyDeniedException("重新排序用户必传");
            } else if (StringUtil.isNull(targetXh)) {
                throw new ToflyDeniedException("请传入目标抄表序号");
            } else if (StringUtil.isEmpty(targetYh)) {
                throw new ToflyDeniedException("请传入目标用户编号");
            } else if (StringUtil.isEmpty(moveFlag)) {
                throw new ToflyDeniedException("请传入移动状态");
            } else if (StringUtil.isEmpty(qydbm)) {
                throw new ToflyDeniedException("请传递区域段编码");
            }

            //处理数据；对前端传过来需要排序的数据，先根据最开始的顺序排好
            yhbingqyList.sort(Comparator.comparing(Yhbingqy::getCbxh));
            //需要排序的用户编号
            List<String> sortYhbhList = yhbingqyList.stream().map(Yhbingqy::getYhbh).collect(Collectors.toList());

            List<Yhbingqy> noSortYhList = null;
            //查询不排序的用户
            if ("1".equals(isApp)) {
                noSortYhList = readmeterhisService.getNoSortYhList(yearMonth, sortYhbhList, qydbm);
            } else {
                noSortYhList = this.list(Wrappers.<Yhbingqy>lambdaQuery()
                        .eq(Yhbingqy::getQydbm, qydbm).notIn(Yhbingqy::getYhbh, sortYhbhList)
                        .select(Yhbingqy::getYhbh, Yhbingqy::getCbxh).orderByAsc(Yhbingqy::getCbxh));
            }
            if (StringUtil.isNotNull(noSortYhList) && noSortYhList.size() > 0) {
                //找索引
                int indexOf = -1;
                for (int i = 0; i < noSortYhList.size(); i++) {
                    //找到目标用户的索引
                    if (targetYh.equals(noSortYhList.get(i).getYhbh()) && targetXh.equals(noSortYhList.get(i).getCbxh())) {
                        indexOf = i;
                        break;
                    }
                }
                //如果最后索引没有找到
                if (indexOf == -1) {
                    throw new ToflyDeniedException("不存在该目标用户");
                }
                //前段用户集合
                List<Yhbingqy> frontYhList = null;
                //后段用户集合
                List<Yhbingqy> behindYhList = null;
                //存放排序后的结果数据
                List<Yhbingqy> resultYhList = new ArrayList<>();
                //将不排序的数据以该索引分隔开为 2 个list，然后中间再插入我们需要重新排序的数据
                //判断后移还是前移
                if ("0".equals(moveFlag)) {
                    //如果是移动到目标用户后；则需要将目标用户放在前段集合中
                    frontYhList = noSortYhList.subList(0, indexOf + 1);
                    //如果目标用户是最后一个的情况，就不用再截取后段用户了，只是这种排除情况，虽然不报错，但是截取也要花时间
                    if (indexOf + 1 != noSortYhList.size()) {
                        behindYhList = noSortYhList.subList(indexOf + 1, noSortYhList.size());
                    }
                } else if ("1".equals(moveFlag)) {
                    //如果是移动到目标用户前；则需要将目标用户放在后段集合中
                    //如果目标用户在第一位的情况，就不用再截取前段用户了，只是这种排除情况，虽然不报错，但是截取也要花时间
                    if (indexOf != 0) {
                        frontYhList = noSortYhList.subList(0, indexOf);
                    }
                    behindYhList = noSortYhList.subList(indexOf, noSortYhList.size());
                } else {
                    throw new ToflyDeniedException("请正确选择前或者后进行排序");
                }

                //将前段用户、重新排序用户、后段用户集合addAll到最终的排序集合中
                if (StringUtil.isNotNull(frontYhList)) {
                    resultYhList.addAll(frontYhList);
                }
                //需要重新排序的用户集合
                resultYhList.addAll(yhbingqyList);
                //后段用户集合
                if (StringUtil.isNotNull(behindYhList)) {
                    resultYhList.addAll(behindYhList);
                }
                //将对应区域段下面的所有用户重新排序
                for (int i = 0; i < resultYhList.size(); i++) {
                    resultYhList.get(i).setCbxh(1 + i);
                }
                //设置排序
                if ("1".equals(isApp)) {
                    //APP的抄表数据排序
                    readmeterhisService.updateCbxh(resultYhList, yearMonth, qydbm);
                } else {
                    baseMapper.updateBingAndXh(resultYhList);
                }
            }
        } else {
            throw new ToflyDeniedException("请正确执行排序操作");
        }
        //修改所有抄表序号，从1开始 ，只有web端的才更新分配绑定表
        if (!"1".equals(isApp)) {
            actionCbxh(yhbhAndCbxhList.getQydbm());
        }

        return true;
    }

    private void actionCbxh(String qydbm) {
        ThreadPoolUtil.executorService.execute(() -> {
            List<Yhbingqy> yhbingqyList = list(Wrappers.<Yhbingqy>lambdaQuery().eq(Yhbingqy::getQydbm, qydbm)
                    .select(Yhbingqy::getYhbh).orderByAsc(Yhbingqy::getCbxh));
            for (int i = 0; i < yhbingqyList.size(); i++) {
                //设置排序
                yhbingqyList.get(i).setCbxh(1 + i);
            }
            if (yhbingqyList.size() > 0) {
                baseMapper.updateBingAndXh(yhbingqyList);
            }
        });
    }

    @Override
    public IPage<Map> getNoOrderYh(Page<Map> page, String exclude, String qydbm, String inputcontent, String isPlan, String qybm) {
        if (StringUtil.isEmpty(exclude)) {
            throw new ToflyDeniedException("请传递已选的用户编号");
        }
        List<String> yhbhList = Arrays.asList(exclude.split(","));
        String month = Date8Util.getYearMonth();
        if ("1".equals(isPlan)) {
            //查询计划的
            return baseMapper.getNoOrderYhPlan(page, yhbhList, qydbm, inputcontent, month, qybm);
        }
        //查询绑定表的
        return baseMapper.getNoOrderYh(page, yhbhList, qydbm, inputcontent, qybm);
    }

    /**
     * 初始化每月生成抄表月计划数据
     *
     * @param shouldDown     生成的用户
     * @param qfcbplandetail 计划信息
     * @param monthPlan      下一月计划
     * @return
     */
    @Override
    public boolean insertShouldCbYh(List<Readmeterhis> shouldDown, Qfcbplandetails qfcbplandetail, String monthPlan) {
        return baseMapper.insertShouldCbYh(shouldDown, qfcbplandetail, monthPlan);
    }

    @Override
    public List<Map> getCbbOrCbpq(String cbpq, String isPqOrCbb) {
        return baseMapper.getCbbOrCbpq(cbpq, isPqOrCbb);
    }

    @Override
    public Page<ForeignYhQyQdVo> getUserQy(Page page, ForeignYhQyQdDto dto) {
        return this.baseMapper.loadQy(page, dto);
    }

    @Override

    public void synchroYhQyAndQyd() {

        try {
            int row = 1000;
            while (true) {
                List<YhQyQdInfoVo> infos = this.baseMapper.getYhQyAndQydInfo(row);
                if (CollUtil.isEmpty(infos)) {
                    return;
                }
                List<ForeignSynchroYhQyQdVo> fs = new ArrayList<>();
                List<String> yhbhs = new ArrayList<>();
                for (YhQyQdInfoVo vo : infos) {
                    ForeignSynchroYhQyQdVo fvo = new ForeignSynchroYhQyQdVo();
                    BeanUtils.copyProperties(vo, fvo);
                    fvo.setZt(ComConstans.INT_ZERO);
                    fs.add(fvo);
                    if (ComConstans.INT_ONE.equals(vo.getYhFlag())) {
                        yhbhs.add(vo.getKhbh());
                    }
                }
                if (sendInfo(fs)) {
                    if (CollUtil.isNotEmpty(yhbhs)) {
                        this.update(new UpdateWrapper<Yhbingqy>()
                                .lambda().set(Yhbingqy::getUpdateFlag, ComConstans.STR_ZERO).in(Yhbingqy::getYhbh, yhbhs));
                    }
                }
                if (infos.size() < row) {
                    break;
                }
            }
        } catch (Exception e) {
            log.error("异常", e);
        }
    }


    public boolean sendInfo(List<ForeignSynchroYhQyQdVo> infos) throws Exception {
        Map<String, Object> datas = new HashMap<>();
        datas.put("DATAS", infos);
        datas.put("ACTION", "query");
        Map cbMap = CallAPIUtil.callAPIUtil(datas, "api/Business/RegionAndSectionUpdate");
        if (CollUtil.isNotEmpty(cbMap) && "0000".equals(cbMap.get("Code"))) {
            return true;
        }
        log.warn("同步区域区段数据::{} 失败, 返回信息:{}", JSONObject.toJSON(infos), JSONObject.toJSON(cbMap));
        return false;
    }


    /**
     * 执行用户分配
     *
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class )
    public boolean clientDistribution() {
        //获取区段信息
        List<Cbqydbm> zoneDataList = cbqydbmService.list(Wrappers.<Cbqydbm>lambdaQuery().eq(Cbqydbm::getYxbz, "0"));

        if (CollectionUtil.isEmpty(zoneDataList)){
            throw new ToflyDeniedException("请确认是否存在有效区段");
        }
        //未分配用户与抄表本信息
        List<Map<String, String>> notAssignedClient = baseMapper.notAssignedClient();
        if (CollectionUtils.isNotEmpty(notAssignedClient)){
            //未分配用户分组
            Map<String, List<Map<String, String>>> notAssignedGroup = notAssignedClient.stream()
                    .filter(m -> StringUtils.isNotBlank(m.get("cbb")))
                    .collect(Collectors.groupingBy(m -> m.get("cbb")));
            Allocation allocation;
            for (Cbqydbm zone : zoneDataList) {
                String qydbm = zone.getQydbm();
                if (notAssignedGroup.containsKey(qydbm)){
                    String clientCode = notAssignedGroup.get(qydbm).stream()
                            .map(m -> m.get("yhbh")).collect(Collectors.joining(","));
                    allocation = new Allocation();
                    allocation.setQybm(zone.getQybm());
                    allocation.setQydbm(qydbm);
                    allocation.setTwoType("0");
                    allocation.setYhbh(clientCode);
                    allocationManager(allocation);
                }
            }
        }
        synchronousAboutMapper.updateMeterNum();
        return true;
    }

}
