
package com.tmerclub.cloud.flow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson2.JSON;
import com.tmerclub.cloud.common.cache.constant.CacheNames;
import com.tmerclub.cloud.common.cache.util.RedisUtil;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.util.Arith;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.ExcelUtil;
import com.tmerclub.cloud.common.util.PriceUtil;
import com.tmerclub.cloud.flow.bo.FlowSystemBO;
import com.tmerclub.cloud.flow.constant.FlowDateTypeEnum;
import com.tmerclub.cloud.flow.constant.SystemTypeEnum;
import com.tmerclub.cloud.flow.dto.FlowAnalysisDTO;
import com.tmerclub.cloud.flow.service.FlowUserAnalysisService;
import com.tmerclub.cloud.flow.service.FlowUserMongoService;
import com.tmerclub.cloud.flow.service.SystemAnalyseMongoService;
import com.tmerclub.cloud.flow.util.FlowArithUtil;
import com.tmerclub.cloud.flow.util.FlowUtil;
import com.tmerclub.cloud.flow.vo.*;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * 流量分析—用户访问数据
 *
 * @author YXF
 * @date 2021-05-21 15:25:19
 */
@Service
public class FlowUserAnalysisServiceImpl implements FlowUserAnalysisService {

    private static final Logger log = LoggerFactory.getLogger(FlowUserAnalysisServiceImpl.class);
    @Autowired
    private FlowUserMongoService flowUserMongoService;
    @Autowired
    private SystemAnalyseMongoService systemAnalyseMongoService;

    private static final String[] SYSTEM_TYPE = {"全部", "PC", "H5", "微信小程序", "安卓", "IOS"};

    @Override
    public FlowAnalysisVO getFlowAnalysisData(FlowAnalysisDTO flowAnalysisDTO) {
        FlowUtil.handleTime(flowAnalysisDTO);
        //获取用于跟新数据比较的旧数据的开始时间
        List<FlowSystemBO> flowSystemList;
        Date beginTime = FlowUtil.previousTime(flowAnalysisDTO.getStartTime(), flowAnalysisDTO.getDateType());
        // 今日实时
        if (Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value())) {
            // 统计当日的数据
            flowSystemList = systemAnalyseMongoService.listRealTimeStatistics(null, null, flowAnalysisDTO.getSystemType());
        }
        // 日、周、月、近7天、近30天
        else {
            flowSystemList = systemAnalyseMongoService.listFlowSystemByDateTypeAndSystemType(
                    beginTime,
                    flowAnalysisDTO.getEndTime(),
                    flowAnalysisDTO.getDateType(),
                    flowAnalysisDTO.getSystemType());
        }

