package com.m2pool.pool.service.impl;

import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.m2pool.common.core.constant.HttpStatus;
import com.m2pool.common.core.text.Convert;
import com.m2pool.common.core.utils.DateUtils;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.core.utils.uuid.IdUtils;
import com.m2pool.common.core.web.Result.AjaxResult;
import com.m2pool.common.core.web.page.TableDataInfo;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.pool.dto.*;
import com.m2pool.pool.entity.UserReadOnlyPageInfo;
import com.m2pool.pool.entity.UserReadOnlyPage;
import com.m2pool.pool.enums.PoolProfitScale;
import com.m2pool.pool.enums.PoolUnits;
import com.m2pool.pool.enums.Pools;
import com.m2pool.pool.mapper.AccountDistributionMapper;
import com.m2pool.pool.mapper.PoolMapper;
import com.m2pool.pool.mapper.UserAccountMapper;
import com.m2pool.pool.service.ReadOnlyService;
import com.m2pool.pool.utils.EnumUtils;
import com.m2pool.pool.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description TODO
 * @Date 2024/6/14 14:29
 * @Author dy
 */
@Service
public class ReadOnlyServiceImpl implements ReadOnlyService {

    @Autowired
    private UserAccountMapper uaMapper;

    @Autowired
    private AccountDistributionMapper distributionMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private PoolMapper poolMapper;

    @Value("${myenv.domain}")
    private String domain;

    @Value("${myenv.img}")
    private String STATIC_IMG;

    private final String READ_ONLY="/readOnlyDisplay";


    @DSTransactional
    @Override
    public AjaxResult createPageUrl(ReadOnlyHtmlCreateVo vo) {
        long t0 = System.currentTimeMillis();
        //获取邮箱 网站用的邮箱做用户名
        String email = SecurityUtils.getUsername();
        if(StringUtils.isNull(email)){
            return AjaxResult.error("token解析异常");
        }
        UserAccountDto info = uaMapper.getAccountByUserAccountAndCoin(email, vo.getAccount(), vo.getCoin());
        if(StringUtils.isNull(info)){
            return AjaxResult.success("根据币种["+vo.getCoin()+"]未找到对应挖矿账号");
        }
        String config = StringUtils.clean(vo.getConfig());
        if(StringUtils.isBlank(config)){
            return AjaxResult.error("请选择至少一个权限");
        }
        //生产uuid作为唯一id
        String key = IdUtils.fastSimpleUUID();
        //判断key是否重复 重复则更换
        if(uaMapper.checkKeyIfExist(key)){
            key = IdUtils.fastSimpleUUID();
            if(uaMapper.checkKeyIfExist(key)){
                return AjaxResult.success("网络异常,请稍后再试");
            }
        }

        //构造入库数据
        UserReadOnlyPage data = new UserReadOnlyPage();
        data.setEmail(info.getUser());
        data.setAccount(info.getAccount());
        data.setCoin(info.getCoin());
        data.setKey(key);
        //默认设置为zh
        data.setLang("zh");
        if(StringUtils.isNotNull(vo.getLang())){
            if("en".equals(vo.getLang())){
                data.setLang("en");
            }
        }
        data.setRemark(StringUtils.clean(vo.getRemark()));
        data.setConfig(config);

        //数据入库
        boolean result = uaMapper.createReadOnlyPage(data);
        if(result){
            PageUrlDto dto = new PageUrlDto();
            dto.setUrl(domain+"/"+data.getLang()+READ_ONLY+"?key="+key);
            dto.setKey(key);
            long t1 = System.currentTimeMillis();
            System.out.println("创建页面业务耗时"+(t1-t0)+"ms");
            return AjaxResult.success(dto);
        }else {
            return AjaxResult.error("网络异常,请稍后再试");
        }
    }

    @Override
    public  TableDataInfo getUrlList(PageVo vo) {
        long t0 = System.currentTimeMillis();
        //获取邮箱 网站用的邮箱做用户名
        String email = SecurityUtils.getUsername();
        TableDataInfo table = new TableDataInfo();
        if(StringUtils.isNull(email)){
            table.setCode(HttpStatus.ERROR);
            table.setRows(new ArrayList<>());
            table.setMsg("未获取到token中的用户名,请稍后再试");
            table.setTotal(0);
            table.setTotalPage(0);
            return table;
        }
        List<UserReadOnlyPageInfo> list = uaMapper.getPageListByUser(email);
        List<UserReadPageInfoReturnDto> returnList = list.stream().map(e -> {
            UserReadPageInfoReturnDto dto = new UserReadPageInfoReturnDto();
            BeanUtils.copyProperties(e,dto);
            dto.setLabel(Pools.getNameByCoin(e.getCoin()));
            if(StringUtils.isBlank(e.getLang())){
                //没值则默认设为中文
                e.setLang("zh");
            }
            if("en".equals(e.getLang()) && "zh".equals(e.getLang())){
                //违规参数则默认设为中文
                e.setLang("zh");
            }
            dto.setUrl(domain +"/"+e.getLang() + READ_ONLY + "?key=" + e.getKey());
            dto.setImg(domain+STATIC_IMG+"/"+Pools.getImgByCoin(e.getCoin()));
            if(StringUtils.isNull(e.getConfig())){
                //防止字段为null时抛出异常 字段未null时设置字段为""
                dto.setConfig("".split(","));
            }else {
                dto.setConfig(e.getConfig().split(","));
            }
            return dto;
        }).collect(Collectors.toList());

        table.setCode(HttpStatus.SUCCESS);
        table.setMsg("查询成功");
        PageInfo pageInfo = new PageInfo(list);
        table.setTotal(pageInfo.getTotal());
        table.setTotalPage(pageInfo.getPages());

        int page = vo.getPage() < 1 ? 1 : vo.getPage();
        int limit = vo.getLimit() < 1 ? 1 : vo.getLimit();

        List<UserReadPageInfoReturnDto> pageList = returnList.stream().skip((page - 1) * limit).limit(limit).collect(Collectors.toList());

        table.setRows(pageList);

        return table;
    }