        // 今日实时、近7天、近30天 的旧数据，用于计算上升、下降率 (有缓存)
        if (Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value()) || FlowDateTypeEnum.nearlyTime(flowAnalysisDTO.getDateType())) {
            // 根据时间类型和beginTime进行缓存，缓存时间为一天
            this.loadOldFlowSystem(flowAnalysisDTO, flowSystemList, beginTime);
        }

        if (CollUtil.isEmpty(flowSystemList)) {
            flowSystemList.add(new FlowSystemBO());
            flowSystemList.add(new FlowSystemBO());
            flowSystemList.get(0).setCreateTime(flowAnalysisDTO.getStartTime());
            flowSystemList.get(1).setCreateTime(beginTime);
        } else if (flowSystemList.size() == 1) {
            flowSystemList.add(new FlowSystemBO());
            // 如果数据库中的数据的开始时间大于等于查询的开始时间，那么就将查询的开始时间赋值给数据库中的数据的开始时间
            if (flowSystemList.get(0).getCreateTime().getTime() >= flowAnalysisDTO.getStartTime().getTime()) {
                flowSystemList.get(1).setCreateTime(beginTime);
            } else {
                flowSystemList.get(1).setCreateTime(flowAnalysisDTO.getStartTime());
            }
        }

        // 统计数据库中无法获取的数据
        List<FlowAnalysisVO> flowAnalysisList = this.dbDataToFlowAnalysis(flowSystemList);
        // 统计数据增减的比例
        return calculateProportion(flowAnalysisList);
    }

    @Override
    public List<FlowAnalysisVO> flowTrend(FlowAnalysisDTO flowAnalysisDTO) {
        FlowUtil.handleTime(flowAnalysisDTO);

        // 统计数据的最开始时间
        int offset = 0;
        if (Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value()) ||
                Objects.equals(FlowDateTypeEnum.DAY.value(), flowAnalysisDTO.getDateType())) {
            // 今日实时
            offset = 29;
        } else if (FlowDateTypeEnum.weekOrMonth(flowAnalysisDTO.getDateType())) {
            // 周、月
            offset = 11;
        }

        Date beginTime;

        if (offset == 0) {
            beginTime = flowAnalysisDTO.getStartTime();
        } else {
            beginTime = FlowUtil.previousTime(flowAnalysisDTO.getStartTime(), flowAnalysisDTO.getDateType(), offset);
        }

        List<FlowSystemBO> flowSystemList = new ArrayList<>();

        // 实时数据 - 使用当天数据进行统计
        if (Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value())) {
            List<FlowSystemBO> flowSystemBOList = systemAnalyseMongoService.listRealTimeStatistics(null,null, flowAnalysisDTO.getSystemType());
            for (FlowSystemBO flowSystemBO : flowSystemBOList) {
                // 统计当日的数据
                if (Objects.equals(flowSystemBO.getSystemType(), flowAnalysisDTO.getSystemType())) {
                    flowSystemList.add(flowSystemBO);
                }
            }
        }
        int dateType;
        if (FlowDateTypeEnum.nearlyTime(flowAnalysisDTO.getDateType()) || Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value())) {
            dateType = FlowDateTypeEnum.DAY.value();
        } else {
            dateType = flowAnalysisDTO.getDateType();
        }

        // 查询前30天的数据
        flowSystemList.addAll(systemAnalyseMongoService.listFlowSystemByDateTypeAndSystemType(
                beginTime,
                flowAnalysisDTO.getEndTime(),
                dateType,
                flowAnalysisDTO.getSystemType()));

        Map<Long, FlowSystemBO> map = flowSystemList.stream().collect(Collectors.toMap(flowSystemBO -> flowSystemBO.getCreateTime().getTime(), f -> f));

        long endTime = flowAnalysisDTO.getEndTime().getTime();
        flowSystemList.clear();

        // 循环生成数据
        while (beginTime.getTime() < endTime){
            FlowSystemBO flowSystemBO = map.get(beginTime.getTime());
            if (Objects.isNull(flowSystemBO)){
                flowSystemBO = new FlowSystemBO();
            }
            flowSystemBO.setCreateTime(beginTime);
            beginTime = getNextTime(beginTime, flowAnalysisDTO.getDateType());
            flowSystemList.add(flowSystemBO);
        }

        List<FlowAnalysisVO> flowAnalysisList = dbDataToFlowAnalysis(flowSystemList);
        for (FlowAnalysisVO flowAnalysisVO : flowAnalysisList) {
            flowAnalysisVO.setSystemType(flowAnalysisDTO.getSystemType());
            flowAnalysisVO.setDateTime(FlowUtil.formatOnDateType(flowAnalysisVO.getDay(), flowAnalysisDTO.getDateType()));
        }
        return flowAnalysisList;
    }

    @Override
    public List<FlowAnalysisVO> flowSour(FlowAnalysisDTO flowAnalysisDTO) {
        FlowUtil.handleTime(flowAnalysisDTO);
        // 获取数据
        List<FlowSystemBO> flowSystemList = new ArrayList<>();

        // 实时数据 - 使用当天数据进行统计
        if (Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value())) {
            flowSystemList = systemAnalyseMongoService.listRealTimeStatistics(null, null, flowAnalysisDTO.getSystemType());
        }

        // 日、周、月、近7天、近30天 - 直接从flow_system中查询已统计的数据
        else {
            // 查询flow_system表中指定日期的所有数据的数据
            flowSystemList = systemAnalyseMongoService.listFlowSystemByDateTypeAndSystemType(
                    flowAnalysisDTO.getStartTime(),
                    flowAnalysisDTO.getEndTime(),
                    flowAnalysisDTO.getDateType(),
                    null
            );
        }
        List<FlowAnalysisVO> flowSourList = this.dbDataToFlowAnalysis(flowSystemList);
        return flowSourList;
    }

    @Override
    public List<FlowAnalysisDataExcelVO> listFlowAnalysisDataExcel(FlowAnalysisDTO flowAnalysisDTO) {
        FlowUtil.handleTime(flowAnalysisDTO);
        List<FlowSystemBO> flowSystemList;
        Date beginTime = FlowUtil.previousTime(flowAnalysisDTO.getStartTime(), flowAnalysisDTO.getDateType());
        // 今日实时
        if (Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value())) {
            // 统计当日的数据
            flowSystemList = systemAnalyseMongoService.listRealTimeStatistics(flowAnalysisDTO.getStartTime(), flowAnalysisDTO.getEndTime(), null);
        }
        // 日、周、月、近7天、近30天
        else {
            // 统计当日的数据
            flowSystemList = systemAnalyseMongoService.listFlowSystemByDateTypeAndSystemType(
                    beginTime,
                    flowAnalysisDTO.getEndTime(),
                    flowAnalysisDTO.getDateType(),
                    null);
        }
        // list类型更改为ArrayList
        flowSystemList = new ArrayList<>(flowSystemList);

        List<Integer> systemTypes = flowSystemList.stream().map(FlowSystemBO::getSystemType).collect(Collectors.toList());
        for (SystemTypeEnum systemTypeEnum : SystemTypeEnum.values()) {
            if (systemTypes.contains(systemTypeEnum.value())) {
                continue;
            }
            FlowSystemBO flowSystemBO = new FlowSystemBO();
            flowSystemBO.setSystemType(systemTypeEnum.value());
            flowSystemList.add(flowSystemBO);
        }

        // 统计数据增减的比例
        List<FlowAnalysisVO> flowAnalysisList = this.dbDataToFlowAnalysis(flowSystemList);
        calculateProportion(flowAnalysisList);

        Map<Integer, FlowAnalysisVO> map = flowAnalysisList.stream().collect(Collectors.toMap(FlowAnalysisVO::getSystemType, f -> f));

        FlowAnalysisVO all = map.get(SystemTypeEnum.ALL.value());
        FlowAnalysisVO pc = map.get(SystemTypeEnum.PC.value());
        FlowAnalysisVO h5 = map.get(SystemTypeEnum.H5.value());
        FlowAnalysisVO wx = map.get(SystemTypeEnum.APPLETS.value());
        FlowAnalysisVO android = map.get(SystemTypeEnum.ANDROID.value());
        FlowAnalysisVO ios = map.get(SystemTypeEnum.IOS.value());

        List<FlowAnalysisDataExcelVO> resList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        getFlowAnalysisDataExcel("访客数", all.getUserNums().toString(), pc.getUserNums().toString(), h5.getUserNums().toString(), wx.getUserNums().toString(), android.getUserNums().toString(), ios.getUserNums().toString(), resList);
        getFlowAnalysisDataExcel("浏览量", all.getVisitNums().toString(), pc.getVisitNums().toString(), h5.getVisitNums().toString(), wx.getVisitNums().toString(), android.getVisitNums().toString(), ios.getVisitNums().toString(), resList);
        getFlowAnalysisDataExcel("人均浏览量", all.getAverageVisitNums().toString(), pc.getAverageVisitNums().toString(), h5.getAverageVisitNums().toString(), wx.getAverageVisitNums().toString(), android.getAverageVisitNums().toString(), ios.getAverageVisitNums().toString(), resList);
        getFlowAnalysisDataExcel("支付人数", all.getPayUser().toString(), pc.getPayUser().toString(), h5.getPayUser().toString(), wx.getPayUser().toString(), android.getPayUser().toString(), ios.getPayUser().toString(), resList);
        getFlowAnalysisDataExcel("下单人数", all.getPlaceOrderUser().toString(), pc.getPlaceOrderUser().toString(), h5.getPlaceOrderUser().toString(), wx.getPlaceOrderUser().toString(), android.getPlaceOrderUser().toString(), ios.getPlaceOrderUser().toString(), resList);
        if (!Objects.equals(flowAnalysisDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value())) {
            getFlowAnalysisDataExcel("新访客数", all.getNewUserNums().toString(), pc.getNewUserNums().toString(), h5.getNewUserNums().toString(), wx.getNewUserNums().toString(), android.getNewUserNums().toString(), ios.getNewUserNums().toString(), resList);
            getFlowAnalysisDataExcel("跳失率", all.getLossUserRate().toString(), pc.getLossUserRate().toString(), h5.getLossUserRate().toString(), wx.getLossUserRate().toString(), android.getLossUserRate().toString(), ios.getLossUserRate().toString(), resList);
            getFlowAnalysisDataExcel("访问-支付转化率", all.getPayRate().toString(), pc.getPayRate().toString(), h5.getPayRate().toString(), wx.getPayRate().toString(), android.getPayRate().toString(), ios.getPayRate().toString(), resList);
            getFlowAnalysisDataExcel("访问-下单转化率", all.getPlaceOrderRate().toString(), pc.getPlaceOrderRate().toString(), h5.getPlaceOrderRate().toString(), wx.getPlaceOrderRate().toString(), android.getPlaceOrderRate().toString(), ios.getPlaceOrderRate().toString(), resList);
        }
        return resList;
    }

    @Override
    public void exportFlowTrend(HttpServletResponse response, FlowAnalysisDTO flowAnalysisDTO) {
        FlowUtil.handleTime(flowAnalysisDTO);
        Map<Integer, String[]> map = new HashMap<>(8);
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            ExcelWriterBuilder excelWriterMerge = ExcelUtil.getExcelWriterMerge(response, FlowTrendExcelVO.EXCEL_NAME, FlowTrendExcelVO.MERGE_ROW_INDEX, FlowTrendExcelVO.MERGE_COLUMN_INDEX);
            excelWriter = ExcelUtil.getExcelModel(excelWriterMerge, map, Constant.START_ROW).build();
            // 业务代码
            for (SystemTypeEnum systemTypeEnum : SystemTypeEnum.values()) {
                flowAnalysisDTO.setSystemType(systemTypeEnum.value());
                // 进行写入操作
                List<FlowAnalysisVO> flowAnalysisVOList = this.flowTrend(flowAnalysisDTO);
                List<FlowTrendExcelVO> resList = new ArrayList<>(Constant.INITIAL_CAPACITY);
                for (FlowAnalysisVO flowAnalysisVO : flowAnalysisVOList) {
                    FlowTrendExcelVO date = new FlowTrendExcelVO();
                    date.setDate(flowAnalysisVO.getDateTime());
                    date.setUserNums(flowAnalysisVO.getUserNums());
                    date.setNewUserNums(flowAnalysisVO.getNewUserNums());
                    date.setVisitNums(flowAnalysisVO.getVisitNums());
                    date.setAverageVisitNums(flowAnalysisVO.getAverageVisitNums());
                    date.setPlusShopCart(flowAnalysisVO.getPlusShopCart());
                    date.setLossUserRate(flowAnalysisVO.getLossUserRate());
                    date.setPlaceOrderUser(flowAnalysisVO.getPlaceOrderUser());
                    date.setPayUser(flowAnalysisVO.getPayUser());
                    resList.add(date);
                }
                if (CollUtil.isNotEmpty(resList)) {
                    resList.add(new FlowTrendExcelVO());
                }
                WriteSheet sheetWriter = EasyExcel.writerSheet(systemTypeEnum.value()).head(FlowTrendExcelVO.class).build();
                excelWriter.write(resList, sheetWriter);
            }
        } catch (Exception e) {
            log.error("导出流量趋势分析失败", e);
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

    }

    @Override
    public List<FlowSourDataExcelVO> listFlowSourDataExcel(FlowAnalysisDTO flowAnalysisDTO) {
        List<FlowAnalysisVO> flowAnalysisVOList = flowSour(flowAnalysisDTO);
        Map<Integer, FlowAnalysisVO> flowAnalysisMap = flowAnalysisVOList.stream().collect(Collectors.toMap(FlowAnalysisVO::getSystemType, f -> f));
        FlowAnalysisVO pc = flowAnalysisMap.containsKey(SystemTypeEnum.PC.value()) ? flowAnalysisMap.get(SystemTypeEnum.PC.value()) : getNewFlowAnalysis();
        FlowAnalysisVO h5 = flowAnalysisMap.containsKey(SystemTypeEnum.H5.value()) ? flowAnalysisMap.get(SystemTypeEnum.H5.value()) : getNewFlowAnalysis();
        FlowAnalysisVO weChat = flowAnalysisMap.containsKey(SystemTypeEnum.APPLETS.value()) ? flowAnalysisMap.get(SystemTypeEnum.APPLETS.value()) : getNewFlowAnalysis();
        FlowAnalysisVO android = flowAnalysisMap.containsKey(SystemTypeEnum.ANDROID.value()) ? flowAnalysisMap.get(SystemTypeEnum.ANDROID.value()) : getNewFlowAnalysis();
        FlowAnalysisVO ios = flowAnalysisMap.containsKey(SystemTypeEnum.IOS.value()) ? flowAnalysisMap.get(SystemTypeEnum.IOS.value()) : getNewFlowAnalysis();
        int userAll = pc.getUserNums() + h5.getUserNums() + weChat.getUserNums() + android.getUserNums() + ios.getUserNums();
        List<FlowSourDataExcelVO> resList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        getFlowSourDataExcel("访客数占比", FlowArithUtil.getRatio(pc.getUserNums(), userAll, 4).toString(), FlowArithUtil.getRatio(h5.getUserNums(), userAll, 4).toString(), FlowArithUtil.getRatio(weChat.getUserNums(), userAll, 4).toString(), FlowArithUtil.getRatio(android.getUserNums(), userAll, 4).toString(), FlowArithUtil.getRatio(ios.getUserNums(), userAll, 4).toString(), resList);
        getFlowSourDataExcel("访客数", pc.getUserNums().toString(), h5.getUserNums().toString(), weChat.getUserNums().toString(), android.getUserNums().toString(), ios.getUserNums().toString(), resList);
        getFlowSourDataExcel("UV价值", pc.getUvAmount().toString(), h5.getUvAmount().toString(), weChat.getUvAmount().toString(), android.getUvAmount().toString(), ios.getUvAmount().toString(), resList);
        getFlowSourDataExcel("加购人数", pc.getPlusShopCartUser().toString(), h5.getPlusShopCartUser().toString(), weChat.getPlusShopCartUser().toString(), android.getPlusShopCartUser().toString(), ios.getPlusShopCartUser().toString(), resList);
        getFlowSourDataExcel("下单人数", pc.getPlaceOrderUser().toString(), h5.getPlaceOrderUser().toString(), weChat.getPlaceOrderUser().toString(), android.getPlaceOrderUser().toString(), ios.getPlaceOrderUser().toString(), resList);
        getFlowSourDataExcel("下单金额", PriceUtil.toDecimalPrice(pc.getPlaceOrderAmount()).toString(), PriceUtil.toDecimalPrice(h5.getPlaceOrderAmount()).toString(), PriceUtil.toDecimalPrice(weChat.getPlaceOrderAmount()).toString(), PriceUtil.toDecimalPrice(android.getPlaceOrderAmount()).toString(), PriceUtil.toDecimalPrice(ios.getPlaceOrderAmount()).toString(), resList);
        getFlowSourDataExcel("访问-下单转化率", pc.getPlaceOrderRate().toString(), h5.getPlaceOrderRate().toString(), weChat.getPlaceOrderRate().toString(), android.getPlaceOrderRate().toString(), ios.getPlaceOrderRate().toString(), resList);
        getFlowSourDataExcel("支付人数", pc.getPayUser().toString(), h5.getPayUser().toString(), weChat.getPayUser().toString(), android.getPayUser().toString(), ios.getPayUser().toString(), resList);
        getFlowSourDataExcel("支付金额", PriceUtil.toDecimalPrice(pc.getPayAmount()).toString(), PriceUtil.toDecimalPrice(h5.getPayAmount()).toString(), PriceUtil.toDecimalPrice(weChat.getPayAmount()).toString(), PriceUtil.toDecimalPrice(android.getPayAmount()).toString(), PriceUtil.toDecimalPrice(ios.getPayAmount()).toString(), resList);
        getFlowSourDataExcel("访问-支付转化率", pc.getPayRate().toString(), h5.getPayRate().toString(), weChat.getPayRate().toString(), android.getPayRate().toString(), ios.getPayRate().toString(), resList);
        getFlowSourDataExcel("客单价", PriceUtil.toDecimalPrice(pc.getCustomerAmount()).toString(), PriceUtil.toDecimalPrice(h5.getCustomerAmount()).toString(), PriceUtil.toDecimalPrice(weChat.getCustomerAmount()).toString(), PriceUtil.toDecimalPrice(android.getCustomerAmount()).toString(), PriceUtil.toDecimalPrice(ios.getCustomerAmount()).toString(), resList);
        return resList;
    }

    private void loadOldFlowSystem(FlowAnalysisDTO flowAnalysisDTO, List<FlowSystemBO> flowSystemList, Date beginTime) {
        String key = CacheNames.FLOW_SYSTEM_ALL + CacheNames.UNION +
                flowAnalysisDTO.getDateType() + CacheNames.UNION +
                FlowUtil.dateToString(beginTime);
        String data = RedisUtil.get(key);
        FlowSystemBO flowSystemBO;
        // 缓存中有数据，直接返回
        if (StrUtil.isNotBlank(data)) {
            flowSystemBO = JSON.parseObject(data, FlowSystemBO.class);
            flowSystemList.add(flowSystemBO);
            return;
        }
        // 没有数据就先统计，然后放入缓存
        List<FlowSystemBO> flowSystems;
        if (true) {
            // 查询今日实时前一天（昨天）的数据
            flowSystems = systemAnalyseMongoService.listFlowSystemByDateTypeAndSystemType(
                    beginTime,
                    flowAnalysisDTO.getStartTime(),
                    flowAnalysisDTO.getDateType(),
                    flowAnalysisDTO.getSystemType());

        } else {
            // 统计7天、30天的旧数据（8-14或者31-60天之间的数据）
            flowSystems = systemAnalyseMongoService.statisticsSystemTypeListOfTime(
                    beginTime,
                    flowAnalysisDTO.getStartTime(),
                    flowAnalysisDTO.getSystemType());
        }
        if (CollUtil.isNotEmpty(flowSystems)) {
            flowSystemBO = flowSystems.get(0);
        } else  {
            flowSystemBO = new FlowSystemBO();
        }

        flowSystemBO.setCreateTime(beginTime);
        flowSystemBO.setDateType(flowAnalysisDTO.getDateType());
        RedisUtil.set(key, JSON.toJSONString(flowSystemBO), Constant.DAY_SECOND);
        flowSystemList.add(flowSystemBO);
    }

    private void getFlowSourDataExcel(String type, String pc, String h5, String weChat, String android, String ios, List<FlowSourDataExcelVO> resList) {
        FlowSourDataExcelVO data = new FlowSourDataExcelVO();
        data.setType(type);
        data.setPc(pc);
        data.setH5(h5);
        data.setWx(weChat);
        data.setAndroid(android);
        data.setIos(ios);
        resList.add(data);
    }

    private void getFlowAnalysisDataExcel(String type, String all, String pc, String h5, String wx, String android, String ios, List<FlowAnalysisDataExcelVO> resList) {
        FlowAnalysisDataExcelVO data = new FlowAnalysisDataExcelVO();
        data.setType(type);
        data.setAll(all);
        data.setPc(pc);
        data.setH5(h5);
        data.setWx(wx);
        data.setAndroid(android);
        data.setIos(ios);
        resList.add(data);
    }

    /**
     * 处理数据库中无法获取的数据
     * @param flowSystemList
     */
    private List<FlowAnalysisVO> dbDataToFlowAnalysis(List<FlowSystemBO> flowSystemList) {

        List<FlowAnalysisVO> flowAnalysisList = new ArrayList<>();
        for (FlowSystemBO flowSystemBO : flowSystemList) {
            FlowAnalysisVO flowAnalysis = BeanUtil.map(flowSystemBO, FlowAnalysisVO.class);
            flowAnalysis.setDay(flowSystemBO.getCreateTime());
            flowAnalysis.setUserNums(flowSystemBO.getVisitUser());
            if (Objects.isNull(flowSystemBO.getVisitUser())) {
                flowAnalysis.setUserNums(0);
            }
            flowAnalysis.setNewUserNums(flowSystemBO.getNewVisitUser());
            if (Objects.isNull(flowSystemBO.getNewVisitUser())) {
                flowAnalysis.setNewUserNums(0);
            }
            flowAnalysis.setVisitNums(flowSystemBO.getVisit());
            if (Objects.isNull(flowSystemBO.getVisit())) {
                flowAnalysis.setVisitNums(0);
            }
            flowAnalysis.setAverageVisitNums(0D);
            if (Objects.isNull(flowSystemBO.getLossUser())) {
                flowAnalysis.setLossUser(0);
            }
            if (Objects.isNull(flowSystemBO.getPlusShopCartUser())) {
                flowAnalysis.setPlusShopCartUser(0);
            }
            if (Objects.isNull(flowSystemBO.getPlaceOrderUser())) {
                flowAnalysis.setPlaceOrderUser(0);
            }
            if (Objects.isNull(flowSystemBO.getPayUser())) {
                flowAnalysis.setPayUser(0);
            }
            if (Objects.isNull(flowSystemBO.getPlaceOrderAmount())) {
                flowAnalysis.setPlaceOrderAmount(Constant.ZERO_LONG);
            }
            if (Objects.isNull(flowSystemBO.getPayAmount())) {
                flowAnalysis.setPayAmount(Constant.ZERO_LONG);
            }
            flowAnalysis.setAverageVisitNums(FlowArithUtil.getAverage(flowAnalysis.getVisitNums(),flowAnalysis.getUserNums()));
            flowAnalysis.setLossUserRate(FlowArithUtil.getRatio(flowAnalysis.getLossUser(), flowAnalysis.getUserNums()));
            flowAnalysis.setPayRate(FlowArithUtil.getRatio(flowAnalysis.getPayUser(),flowAnalysis.getUserNums()));
            flowAnalysis.setPlaceOrderRate(FlowArithUtil.getRatio(flowAnalysis.getPlaceOrderUser(),flowAnalysis.getUserNums()));
            flowAnalysis.setUvAmount(FlowArithUtil.getAnalysisAmount(flowAnalysis.getPayAmount(),flowAnalysis.getUserNums()));
            flowAnalysis.setCustomerAmount(FlowArithUtil.getAnalysisAmount(flowAnalysis.getPayAmount(),flowAnalysis.getPayUser()));
            flowAnalysisList.add(flowAnalysis);
        }
        return flowAnalysisList;
    }

    /**
     * 获取下一次循环的开始时间，以及设置后台显示的时间字符串
     * @param startTime 开始时间
     * @param type 时间类型
     * @return
     */
    private Date getNextTime(Date startTime,Integer type){
        if (type.equals(FlowDateTypeEnum.WEEK.value())) {
            //时间类型 2:自然周
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(startTime);
            //设置周一为每周的开始日期
            calendar.setFirstDayOfWeek(Calendar.MONDAY);
            startTime = DateUtil.beginOfWeek(DateUtil.offsetWeek(startTime,1));
        }else if (type.equals(FlowDateTypeEnum.MONTH.value())){
            //时间类型 3:自然月
            startTime = DateUtil.beginOfMonth(DateUtil.offsetMonth(startTime,1));
        }else {
            startTime = DateUtil.offsetDay(startTime, 1);
        }
        return startTime;
    }

    /**
     * 统计数据的增减比例
     *
     * @param flowAnalysisVOList
     */
    private FlowAnalysisVO calculateProportion(List<FlowAnalysisVO> flowAnalysisVOList) {
        FlowAnalysisVO flowAnalysisVO;
        FlowAnalysisVO oldFlowAnalysisVO;
        if (flowAnalysisVOList.get(0).getDay().getTime() > flowAnalysisVOList.get(1).getDay().getTime()) {
            flowAnalysisVO = flowAnalysisVOList.get(0);
            oldFlowAnalysisVO = flowAnalysisVOList.get(1);
        } else {
            flowAnalysisVO = flowAnalysisVOList.get(1);
            oldFlowAnalysisVO = flowAnalysisVOList.get(0);
        }
        FlowAnalysisRatioVO ratio = new FlowAnalysisRatioVO();
        flowAnalysisVO.setRatio(ratio);
        //获取比例大小
        ratio.setUserNumsRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getUserNums(), oldFlowAnalysisVO.getUserNums()));
        ratio.setNewUserNumsRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getNewUserNums(), oldFlowAnalysisVO.getNewUserNums()));
        ratio.setLossUserRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getLossUserRate(), oldFlowAnalysisVO.getLossUserRate()));
        ratio.setVisitNumsRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getVisitNums(), oldFlowAnalysisVO.getVisitNums()));
        ratio.setAverageVisitNumsRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getAverageVisitNums(), oldFlowAnalysisVO.getAverageVisitNums()));
        ratio.setPlusShopCartUserRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getPlusShopCartUser(), oldFlowAnalysisVO.getPlusShopCartUser()));
        ratio.setPlaceOrderRatio(Arith.sub(flowAnalysisVO.getPlaceOrderRate(), oldFlowAnalysisVO.getPlaceOrderRate()));
        ratio.setPlaceOrderUserRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getPlaceOrderUser(), oldFlowAnalysisVO.getPayUser()));
        ratio.setPayRatio(Arith.sub(flowAnalysisVO.getPayRate(), oldFlowAnalysisVO.getPayRate()));
        ratio.setPayUserRatio(FlowArithUtil.getIncreaseRatio(flowAnalysisVO.getPayUser(), oldFlowAnalysisVO.getPayUser()));
        return flowAnalysisVO;
    }

    /**
     * 获取用户比较上一段统计时间的开始时间
     *
     * @param timeType  时间类型
     * @param startTime 当前统计数据的开始时间
     * @return 上一段统计时间的开始时间
     */
    public Date getOldStartTime(Integer timeType, Date startTime) {
        FlowDateTypeEnum flowDateTypeEnum = FlowDateTypeEnum.instance(timeType);
        return DateUtil.offsetDay(startTime, -flowDateTypeEnum.getNum());
    }

    private FlowAnalysisVO getNewFlowAnalysis() {
        FlowAnalysisVO flowAnalysis = new FlowAnalysisVO();
        flowAnalysis.setUserNums(0);
        flowAnalysis.setNewUserNums(0);
        flowAnalysis.setVisitNums(0);
        flowAnalysis.setAverageVisitNums(0.0D);
        flowAnalysis.setLossUser(0);
        flowAnalysis.setPlusShopCart(0);
        flowAnalysis.setPlusShopCartUser(0);
        flowAnalysis.setPlaceOrderUser(0);
        flowAnalysis.setPayUser(0);
        flowAnalysis.setPlaceOrderAmount(Constant.ZERO_LONG);
        flowAnalysis.setPayAmount(Constant.ZERO_LONG);
        flowAnalysis.setLossUserRate(0.0D);
        flowAnalysis.setAverageVisitNums(0.0D);
        flowAnalysis.setPayRate(0.0D);
        flowAnalysis.setUvAmount(Constant.ZERO_LONG);
        flowAnalysis.setPlaceOrderRate(0.0D);
        flowAnalysis.setCustomerAmount(Constant.ZERO_LONG);
        return flowAnalysis;
    }
}