    @Override
    public AjaxResult getUrlInfo(ReadOnlyKeyVo vo) {
        String key = StringUtils.clean(vo.getKey());
        String email = SecurityUtils.getUsername();
        if(StringUtils.isNull(email)){
            return null;
        }
        PageHelper.clearPage();
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(key);
        if(StringUtils.isNull(info)){
            return AjaxResult.error("未找到对应Key");
        }

        if(!email.equals(info.getUser())){
            return AjaxResult.error("请勿擅自修改Key");
        }
        UserReadPageInfoReturnDto dto = new UserReadPageInfoReturnDto();
        BeanUtils.copyProperties(info,dto);

        dto.setLabel(Pools.getNameByCoin(info.getCoin()));
        if(StringUtils.isBlank(info.getLang())){
            //没值则默认设为中文
            info.setLang("zh");
        }
        if("en".equals(info.getLang()) && "zh".equals(info.getLang())){
            //违规参数则默认设为中文
            info.setLang("zh");
        }
        dto.setUrl(domain + "/" +info.getLang() + READ_ONLY + "?key=" + info.getKey());
        if(StringUtils.isNull(info.getConfig())){
            //防止字段为null时抛出异常 字段未null时设置字段为""
            dto.setConfig("".split(","));
        }else {
            dto.setConfig(info.getConfig().split(","));
        }
        return AjaxResult.success(dto);
    }

    @Override
    public AjaxResult changeUrlInfo(ReadOnlyHtmlChangeVo vo) {
        if(StringUtils.isNotBlank(vo.getConfig())){
            if(!vo.getConfig().equals(StringUtils.clean(vo.getConfig()))){
                return AjaxResult.error("配置传参中传入违规字符");
            }
        }else {
            return AjaxResult.error("配置传参不能为空");
        }

        if(StringUtils.isNotBlank(vo.getKey())){
            if(!vo.getKey().equals(StringUtils.clean(vo.getKey()))){
                return AjaxResult.error("页面码传参中传入违规字符");
            }
        }

        boolean result = uaMapper.changeReadOnlyPage(vo);
        if(result){
            return AjaxResult.success("修改成功");
        }else {
            result = uaMapper.changeReadOnlyPage(vo);
            if(result){
                return AjaxResult.success("修改成功");
            }else {
                if(uaMapper.checkKeyIfExist(vo.getKey())){
                    return AjaxResult.error("网络异常,请稍后再试");
                }else {
                    return AjaxResult.error("当前Key失效,请检查是否修改");
                }
            }
        }
    }

    @Override
    @DSTransactional
    public AjaxResult delPageByKey(ReadOnlyKeyVo vo) {
        String email = SecurityUtils.getUsername();
        if(StringUtils.isNull(email)){
            return AjaxResult.error("token解析异常");
        }

        List<String> keys = Arrays.asList(StringUtils.clean(vo.getKey()).split(","));

        if(keys.size() < 1){
            return AjaxResult.error("key不能为空");
        }

        //todo 改成一次性查多个
        List<UserReadOnlyPageInfo> list = uaMapper.getPageListByUser(email);
        if(list.size() < 0){
            return AjaxResult.error("key不存在");
        }

        List<String> userKeys = list.stream().map(UserReadOnlyPageInfo::getKey).collect(Collectors.toList());

        if(userKeys.containsAll(keys)){
            //执行删除
            try {
                uaMapper.batchDeletePageByKeyAndUser(keys, email);
                return AjaxResult.success("删除成功");
            }catch (Exception e){
                return AjaxResult.error("网络异常,请稍后再试");
            }

        }else {
            StringBuilder msg = new StringBuilder();
            msg.append("当前用户下不存在对应key:");
            keys.stream().forEach(key -> {
                if(!userKeys.contains(key)){
                    msg.append(key+"\n");
                }
            });
            return AjaxResult.error(msg.toString());
        }

    }

    @Override
    public AjaxResult getPageInfo(ReadOnlyKeyVo vo) {
        String key = StringUtils.clean(vo.getKey());
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(key);

        ReadOnlyPageInfo returnInfo = new ReadOnlyPageInfo();
        returnInfo.setCoin(info.getCoin());
        returnInfo.setAccount(info.getAccount());
        returnInfo.setImg(domain+STATIC_IMG+"/"+Pools.getImgByCoin(info.getCoin()));
        if(StringUtils.isNull(info.getConfig())){
            //防止字段为null时抛出异常 字段未null时设置字段为""
            returnInfo.setConfig("".split(","));
        }else {
            returnInfo.setConfig(info.getConfig().split(","));
        }
        returnInfo.setId(info.getId());
        return AjaxResult.success(returnInfo);
    }

    @Override
    public AjaxResult getProfitInfo(ReadOnlyKeyVo vo) {
        //权限鉴定 通过key鉴定 权限配置中需要包含2
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(vo.getKey());
        if(StringUtils.isNull(info)){
            return AjaxResult.error("key不存在");
        }
        boolean check = checkPermission("2", info.getConfig());
        if(!check){
            return AjaxResult.error("鉴权失败");
        }

        UserAccountDto dto = uaMapper.getAccountByUserAccountAndCoin(info.getUser(), info.getAccount(), info.getCoin());
        AccountProfitInfoDto infoDto = new AccountProfitInfoDto();
        if(StringUtils.isNull(dto)){
            return AjaxResult.error("找不到"+vo.getKey()+"对应数据");
        }

        infoDto.setAccount(dto.getAccount());

        try {

            //根据用户名拿所有状态不为3（失败）的数据 然后根据拿到的数据分情况算总和 ->不筛选状态
            List<ProfitDto> list = distributionMapper.getDistributionByMinerAccount(dto.getCoin(), dto.getAccount());

            Map<Integer, BigDecimal> countMap = list.stream().collect(Collectors.groupingBy(
                    ProfitDto::getStatus,
                    Collectors.reducing(BigDecimal.ZERO,ProfitDto::getProfit,BigDecimal::add)
            ));

            BigDecimal total = list.stream().map(ProfitDto::getProfit).reduce(BigDecimal.ZERO, BigDecimal::add);

            //支付中 status =0
            BigDecimal pengding = countMap.getOrDefault(0, BigDecimal.ZERO);
            //已支付 status =1
            BigDecimal success = countMap.getOrDefault(1, BigDecimal.ZERO);

            //2不足起付额 激活了自动提现
            BigDecimal state2 = countMap.getOrDefault(2, BigDecimal.ZERO);
            //3满足起付额 没激活自动提现
            BigDecimal state3 = countMap.getOrDefault(3, BigDecimal.ZERO);
            //4不足起付额 且没激活自动提现
            BigDecimal state4 = countMap.getOrDefault(4, BigDecimal.ZERO);

            //wallet_outv2表 已支出
            BigDecimal out = distributionMapper.getTotalOutByCoinAndUser(dto.getCoin(), dto.getAccount());
            if(StringUtils.isNull(out)) {
                out =BigDecimal.ZERO;
            }

            System.out.println("success:"+success+",pengding:"+pengding);
            /********************************************  获取今日和昨天起始时间 开始 *********************************************************/
            //因为数据库create_date为结算时间 所以以今日0为时间的数据代表昨日已挖
            Date today = DateUtils.dateTime(DateUtils.YYYY_MM_DD, DateUtils.getDate());

            Calendar ca = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
            ca.setTime(today);
            ca.add(Calendar.DATE,1);

            Date todayEnd = ca.getTime();
            System.out.println(today+" to "+todayEnd);
            /********************************************  获取今日和昨天起始时间 结束 *********************************************************/

            List<ProfitDto> preList = list.stream().filter(e ->
                    ((e.getDate().after(today) || e.getDate().equals(today)) && e.getDate().before(todayEnd))).collect(Collectors.toList());
            BigDecimal preProfit = BigDecimal.ZERO;
            if (preList.size() > 0){
                preProfit = preList.stream()
                        .map(ProfitDto::getProfit)
                        .reduce(BigDecimal::add)
                        .get();
            }


            //今日收益通过算力占比和今日报块总和去计算 当前挖矿账户
            Date now = new Date();
            int minute = (now.getMinutes() / 30) * 30;
            now.setMinutes(minute);
            now.setSeconds(0);
            List<BlockInfoDto> tbList = new ArrayList<>();
            switch (dto.getCoin().toLowerCase()){
                case "nexa": tbList = poolMapper.getNewNEXABlockInfoList(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, today));break;
                case "mona": tbList = poolMapper.getNewMONABlockInfoList(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, today));break;
                case "grs": tbList = poolMapper.getNewGRSBlockInfoList(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, today));break;
                case "dgbo": tbList = poolMapper.getNewDGBOBlockInfoList(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, today));break;
                case "dgbq": tbList = poolMapper.getNewDGBQBlockInfoList(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, today));break;
                case "dgbs": tbList = poolMapper.getNewDGBSBlockInfoList(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, today));break;
                case "rxd": tbList = poolMapper.getNewRXDBlockInfoList(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, today));break;
                default: break;
            }            //今日矿池总收益
            BigDecimal reward = tbList.stream()
                    .filter(e -> (e.getDate().before(now) || e.getDate().equals(now)))
                    .map(e -> {
                        return BigDecimal.valueOf(e.getReward());
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            //今日总收益 * 用户今日平均算力占比 = 用户今日预估收益
            BigDecimal ratio = Convert.toBigDecimal(redisService.getCacheMapValue(dto.getCoin().toUpperCase()+"UserPowerRatio", dto.getAccount()), BigDecimal.ZERO);

            //List<ProfitDto> todayList = list.stream().filter(e ->
            //        (e.getDate().after(today) || e.getDate().equals(today))).collect(Collectors.toList());
            BigDecimal todayProfit = reward.multiply(ratio).setScale(2,BigDecimal.ROUND_HALF_UP);


            int scale = PoolProfitScale.getScaleByCoin(info.getCoin());
            // 总收益  支付中+已支付  ——》total
            infoDto.setTotalProfit(total.setScale(scale,BigDecimal.ROUND_HALF_UP));
            System.out.println(infoDto);
            //总支出  支付中+wallet_outv2表中全部
            infoDto.setExpend(pengding.add(out).setScale(scale,BigDecimal.ROUND_HALF_UP));
            System.out.println(infoDto);
            //昨日收益
            infoDto.setPreProfit(preProfit.setScale(scale,BigDecimal.ROUND_HALF_UP));
            System.out.println(infoDto);
            //今日收益 根据公式 (挖矿账户mhs24h/矿池mhs24h) * 当日矿池收益 当日矿池收益:select sum(
            infoDto.setTodayPorfit(todayProfit.setScale(scale,BigDecimal.ROUND_HALF_UP));
            //账户余额 已成熟
            //总余额  总收益-总支出
            //infoDto.setBalance(infoDto.getTotalProfit().subtract(infoDto.getExpend()));
            infoDto.setBalance(state2.add(state3).add(state4).setScale(scale,BigDecimal.ROUND_HALF_UP));
            //可提现余额  已成熟0
            infoDto.setWithdrawable(BigDecimal.ZERO);

            return AjaxResult.success(infoDto);
        }catch (Exception e){
            System.out.println("异常：");
            e.printStackTrace();

            infoDto.setTotalProfit(BigDecimal.valueOf(0));
            infoDto.setBalance(BigDecimal.valueOf(0));
            infoDto.setBalance(BigDecimal.valueOf(0));
            infoDto.setExpend(BigDecimal.valueOf(0));
            infoDto.setPreProfit(BigDecimal.valueOf(0));
            infoDto.setTodayPorfit(BigDecimal.valueOf(0));

            return AjaxResult.success(infoDto);
        }
    }

    @Override
    public AjaxResult getMinerAccountPower(ReadOnlyIntervalVo vo) {
        //权限鉴定 通过key鉴定 权限配置中需要包含1
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(vo.getKey());
        if(StringUtils.isNull(info)){
            return AjaxResult.error("key不存在");
        }
        //boolean check = checkPermission("1", info.getConfig());
        //if(!check){
        //    return AjaxResult.error("鉴权失败");
        //}

        UserAccountDto dto = uaMapper.getAccountByUserAccountAndCoin(info.getUser(), info.getAccount(), info.getCoin());
        if(StringUtils.isNull(dto)){
            return AjaxResult.error("未请求到数据,请确认Key是否失效");
        }
        Pools pool = (Pools) EnumUtils.get(Pools.class, dto.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error("数据异常！请稍后再试");
        }
        int scale = PoolProfitScale.getScaleByCoin(pool.getCoin());
        if("1h".equals(vo.getInterval())  || "rt".equals(vo.getInterval())){
            //根据需求改成返回30分钟的数据
            List<PowerLineDto> list = poolMapper.get30mAccountPowerListByAccount(pool.getCoin()+"_users_30m", info.getAccount());
            Collections.reverse(list);
            list.stream().forEach(e -> {
                //MH/s 每秒转GH/s
                e.setPv(e.getPv().divide(BigDecimal.valueOf(1000),scale,BigDecimal.ROUND_HALF_UP).stripTrailingZeros());
            });
            return AjaxResult.success(list);
        }else if ("1d".equals(vo.getInterval())){
            List<PowerLineDto> list = poolMapper.getDailyAccountPowerListByAccount(pool.getMhs()+"24h",info.getAccount());
            //从里面筛选date=0
            Collections.reverse(list);

            list.stream().forEach(e -> {
                //MH/s 每秒转GH/s
                e.setDate(DateUtils.addDays(e.getDate(),-1));
                e.setPv(e.getPv().divide(BigDecimal.valueOf(1000),scale,BigDecimal.ROUND_HALF_UP).stripTrailingZeros());
            });

            List<MinerDataDto> lastList = poolMapper.getMinerInfoList(pool.getMhs());
            BigDecimal mhs = lastList.stream().filter(e -> e.getUser().equals(dto.getAccount())).map(e -> {
                return BigDecimal.valueOf(e.getMhs24h());
            }).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(BigDecimal.valueOf(pool.getFac()));
            PowerLineDto lastPLDto= new PowerLineDto();
            Date now = DateUtils.parseDate(DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD));
            lastPLDto.setDate(now);
            lastPLDto.setPv(mhs.divide(BigDecimal.valueOf(1000),scale,BigDecimal.ROUND_HALF_UP).stripTrailingZeros());
            list.add(lastPLDto);


            return AjaxResult.success(list);
        }else {
            return AjaxResult.error("参数错误:interval");
        }
    }

    @Override
    public AjaxResult getAccountPowerDistribution(ReadOnlyIntervalVo vo) {
        //权限鉴定 通过key鉴定 权限配置中需要包含1
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(vo.getKey());
        if(StringUtils.isNull(info)){
            return AjaxResult.error("key不存在");
        }
        //boolean check = checkPermission("1", info.getConfig());
        //if(!check){
        //    return AjaxResult.error("鉴权失败");
        //}

        UserAccountDto dto = uaMapper.getAccountByUserAccountAndCoin(info.getUser(), info.getAccount(), info.getCoin());
        if(StringUtils.isNull(dto)){
            return AjaxResult.error("未请求到数据,请确认Key是否失效");
        }
        Pools pool = (Pools) EnumUtils.get(Pools.class, dto.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error("数据异常！请稍后再试");
        }
        List<MinerDataDto> minerList= poolMapper.getMinerInfoList(pool.getMhs());
        minerList.stream().forEach(e ->{
            if(StringUtils.isNotNull(e.getMhs())){
                e.setMhs(e.getMhs() * pool.getFac());
            }
        });
        List<MinerDataDto> list = minerList.stream().filter(e -> info.getAccount().equals(e.getUser())).collect(Collectors.toList());
        if(StringUtils.isNull(list)){
            return AjaxResult.success();
        }
        /************************************************  过滤异常数据 开始 ******************************************************/
        //
        Date nowDate = DateUtils.getNowDate();
        Date start = DateUtils.addDays(nowDate, -1);
        //过滤掉last_submit超过一天的offline数据
        list = list.stream().filter(e -> e.getLastSubmit().after(start)).collect(Collectors.toList());
        /************************************************  过滤异常数据 结束 ******************************************************/
        if(StringUtils.isNull(list)){
            return AjaxResult.success();
        }

        if("rt".equals(vo.getInterval())){
            //用mhs
            List<DistributionDto> returnList = getDailyDistributionDtoByDataList(list, pool.getCoin());
            return AjaxResult.success(returnList);

        }
        else if ("1d".equals(vo.getInterval())){
            //用mhs24h
            List<DistributionDto> returnList = getDailyDistributionDtoByDataList(list, pool.getCoin());
            return AjaxResult.success(returnList);

        }else {
            return AjaxResult.error("参数错误:interval");
        }
    }

    @Override
    public AjaxResult getMinerList(ReadOnlyListVo vo) {
        //权限鉴定 通过key鉴定 权限配置中需要包含1
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(vo.getKey());
        if(StringUtils.isNull(info)){
            return AjaxResult.error("key不存在");
        }
        boolean check = checkPermission("1", info.getConfig());
        if(!check){
            return AjaxResult.error("鉴权失败");
        }

        UserAccountDto uaDto = uaMapper.getAccountByUserAccountAndCoin(info.getUser(), info.getAccount(), info.getCoin());
        if(StringUtils.isNull(uaDto)){
            return AjaxResult.error("未请求到数据,请确认Key是否失效");
        }
        Pools pool = (Pools) EnumUtils.get(Pools.class, uaDto.getCoin());
        if(StringUtils.isNull(pool)){
            return AjaxResult.error("数据异常！请稍后再试");
        }

        List<MinerDataDto> minerList= poolMapper.getMinerInfoList(pool.getMhs());
        minerList.stream().forEach(e ->{
            if(StringUtils.isNotNull(e.getMhs())){
                e.setMhs(BigDecimal.valueOf(e.getMhs() * pool.getFac()).setScale(6,BigDecimal.ROUND_HALF_UP).doubleValue());
            }
        });
        List<MinerDataDto> list = minerList.stream().filter(e -> info.getAccount().equals(e.getUser())).collect(Collectors.toList());
        /************************************************  过滤异常数据 开始 ******************************************************/
        //
        Date nowDate = DateUtils.getNowDate();
        Date start = DateUtils.addDays(nowDate, -1);
        //过滤掉last_submit超过一天的offline数据
        list = list.stream().filter(e -> e.getLastSubmit().after(start)).collect(Collectors.toList());
        /************************************************  过滤异常数据 结束 ******************************************************/

        //判断是否需要排序
        if("30m".equals(vo.getSort())){
            //根据30分钟算力排序 默认降序 即算力从大到小
            list = list.stream().sorted(
                    Comparator.comparing(MinerDataDto::getMhs).reversed().thenComparing(MinerDataDto::getMhs)
            ).collect(Collectors.toList());
            if("asc".equals(vo.getCollation())){
                Collections.reverse(list);
            }
        }else if("24h".equals(vo.getSort())){
            //根据24h算力排序 默认升序
            list = list.stream().sorted(
                    Comparator.comparing(MinerDataDto::getMhs24h).reversed().thenComparing(MinerDataDto::getMhs24h)
            ).collect(Collectors.toList());
            if("asc".equals(vo.getCollation())){
                Collections.reverse(list);
            }
        }else {
            //其他情况不做处理
        }

        Map<String, Long> countMap = list.stream().collect(Collectors.groupingBy(
                MinerDataDto::getOnline,
                Collectors.counting()
        ));
        Long online = countMap.getOrDefault("online", 0L);
        //if(online < 0){
        //    return AjaxResult.error("");
        //}
        Long offline = countMap.getOrDefault("offline", 0L) +countMap.getOrDefault("disconnected", 0L)  ;

        //if(offline < 0){
        //    return AjaxResult.error("");
        //}
        if((online+offline) != list.size()){
            return AjaxResult.error("后台数据暂不可以用,请等待后台修复..");
        }
        //计算1h算力总和
        double rate = list.stream().map(e -> BigDecimal.valueOf(e.getMhs())).collect(Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)).setScale(6,BigDecimal.ROUND_HALF_UP).doubleValue();
        //计算24h算力总和
        double dailyRate = list.stream().map(e -> BigDecimal.valueOf(e.getMhs24h())).collect(Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)).setScale(6,BigDecimal.ROUND_HALF_UP).doubleValue();

        //根据公式 rejects / (rejects+accepts)
        BigDecimal rejects = list.stream().map(e -> BigDecimal.valueOf(e.getRejects())).collect(Collectors.reducing(BigDecimal.ZERO, BigDecimal::add));
        BigDecimal submits = list.stream().map(e -> BigDecimal.valueOf(e.getRejects()).add(BigDecimal.valueOf(e.getAccepts()))).collect(Collectors.reducing(BigDecimal.ZERO, BigDecimal::add));
        double reject =0;
        if(submits.compareTo(BigDecimal.ZERO) != 0){
            reject = rejects.divide(submits,6,BigDecimal.ROUND_HALF_UP).doubleValue();
        }


        //根据用户传参 构造矿工列表表格数据
        //1.通过type判断是返回全部/在线/离线
        String type = "";
        int total = list.size();
        List<MinerDto> row = new ArrayList<>();
        if(0 == vo.getType()){
            row = list.stream().map(e -> {
                MinerDto mDto = new MinerDto();
                mDto.setId("all"+e.getMiner());
                mDto.setMiner(e.getMiner());
                mDto.setRate(e.getMhs());
                mDto.setDailyRate(e.getMhs24h());
                mDto.setReject(e.getRejectsRatio());
                mDto.setSubmit(e.getLastSubmit());
                if("online".equals(e.getOnline())){
                    mDto.setStatus("1");
                }else if("offline".equals(e.getOnline())){
                    mDto.setStatus("2");
                    long  offTime = DateUtils.getDateDiffMinute(e.getDate(),e.getLastSubmit());
                    offTime = offTime < 0 ? 0 : offTime;
                    mDto.setOffline(offTime);
                }else {
                    mDto.setStatus(e.getOnline());
                }
                return mDto;
            }).collect(Collectors.toList());
        }else if(1 == vo.getType()){
            row = list.stream().filter(e -> "online".equals(e.getOnline())).map(e -> {
                MinerDto mDto = new MinerDto();
                mDto.setId("on"+e.getMiner());
                mDto.setMiner(e.getMiner());
                mDto.setRate(e.getMhs());
                mDto.setDailyRate(e.getMhs24h());
                mDto.setReject(e.getRejectsRatio());
                mDto.setSubmit(e.getLastSubmit());
                mDto.setStatus("1");
                return mDto;
            }).collect(Collectors.toList());

            total = Convert.toInt(online);
        }else  if(2 == vo.getType()){
            row = list.stream().filter(e -> ("offline".equals(e.getOnline()) || "disconnected".equals(e.getOnline()))).map(e -> {
                MinerDto mDto = new MinerDto();
                mDto.setId("off"+e.getMiner());
                mDto.setMiner(e.getMiner());
                mDto.setRate(e.getMhs());
                mDto.setDailyRate(e.getMhs24h());
                mDto.setReject(e.getRejectsRatio());
                mDto.setSubmit(e.getLastSubmit());
                mDto.setStatus("2");
                long  offTime = DateUtils.getDateDiffMinute(e.getDate(),e.getLastSubmit());
                offTime = offTime < 0 ? 0 : offTime;
                mDto.setOffline(offTime);
                return mDto;
            }).collect(Collectors.toList());

            total = Convert.toInt(offline);
        }else {
            return AjaxResult.error("参数错误");
        }
        //2.通过filter参数传的矿工名根据矿工再次筛选
        if(StringUtils.isNotBlank(vo.getFilter())){
            row = row.stream().filter(e -> vo.getFilter().equals(e.getMiner())).collect(Collectors.toList());
        }


        //分页
        int page = vo.getPage() > 0 ? vo.getPage() : 1;
        int size = vo.getLimit() > 0 ? vo.getLimit() : 10;
        row = row.stream().skip((page - 1) * size).limit(size).collect(Collectors.toList());


        //构造返回体数据
        MinerListDto dto = new MinerListDto();
        //筛选出的list数据条数据即矿工总数
        dto.setAll(list.size());
        dto.setOffline(Convert.toInt(offline));
        dto.setOnline(Convert.toInt(online));
        dto.setDailyRate(dailyRate);
        dto.setMiner("合计");
        dto.setRate(rate);
        dto.setReject(reject);
        dto.setTotal(total);
        dto.setPage(getTotalPage(total,size));
        dto.setRows(row);
        //todo 删除掉
        dto.setSubmit(nowDate);
        return AjaxResult.success(dto);
    }

    @Override
    public AjaxResult getMinerPower(ReadOnlyMinerVo vo) {
        //权限鉴定 通过key鉴定 权限配置中需要包含1
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(vo.getKey());
        if(StringUtils.isNull(info)){
            return AjaxResult.error("key不存在");
        }
        boolean check = checkPermission("1", info.getConfig());
        if(!check){
            return AjaxResult.error("鉴权失败");
        }
        UserAccountDto uaDto = null;
        try {
            uaDto = uaMapper.getAccountByUserAccountAndCoin(info.getUser(), info.getAccount(), info.getCoin());
        }catch (Exception e){
            //避免数据库报错未能捕捉到
            return AjaxResult.error("网络异常,请稍后再试");
        }

        if(StringUtils.isNull(uaDto)){
            return AjaxResult.error("页面已失效！");
        }

        Pools pool = (Pools) EnumUtils.get(Pools.class, uaDto.getCoin());
        //根据account和miner查询
        String table = pool.getMhs()+"30m";

        List<PowerLineDto> list = poolMapper.getMinerPowerList(table, info.getAccount(), vo.getAccount());

        Collections.reverse(list);

        return AjaxResult.success(list);
    }

    @Override
    public TableDataInfo getHistoryIncome(ReadOnlyPageVo vo) {
        //权限鉴定 通过key鉴定 权限配置中需要包含2
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(vo.getKey());
        TableDataInfo rspData = new TableDataInfo();
        if(StringUtils.isNull(info)){
            rspData.setCode(HttpStatus.ERROR);
            rspData.setRows(new ArrayList<>());
            rspData.setMsg("key不存在");
            rspData.setTotal(0);
            rspData.setTotalPage(0);
            return rspData;
        }
        boolean check = checkPermission("2", info.getConfig());
        if(!check){
            rspData.setCode(HttpStatus.ERROR);
            rspData.setRows(new ArrayList<>());
            rspData.setMsg("鉴权失败");
            rspData.setTotal(0);
            rspData.setTotalPage(0);
            return rspData;
        }

        UserAccountDto dto = uaMapper.getAccountByUserAccountAndCoin(info.getUser(), info.getAccount(), info.getCoin());

        if(StringUtils.isNull(dto)) {
            rspData.setCode(HttpStatus.ERROR);
            rspData.setRows(new ArrayList<>());
            rspData.setMsg("未获取到挖矿账户信息");
            rspData.setTotal(0);
            rspData.setTotalPage(0);
            return rspData;
        }

        List<ProfitDto> list = distributionMapper.getDayDistributionByMinerAccount(dto.getCoin(), dto.getAccount());
        //查询挖矿账户每日0点的算力  查询结果需要-1 才能代表当天的算力
        List<PowerLineDto> mhsList = poolMapper.get24hAccountPowerListByAccount(dto.getCoin() + "_users_24h", dto.getAccount());

        List<IncomeDto> returnList = list.stream().map(e -> {
            IncomeDto income = new IncomeDto();
            income.setCoin(dto.getCoin());
            income.setDate(e.getDate());
            //income.setDifficult();
            mhsList.stream().anyMatch(m ->{
                if(m.getDate().equals(e.getDate())){
                    income.setMhs(m.getPv().stripTrailingZeros().doubleValue());
                }
                return false;
            });
            if(StringUtils.isNotNull(e.getProfit())){
                income.setAmount(e.getProfit()
                        .setScale(10,BigDecimal.ROUND_HALF_UP));
                //alph需要除以10的18次方
                if("aplh".equals(dto.getCoin())){
                    income.setAmount(e.getProfit()
                            .divide(BigDecimal.valueOf(1000*1000))
                            .divide(BigDecimal.valueOf(1000*1000))
                            .divide(BigDecimal.valueOf(1000*1000))
                            .setScale(10,BigDecimal.ROUND_HALF_UP));
                }
            }
            return income;
        }).collect(Collectors.toList());


        rspData.setCode(HttpStatus.SUCCESS);
        rspData.setMsg("查询成功");
        PageInfo pageInfo = new PageInfo(returnList);
        rspData.setTotal(pageInfo.getTotal());
        rspData.setTotalPage(pageInfo.getPages());

        int page = vo.getPage() < 1 ? 1 : vo.getPage();
        int limit = vo.getLimit() < 1 ? 1 : vo.getLimit();

        List<IncomeDto> pageList = returnList.stream().skip((page - 1) * limit).limit(limit).collect(Collectors.toList());

        rspData.setRows(pageList);

        return rspData;
    }

    @Override
    public TableDataInfo getHistoryOutcome(ReadOnlyPageVo vo) {

        //权限鉴定 通过key鉴定 权限配置中需要包含3
        UserReadOnlyPageInfo info = uaMapper.getPageInfoByKey(vo.getKey());
        TableDataInfo rspData = new TableDataInfo();
        if(StringUtils.isNull(info)){
            rspData.setCode(HttpStatus.ERROR);
            rspData.setRows(new ArrayList<>());
            rspData.setMsg("key不存在");
            rspData.setTotal(0);
            rspData.setTotalPage(0);
            return rspData;
        }
        boolean check = checkPermission("3", info.getConfig());
        if(!check){
            rspData.setCode(HttpStatus.ERROR);
            rspData.setRows(new ArrayList<>());
            rspData.setMsg("鉴权失败");
            rspData.setTotal(0);
            rspData.setTotalPage(0);
            return rspData;
        }

        UserAccountDto dto = uaMapper.getAccountByUserAccountAndCoin(info.getUser(), info.getAccount(), info.getCoin());
        if(StringUtils.isNull(dto)){
            rspData.setCode(HttpStatus.ERROR);
            rspData.setRows(new ArrayList<>());
            rspData.setMsg("未获取到挖矿账户信息");
            rspData.setTotal(0);
            rspData.setTotalPage(0);
            return rspData;
        }

        List<ProfitDto> inPendingList = distributionMapper.getDayPendingByMinerAccount(info.getCoin(),info.getAccount());
        List<OutcomeDto> pendingList = inPendingList.stream().map(e -> {
            OutcomeDto dto1 = new OutcomeDto();
            dto1.setDate(e.getDate());
            dto1.setAmount(e.getProfit().doubleValue());
            dto1.setCoin(info.getCoin());
            dto1.setStatus(e.getStatus());
            return dto1;
        }).collect(Collectors.toList());
        List<OutcomeDto> list = distributionMapper.getOutcomeListByCoinAndUser(info.getCoin(),info.getAccount());

        list.addAll(pendingList);
        Collections.sort(list,Comparator.comparing(OutcomeDto::getDate,(o1,o2) -> o2.compareTo(o1)));

        rspData.setCode(HttpStatus.SUCCESS);
        rspData.setMsg("查询成功");
        PageInfo pageInfo = new PageInfo(list);
        rspData.setTotal(pageInfo.getTotal());
        rspData.setTotalPage(pageInfo.getPages());

        int page = vo.getPage() < 1 ? 1 : vo.getPage();
        int limit = vo.getLimit() < 1 ? 1 : vo.getLimit();

        List<OutcomeDto> pageList = list.stream().skip((page - 1) * limit).limit(limit).collect(Collectors.toList());
        if("aplh".equals(dto.getCoin())){
            pageList.stream().forEach(e ->{
                e.setAmount(BigDecimal.valueOf(e.getAmount())
                        .divide(BigDecimal.valueOf(1000*1000))
                        .divide(BigDecimal.valueOf(1000*1000))
                        .divide(BigDecimal.valueOf(1000*1000))
                        .setScale(10,BigDecimal.ROUND_HALF_UP).doubleValue());
            });
        }


        rspData.setRows(pageList);

        return rspData;
    }

    public boolean checkPermission(String need,String config) {
        //权限鉴定 通过key鉴定 权限配置中需要包含3
        if (StringUtils.isBlank(config)){
            return false;
        }
        try {
            List<String> list = Arrays.asList(config.split(","));
            return list.stream().anyMatch(e -> need.equals(e));
        }catch (Exception e){
            return false;
        }
    }

    public List<DistributionDto> getDailyDistributionDtoByDataList(List<MinerDataDto> list,String coin){
        PoolUnits poolUnit = (PoolUnits) EnumUtils.get(PoolUnits.class, coin);
        int rate = 1000;
        if(StringUtils.isNotNull(poolUnit)){
            rate = poolUnit.getgRate();
        }
        Map<Integer,Integer> map =new LinkedHashMap<>();
        //设置默认格子
        int interval = 10;
        if (list.size() < 1){
            map.put(0,0);
            map.put(10,0);
            map.put(20,0);
            map.put(30,0);
            map.put(40,0);
            map.put(50,0);
            map.put(60,0);
            map.put(70,0);
            map.put(80,0);
            map.put(90,0);
        }else {
            double min = list.stream().min(Comparator.comparingDouble(MinerDataDto::getMhs24h)).get().getMhs24h();
            double max = list.stream().max(Comparator.comparingDouble(MinerDataDto::getMhs24h)).get().getMhs24h();

            //最大最小值除以10  根据这个值去判断每个格子的Value 固定为10格
            double v = BigDecimal.valueOf(max).subtract(BigDecimal.valueOf(min)).divide(BigDecimal.valueOf(10), 2, BigDecimal.ROUND_HALF_UP).doubleValue();

            //根据上一步计算的值确定格子

            if(v<=10){
                interval=10;
            }else if(v <=100){
                interval = 100;
            }else if(v <=200){
                interval = 200;
            }else if(v <=500){
                interval = 500;
            }else if(v <=1000){
                interval = 1000;
            }else {
                //todo 修改为先进行list进制转换 如MH/s -> GH/s再进行处理
                interval = 10000;
            }

            int temp = (int) (min / interval);
            int start = temp * interval;
            for(int i=0;i<10;i++){
                int key = start + (i*interval);
                map.put(key,0);
            }

            int gRate = rate;
            int dataInterval = interval;
            list.stream().forEach(e -> {
                int a = (int) (e.getMhs24h() / dataInterval) * dataInterval;
                if(map.containsKey(a)){
                    map.replace(a,map.get(a)+1);
                }else {
                    map.put(a,1);
                }
            });
        }


        int finalInterval = interval;
        List<DistributionDto> returnList = map.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey()))
                .map(e -> {
                    DistributionDto  dto = new DistributionDto();
                    dto.setLow(e.getKey());
                    dto.setHigh(e.getKey()+ finalInterval);
                    dto.setCount(e.getValue());
                    return dto;
                }).collect(Collectors.toList());

        return returnList;
    }

    public int getTotalPage(int total,int size){
        if(size == 0){
            return  0;
        }
        int page = total /size;
        if(total % size != 0){
            page++;
        }
        return page;
    }
}
