
package com.tmerclub.cloud.order.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.constant.EsRenovationProductSortEnum;
import com.tmerclub.cloud.api.multishop.bo.ShopSimpleBO;
import com.tmerclub.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.tmerclub.cloud.api.order.constant.OrderStatus;
import com.tmerclub.cloud.api.order.constant.TimeTypeEnum;
import com.tmerclub.cloud.api.order.dto.CustomerRetainedDTO;
import com.tmerclub.cloud.api.order.dto.FlowOrderItemDTO;
import com.tmerclub.cloud.api.order.dto.FormExcelDTO;
import com.tmerclub.cloud.api.order.vo.*;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
import com.tmerclub.cloud.api.user.bo.UserOrderStatisticBO;
import com.tmerclub.cloud.api.user.bo.UserOrderStatisticListBO;
import com.tmerclub.cloud.api.user.dto.FinanceDetailDTO;
import com.tmerclub.cloud.api.user.dto.MemberReqDTO;
import com.tmerclub.cloud.api.user.dto.ShopCustomerReqDTO;
import com.tmerclub.cloud.api.user.vo.FinanceDetailVO;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.util.PageUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.i18n.I18nMessage;
import com.tmerclub.cloud.common.i18n.LanguageEnum;
import com.tmerclub.cloud.common.mongodb.bo.order.MongoOrderBO;
import com.tmerclub.cloud.common.mongodb.bo.order.MongoOrderItemBO;
import com.tmerclub.cloud.common.order.constant.*;
import com.tmerclub.cloud.common.order.dto.OrderSearchDTO;
import com.tmerclub.cloud.common.order.vo.OrderActivityVO;
import com.tmerclub.cloud.common.order.vo.OrderCountVO;
import com.tmerclub.cloud.common.order.vo.UserOrderStatisticVO;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.util.Arith;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.DateUtils;
import com.tmerclub.cloud.order.constant.FinanceBizType;
import com.tmerclub.cloud.order.service.OrderAllinpayService;
import com.tmerclub.cloud.order.vo.HotProductVO;
import com.tmerclub.cloud.order.vo.OrderOverviewVO;
import com.tmerclub.cloud.order.vo.SupplierOrderOverviewVO;
import com.tmerclub.cloud.order.vo.mongo.*;
import com.tmerclub.cloud.order.vo.mongo.*;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

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

/**
 * 订单统计
 *
 * @author tmerclub
 * @date 2020/12/07
 */
@Component
public class MongoOrderManager {
    private static final Logger logger = LoggerFactory.getLogger(MongoOrderManager.class);

    @Autowired
    private MongoTemplate mongoTemplate;
    @DubboReference
    private ShopDetailFeignClient shopDetailFeignClient;
    @Autowired
    private OrderAllinpayService orderAllinpayService;

    public void save(MongoOrderBO mongoOrderBO) {
        mongoTemplate.save(mongoOrderBO);
    }

    public OrderOverviewVO getOrderOverviewInfoByShopId(Long shopId, Date startTime, Date endTime) {

        // 如果是预售订单，且未付尾款，订单金额为用户支付的定金，其他请求才是订单实际支付金额
        ProjectionOperation initProjectOperation = Aggregation.project("userId","platformAmount")
                .and(ConditionalOperators.when(Criteria.where("status").is(OrderStatus.WAIT_BALANCE.value())).then("$depositAmount").otherwise("$actualTotal")).as("actualTotal");

        // 先根据用户进行分组，统计
        GroupOperation userGroupOperation = Aggregation.group("userId")
                .count().as("payOrderCount")
                .sum("actualTotal").as("actualTotal")
                .sum("platformAmount").as("platformAmount");
        GroupOperation groupOperation = Aggregation.group()
                .count().as("payUserCount")
                .sum("payOrderCount").as("payOrderCount")
                .sum("actualTotal").as("actualTotal")
                .sum("platformAmount").as("platformAmount");
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, shopId, startTime, endTime);
        // 只查询已支付的订单
        // 显示refundSpuCount, refundProdName，字段_id取别名spuId
        ProjectionOperation project = Aggregation.project("payUserCount", "payOrderCount", "actualTotal", "platformAmount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        initProjectOperation,
                        userGroupOperation,
                        groupOperation,
                        project
                );
        logger.info("构建getOrderOverviewInfoByShopId()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        OrderOverviewVO orderOverviewVO;
        if (CollUtil.isNotEmpty(list)) {
            MongoOrderStatisticsVO mongoOrderStatisticsVO = list.get(0);
            orderOverviewVO = BeanUtil.map(mongoOrderStatisticsVO, OrderOverviewVO.class);
            if (Objects.isNull(shopId)) {
                orderOverviewVO.setPayActual(Arith.div(mongoOrderStatisticsVO.getActualTotal(), 100, 2));
            } else {
                orderOverviewVO.setPayActual(Arith.div(mongoOrderStatisticsVO.getActualTotal() + mongoOrderStatisticsVO.getPlatformAmount(), 100, 2));
            }
        } else {
            orderOverviewVO = new OrderOverviewVO();
            orderOverviewVO.setPayOrderCount(0);
            orderOverviewVO.setPayUserCount(0);
            orderOverviewVO.setPayActual(0.0D);
        }
        return orderOverviewVO;
    }

    public List<OrderOverviewVO> listActualByHour(Long shopId, Date startTime, Date endTime) {
        // 分组前，先处理下要用于分组的时间
        ProjectionOperation projectionOperation = Aggregation.project("actualTotal", "platformAmount")
                .and(DateOperators.DateToString.dateOf("createTime").toString("%H")).as("groupDate");

        // 分组
        GroupOperation groupOperation = Aggregation.group("groupDate")
                .sum("actualTotal").as("actualTotal")
                .sum("platformAmount").as("platformAmount");
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, shopId, startTime, endTime);

        ProjectionOperation project = Aggregation.project("actualTotal", "platformAmount").and("_id").as("timeData");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        projectionOperation,
                        groupOperation,
                        project
                );
        logger.info("listActualByHour()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<OrderOverviewVO> orderOverviewList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            OrderOverviewVO orderOverviewVO = new OrderOverviewVO();
            orderOverviewVO.setTimeData(mongoOrderStatisticsVO.getTimeData());
            if (Objects.isNull(shopId)) {
                orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal());
            } else {
                orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal() + mongoOrderStatisticsVO.getPlatformAmount());
            }
            orderOverviewList.add(orderOverviewVO);
        }
        return orderOverviewList;
    }

    public List<OrderOverviewVO> listOrderOverviewInfoByShopIdAndDateRange(Long shopId, Date startTime, Date endTime, Integer dayCount) {
        List<Integer> refundStatus = new ArrayList<>(Constant.INITIAL_CAPACITY);
        refundStatus.add(RefundStatusEnum.SUCCEED.value());
        refundStatus.add(RefundStatusEnum.PARTIAL_SUCCESS.value());
        // 分组前，先处理下要用于分组的时间
        ProjectionOperation projectionOperation = Aggregation.project("actualTotal", "platformAmount", "refundStatus")
                .and(ConditionalOperators.when(Criteria.where("refundStatus").in(refundStatus)).then(1).otherwise(0)).as("refundStatus")
                .and(DateOperators.DateToString.dateOf("payTime").toString("%Y-%m-%d")).as("groupDate");
        // 分组
        GroupOperation groupOperation = Aggregation.group("groupDate")
                .count().as("payOrderCount")
                .sum("refundStatus").as("chargebackCount")
                .sum("actualTotal").as("actualTotal")
                .sum("platformAmount").as("platformAmount");
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, shopId, startTime, endTime);

        ProjectionOperation project = Aggregation.project("actualTotal", "platformAmount", "payOrderCount", "chargebackCount")
                .and("_id").as("timeData");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        projectionOperation,
                        groupOperation,
                        project
                );
        logger.info("listOrderOverviewInfoByShopIdAndDateRange()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<OrderOverviewVO> orderOverviewList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            OrderOverviewVO orderOverviewVO = new OrderOverviewVO();
            orderOverviewVO.setTimeData(mongoOrderStatisticsVO.getTimeData());
            orderOverviewVO.setPayOrderCount(mongoOrderStatisticsVO.getPayOrderCount());
            orderOverviewVO.setChargebackCount(mongoOrderStatisticsVO.getChargebackCount());
            if (Objects.isNull(shopId)) {
                orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal());
            } else {
                orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal() + mongoOrderStatisticsVO.getPlatformAmount());
            }
            orderOverviewList.add(orderOverviewVO);
        }
        return orderOverviewList;
    }

    public OrderCountVO getOrderCountOfStatusByShopId(Long shopId) {
        // 分组前，先处理下要用于分组的时间
        ProjectionOperation projectionOperation = Aggregation.project()
                .and(ConditionalOperators.when(new Criteria().orOperator(Criteria.where("status").is(1), Criteria.where("status").is(9)))
                        .then(1).otherwise(0)).as("unPay")
                .and(ConditionalOperators.when(new Criteria().andOperator(Criteria.where("status").is(2), Criteria.where("waitPurchase").ne(1))).then(1).otherwise(0)).as("payed")
                .and(ConditionalOperators.when(Criteria.where("status").is(3)).then(1).otherwise(0)).as("consignment")
                .and(ConditionalOperators.when(Criteria.where("status").is(5)).then(1).otherwise(0)).as("success");
        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("allCount")
                .sum("unPay").as("unPay")
                .sum("payed").as("payed")
                .sum("consignment").as("consignment")
                .sum("success").as("success");

        ProjectionOperation project = Aggregation.project("actualTotal", "platformAmount", "payOrderCount", "chargebackCount").and("_id").as("timeData");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(Criteria.where("shopId").is(shopId)),
                        projectionOperation,
                        groupOperation
                );
        logger.info("getOrderCountOfStatusByShopId()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        OrderCountVO orderCountVO;
        if (CollUtil.isNotEmpty(list)) {
            orderCountVO = BeanUtil.map(list.get(0), OrderCountVO.class);
        } else {
            orderCountVO = new OrderCountVO();
            orderCountVO.setAllCount(0);
            orderCountVO.setUnPay(0);
            orderCountVO.setPayed(0);
            orderCountVO.setConsignment(0);
            orderCountVO.setSuccess(0);
        }
        return orderCountVO;
    }

    public List<OrderOverviewVO> listSpuRankingByOrderCount(Long shopId, Date startTime, Date endTime, int limit) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, shopId, startTime, endTime);
        Integer lang = I18nMessage.getLang();

        String spuName = "orderItems.spuNameZh";
        if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
            spuName = "orderItems.spuNameEn";
        }

        // 分组
        GroupOperation groupOperation = Aggregation.group("orderItems.spuId")
                .count().as("payOrderCount")
                .last(spuName).as("spuName");

        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "payOrderCount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        Aggregation.unwind("orderItems"),
                        groupOperation,
                        sortOperation,
                        Aggregation.limit(limit)
                );
        logger.info("listSpuRankingByOrderCount()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<OrderOverviewVO> orderOverviewList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isEmpty(list)) {
            return orderOverviewList;
        }
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            OrderOverviewVO orderOverviewVO = new OrderOverviewVO();
            orderOverviewVO.setPayOrderCount(mongoOrderStatisticsVO.getPayOrderCount());
            orderOverviewVO.setSpuName(mongoOrderStatisticsVO.getSpuName());
            orderOverviewList.add(orderOverviewVO);
        }
        return orderOverviewList;
    }

    public List<OrderOverviewVO> listShopRankingByPayActual(Date startTime, Date endTime, int limit) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, null, startTime, endTime);

        // 分组
        GroupOperation userGroupOperation = Aggregation.group(new String[]{"shopId", "userId"})
                .count().as("payOrderCount")
                .sum("actualTotal").as("payActual");


        // 分组
        GroupOperation groupOperation = Aggregation.group("_id.shopId")
                .count().as("payUserCount")
                .sum("payOrderCount").as("payOrderCount")
                .sum("payActual").as("payActual");

        ProjectionOperation projectionOperation = Aggregation.project("payUserCount", "payOrderCount", "payActual").and("_id").as("shopId");

        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "payActual");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        userGroupOperation,
                        groupOperation,
                        projectionOperation,
                        sortOperation,
                        Aggregation.limit(limit)
                );
        logger.info("listShopRankingByPayActual()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return BeanUtil.mapAsList(list, OrderOverviewVO.class);
    }

    public List<UserOrderStatisticVO> statisticalUserOrderData(List<Long> userIds, Long shopId) {
        if (CollUtil.isEmpty(userIds)) {
            return new ArrayList<>(0);
        }

        ProjectionOperation projection = Aggregation.project("userId", "payTime")
                .and(ConditionalOperators.when(Criteria.where("payType").ne(0)).then("$total").otherwise(0)).as("total")
                .and(ConditionalOperators.when(Criteria.where("payType").ne(0)).then("$freightAmount").otherwise(0)).as("freightAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").ne(0)).then("$actualTotal").otherwise(0)).as("actualAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").ne(0)).then(1).otherwise(0)).as("consTimes")
                .and(ConditionalOperators.when(Criteria.where("payType").ne(0)).then("$reduceAmount").otherwise(0)).as("reduceAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").ne(0)).then("$actualTotal").otherwise(0)).as("totalAmount");

        // 分组
        GroupOperation groupOperation = Aggregation.group("userId")
                .last("payTime").as("payTime")
                .sum("total").as("total")
                .sum("freightAmount").as("freightAmount")
                .sum("actualAmount").as("actualAmount")
                .sum("consTimes").as("consTimes")
                .sum("reduceAmount").as("reduceAmount")
                .sum("totalAmount").as("totalAmount");

        ProjectionOperation projectionOperation = Aggregation.project("payTime", "total", "freightAmount", "actualAmount", "consTimes", "reduceAmount", "totalAmount")
                .and("_id").as("userId");

        Criteria criteria;
        if (Objects.nonNull(shopId) && !Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) {
            criteria = Criteria.where("isPayed").is(1).and("userId").in(userIds).and("shopId").is(shopId);
        } else {
            criteria = Criteria.where("isPayed").is(1).and("userId").in(userIds);
        }
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        projection,
                        groupOperation,
                        projectionOperation
                );
        logger.info("statisticalUserOrderData()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<UserOrderStatisticVO> userOrderStatisticList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            mongoOrderStatisticsVO.setConsAmount(mongoOrderStatisticsVO.getTotal() + mongoOrderStatisticsVO.getFreightAmount());
            UserOrderStatisticVO userOrderStatistic = BeanUtil.map(mongoOrderStatisticsVO, UserOrderStatisticVO.class);
            userOrderStatistic.setReConsTime(mongoOrderStatisticsVO.getPayTime());
            if (Objects.nonNull(mongoOrderStatisticsVO.getConsAmount()) && mongoOrderStatisticsVO.getConsAmount() > 0) {
                double averDiscount = Arith.div(Double.valueOf(mongoOrderStatisticsVO.getTotalAmount()), Double.valueOf(mongoOrderStatisticsVO.getConsAmount()), 2);
                userOrderStatistic.setAverDiscount(new BigDecimal(averDiscount));
            } else {
                userOrderStatistic.setAverDiscount(new BigDecimal(0));
            }
            userOrderStatisticList.add(userOrderStatistic);
        }
        return userOrderStatisticList;
    }

    public UserOrderStatisticBO getPaidMemberByParam(MemberReqDTO param) {
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, null, param.getStartTime(), param.getEndTime());

        if (Objects.nonNull(param.getMemberType()) && param.getMemberType() > 0) {
            criteria.and("userType").is(param.getMemberType() - 1);
        }

        // 分组
        GroupOperation userGroupOperation = Aggregation.group("userId")
                .count().as("consTimes")
                .sum("actualTotal").as("actualAmount");


        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("payUserCount")
                .sum("consTimes").as("consTimes")
                .sum("actualAmount").as("actualAmount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<UserOrderStatisticBO> typedAggregation =
                Aggregation.newAggregation(
                        UserOrderStatisticBO.class,
                        Aggregation.match(criteria),
                        userGroupOperation,
                        groupOperation
                );
        logger.info("getPaidMemberByParam()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<UserOrderStatisticBO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, "order", UserOrderStatisticBO.class);
        // 取出最终结果
        List<UserOrderStatisticBO> list = aggregationResults.getMappedResults();


        if (CollUtil.isNotEmpty(list)) {
            return list.get(0);
        }
        UserOrderStatisticBO userOrderStatisticBO = new UserOrderStatisticBO();
        userOrderStatisticBO.setPayUserCount(0);
        userOrderStatisticBO.setActualAmount(0L);
        userOrderStatisticBO.setConsTimes(0);
        return userOrderStatisticBO;
    }

    public UserOrderStatisticListBO getMemberPayData(MemberReqDTO param) {
        // 过滤
        UserOrderStatisticListBO userOrderStatisticListBO = this.totalMemberPayData(param);
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, null, param.getStartTime(), param.getEndTime());
        // 分组前，先处理下要用于分组的时间
        ProjectionOperation beforeProjectionOperation = Aggregation.project("userId", "newPayUser", "actualTotal", "firstOrder")
                .and(DateOperators.DateToString.dateOf("payTime").toString("%Y-%m-%d")).as("payTime");

        // 分组, 统计用户的支付信息
        GroupOperation userGroupOperation = Aggregation.group(new String[]{"payTime", "userId"})
                .count().as("consTimes")
                //.sum("newPayUser").as("newPayUserCount")
                .sum("firstOrder").as("newPayUserCount")
                .sum("actualTotal").as("consAmount");

        // 分组, 统计新老用户的支付信息
        GroupOperation groupOperation = Aggregation.group(new String[]{"_id.payTime", "newPayUserCount"})
                .count().as("payUserCount")
                .sum("consTimes").as("consTimes")
                .sum("consAmount").as("consAmount");

        ProjectionOperation projectionOperation = Aggregation.project("payUserCount", "consAmount", "consTimes")
                .andExclude("_id")
                .and("payTime").as("timeData")
                .and("newPayUserCount").as("userType");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        beforeProjectionOperation,
                        userGroupOperation,
                        groupOperation,
                        projectionOperation
                );
        logger.info("getMemberPayData()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return userOrderStatisticListBO;
        }
        Map<Integer, List<MongoOrderStatisticsVO>> map = list.stream().collect(Collectors.groupingBy(MongoOrderStatisticsVO::getUserType));
        if (map.containsKey(0)) {
            userOrderStatisticListBO.setOldUserOrderStatisticList(BeanUtil.mapAsList(map.get(0), UserOrderStatisticBO.class));
        }
        if (map.containsKey(1)) {
            userOrderStatisticListBO.setNewUserOrderStatisticList(BeanUtil.mapAsList(map.get(1), UserOrderStatisticBO.class));
        }
        return userOrderStatisticListBO;
    }

    private UserOrderStatisticListBO totalMemberPayData(MemberReqDTO param) {
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, null, param.getStartTime(), param.getEndTime());

        // 分组, 统计用户的支付信息
        GroupOperation userGroupOperation = Aggregation.group("userId")
                .count().as("consTimes")
                .sum("newPayUser").as("newPayUserCount")
                .sum("actualTotal").as("consAmount");

        // 分组, 统计新老用户的支付信息
        GroupOperation groupOperation = Aggregation.group("newPayUserCount")
                .count().as("payUserCount")
                .sum("consTimes").as("consTimes")
                .sum("consAmount").as("consAmount");

        ProjectionOperation projectionOperation = Aggregation.project("payUserCount", "consTimes", "consAmount").and("_id").as("userType");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        userGroupOperation,
                        groupOperation,
                        projectionOperation
                );
        logger.info("getMemberPayData()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        UserOrderStatisticListBO userOrderStatisticListBO = new UserOrderStatisticListBO();
        if (CollUtil.isEmpty(list)) {
            return userOrderStatisticListBO;
        }
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            UserOrderStatisticBO userOrderStatisticBO = BeanUtil.map(mongoOrderStatisticsVO, UserOrderStatisticBO.class);
            if (Objects.equals(mongoOrderStatisticsVO.getUserType(), 1)) {
                userOrderStatisticListBO.setNewUserOrderStatistic(userOrderStatisticBO);
                continue;
            }
            userOrderStatisticListBO.setOldUserOrderStatistic(userOrderStatisticBO);
        }
        return userOrderStatisticListBO;
    }

    /**
     * - 获取新支付用户的信息
     * - 关联订单，过滤掉不属于筛选时间段内的订单
     * - 根据从表的用户和时间进行分组，得到每个用户的购买时间
     * - 根据主表时间和从表的时间进行分组，计算每组的数量返回即可
     *
     * @param customerRetainedDTO
     * @return
     */
    public List<CustomerRetainVO> getTradeRetained(CustomerRetainedDTO customerRetainedDTO) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1).and("firstOrder").is(1);
        filterQuery(criteria, null, customerRetainedDTO.getStartTime(), customerRetainedDTO.getEndTime());

        ProjectionOperation initProjectionOperation = Aggregation.project("userId").and(DateOperators.DateToString.dateOf("payTime").toString("%Y%m")).as("newPayTime");
//        // 分不分组都行，分组保险一点
//        // 分组
        GroupOperation userGroupOperation = Aggregation.group("newPayTime", "userId");

        // 关联order表
        LookupOperation lookupOperation = Aggregation.lookup("order", "_id.userId", "userId", "orders");


        MatchOperation lookupMatchOperation = Aggregation.match(
                Criteria.where("orders.isPayed").is(1)
                        .and("orders.payTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(customerRetainedDTO.getStartTime()))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(customerRetainedDTO.getEndTime())))
        );

        UnwindOperation lookupUnwindOperation = Aggregation.unwind("orders");

        ProjectionOperation lookupProjectionOperation = Aggregation.project()
                .and(DateOperators.DateToString.dateOf("orders.payTime").toString("%Y%m")).as("payTime")
                .and("$_id.newPayTime").as("newPayTime")
                .and("$orders.userId").as("userId");

        // 关联分组
        GroupOperation lookupGroupOperation = Aggregation.group("newPayTime", "payTime", "userId");

        // 分组
        GroupOperation groupOperation = Aggregation.group(new String[]{"_id.newPayTime", "_id.payTime"})
                .count().as("payUserCount");

        ProjectionOperation projectionOperation = Aggregation.project("payUserCount")
                .and("newPayTime").as("currentMonth")
                .and("payTime").as("timeData");


        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        initProjectionOperation,
                        userGroupOperation,
                        lookupOperation,
                        lookupUnwindOperation,
                        lookupMatchOperation,
                        lookupProjectionOperation,
                        lookupGroupOperation,
                        groupOperation,
                        projectionOperation
                );
        logger.info("getTradeRetained()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<CustomerRetainVO> customerRetainList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            CustomerRetainVO customerRetainVO = new CustomerRetainVO();
            customerRetainVO.setNewCustomers(mongoOrderStatisticsVO.getPayUserCount());
            customerRetainVO.setDealMonth(mongoOrderStatisticsVO.getTimeData());
            customerRetainVO.setCurrentMonth(mongoOrderStatisticsVO.getCurrentMonth());
            customerRetainList.add(customerRetainVO);
        }
        return customerRetainList;
    }

    public List<HotProductVO> listHotProdRanking(Long supplierId, Date startTime, Date endTime, Integer lang) {
        List<HotProductVO> prodList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1)
                .and("supplierDeliveryType").is(1)
                .and("waitPurchase").ne(1);
        filterQuery(criteria, null, startTime, endTime);
        if (Objects.nonNull(supplierId)) {
            criteria.and("supplierId").is(supplierId);
        }

        UnwindOperation unwindOperation = Aggregation.unwind("orderItems");

        String spuName;
        if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
            spuName = "orderItems.spuNameEn";
        } else {
            spuName = "orderItems.spuNameZh";
        }

        // 分组
        GroupOperation groupOperation = Aggregation.group("orderItems.spuId")
                .count().as("payOrderCount")
                .sum("orderItems.actualTotal").as("totalAmount")
                .sum("orderItems.count").as("payProdCount")
                .last(spuName).as("spuName");
        // 支付金额倒序
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "totalAmount");
        ProjectionOperation projectionOperation = Aggregation.project("payOrderCount", "totalAmount", "payProdCount", "spuName")
                .and("_id").as("spuId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        unwindOperation,
                        groupOperation,
                        sortOperation,
                        projectionOperation
                );
        logger.info("listHotProdRanking()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return prodList;
        }
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            HotProductVO hotProductVO = new HotProductVO();
            hotProductVO.setSpuId(mongoOrderStatisticsVO.getSpuId());
            hotProductVO.setSpuName(mongoOrderStatisticsVO.getSpuName());
            hotProductVO.setOrderNums(mongoOrderStatisticsVO.getPayOrderCount());
            hotProductVO.setProductNums(mongoOrderStatisticsVO.getPayProdCount());
            hotProductVO.setPayAmount(Double.valueOf(mongoOrderStatisticsVO.getTotalAmount()));
            prodList.add(hotProductVO);
        }
        return prodList;
    }

    public OrderOverviewVO getOrderOverviewInfoBySupplierId(Long supplierId, Date startTime, Date endTime) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1)
                .and("supplierDeliveryType").is(1)
                .and("waitPurchase").ne(1);
        filterQuery(criteria, null, startTime, endTime);
        if (Objects.nonNull(supplierId)) {
            criteria.and("supplierId").is(supplierId);
        }

        // 根据用户进行分组
        GroupOperation userIdGroupOperation = Aggregation.group("userId")
                .sum("actualTotal").as("actualTotal")
                .sum("platformAmount").as("platformAmount");

        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("payUserCount")
                .sum("actualTotal").as("actualTotal")
                .sum("platformAmount").as("platformAmount");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        userIdGroupOperation,
                        groupOperation
                );
        logger.info("getOrderOverviewInfoBySupplierId()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        OrderOverviewVO orderOverviewVO = new OrderOverviewVO();
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            orderOverviewVO.setPayUserCount(0);
            orderOverviewVO.setPayActual(0.0D);
            return orderOverviewVO;
        }
        MongoOrderStatisticsVO mongoOrderStatisticsVO = list.get(0);
        if (Objects.isNull(supplierId)) {
            orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal());
        } else {
            orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal() + mongoOrderStatisticsVO.getPlatformAmount());
        }
        orderOverviewVO.setPayUserCount(mongoOrderStatisticsVO.getPayUserCount());
        return orderOverviewVO;
    }

    public List<SupplierOrderOverviewVO> listProdSoldCountByHour(Long supplierId, Date startTime, Date endTime) {
        List<SupplierOrderOverviewVO> orderList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1)
                .and("supplierDeliveryType").is(1)
                .and("status").ne(6);
        filterQuery(criteria, null, startTime, endTime);
        if (Objects.nonNull(supplierId)) {
            criteria.and("supplierId").is(supplierId);
        }

        ProjectionOperation beginProjectionOperation = Aggregation.project("payTime", "allCount")
                .and(DateOperators.DateToString.dateOf("payTime").toString("%H")).as("timeData");

        // 根据用户进行分组
        GroupOperation groupOperation = Aggregation.group("timeData")
                .sum("allCount").as("payOrderCount");


        ProjectionOperation projectionOperation = Aggregation.project("payOrderCount").and("_id").as("timeData");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        beginProjectionOperation,
                        groupOperation,
                        projectionOperation
                );
        logger.info("listProdSoldCountByHour()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return orderList;
        }
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            SupplierOrderOverviewVO supplierOrderOverviewVO = new SupplierOrderOverviewVO();
            supplierOrderOverviewVO.setTimeData(mongoOrderStatisticsVO.getTimeData());
            supplierOrderOverviewVO.setTodayProdSoldNums(mongoOrderStatisticsVO.getPayOrderCount());
            orderList.add(supplierOrderOverviewVO);
        }
        return orderList;
    }

    public List<OrderOverviewVO> listPayActualByDay(Long supplierId, Date startTime, Date endTime) {
        List<OrderOverviewVO> orderList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1)
                .and("supplierDeliveryType").is(1)
                .and("waitPurchase").ne(1);
        filterQuery(criteria, null, startTime, endTime);
        if (Objects.nonNull(supplierId)) {
            criteria.and("supplierId").is(supplierId);
        }

        ProjectionOperation beginProjectionOperation = Aggregation.project("actualTotal", "platformAmount")
                .and(DateOperators.DateToString.dateOf("payTime").toString("%Y-%m-%d")).as("timeData");

        // 根据用户进行分组
        GroupOperation groupOperation = Aggregation.group("timeData")
                .sum("actualTotal").as("actualTotal")
                .sum("platformAmount").as("platformAmount");


        ProjectionOperation projectionOperation = Aggregation.project("actualTotal", "platformAmount").and("_id").as("timeData");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        beginProjectionOperation,
                        groupOperation,
                        projectionOperation
                );
        logger.info("listPayActualByDay()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return orderList;
        }
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            OrderOverviewVO orderOverviewVO = new OrderOverviewVO();
            if (Objects.isNull(supplierId)) {
                orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal());
            } else {
                orderOverviewVO.setPayActual(mongoOrderStatisticsVO.getActualTotal() + mongoOrderStatisticsVO.getPlatformAmount());
            }
            orderOverviewVO.setTimeData(mongoOrderStatisticsVO.getTimeData());
            orderList.add(orderOverviewVO);
        }
        return orderList;
    }

    public Integer getShopNewUsersBySupplierId(Long supplierId, Date startTime, Date endTime) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1)
                .and("supplierDeliveryType").is(1)
                .and("firstSupplierOrder").is(1)
                .and("supplierId").is(supplierId);
        filterQuery(criteria, null, startTime, endTime);

        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("payUserCount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation
                );
        logger.info("getShopNewUsersBySupplierId()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return 0;
        }
        return list.get(0).getPayUserCount();
    }

    public OrderCountVO getOrderCountOfStatusBySupplierId(Long supplierId) {
        // 过滤
        Criteria criteria = Criteria.where("supplierId").is(supplierId)
                .and("supplierDeliveryType").is(1)
                .and("waitPurchase").ne(1);

        // 分组前，先处理下要用于分组的时间
        ProjectionOperation projectionOperation = Aggregation.project()
                .and(ConditionalOperators.when(Criteria.where("status").is(1)).then(1).otherwise(0)).as("unPay")
                .and(ConditionalOperators.when(Criteria.where("status").is(2)).then(1).otherwise(0)).as("payed")
                .and(ConditionalOperators.when(Criteria.where("status").is(3)).then(1).otherwise(0)).as("consignment")
                .and(ConditionalOperators.when(Criteria.where("status").is(5)).then(1).otherwise(0)).as("success");
        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("allCount")
                .sum("unPay").as("unPay")
                .sum("payed").as("payed")
                .sum("consignment").as("consignment")
                .sum("success").as("success");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        projectionOperation,
                        groupOperation
                );
        logger.info("getOrderCountOfStatusBySupplierId()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        OrderCountVO orderCountVO;
        if (CollUtil.isNotEmpty(list)) {
            orderCountVO = BeanUtil.map(list.get(0), OrderCountVO.class);
        } else {
            orderCountVO = new OrderCountVO();
            orderCountVO.setAllCount(0);
            orderCountVO.setUnPay(0);
            orderCountVO.setPayed(0);
            orderCountVO.setConsignment(0);
            orderCountVO.setSuccess(0);
        }
        return orderCountVO;
    }

    public List<Long> getOrderUserIdsBySearchDTO(OrderSearchDTO orderSearchDTO) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1);
        filterQuery(criteria, null, orderSearchDTO.getStartTime(), orderSearchDTO.getEndTime());
        if (Objects.nonNull(orderSearchDTO.getDeleteStatus())) {
            criteria.and("deleteStatus").is(orderSearchDTO.getDeleteStatus());
        }
        if (Objects.nonNull(orderSearchDTO.getStatus())) {
            criteria.and("status").is(orderSearchDTO.getStatus());
        }
        if (Objects.nonNull(orderSearchDTO.getActualTotalMin()) && Objects.nonNull(orderSearchDTO.getActualTotalMax())) {
            criteria.and("actualTotal").gte(orderSearchDTO.getActualTotalMin()).lte(orderSearchDTO.getActualTotalMax());
        }

        // 分组
        GroupOperation groupOperation = Aggregation.group("userId");

        ProjectionOperation projectionOperation = Aggregation.project().and("_id").as("userId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        projectionOperation
                );
        logger.info("getOrderUserIdsBySearchDTO()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return list.stream().map(MongoOrderStatisticsVO::getUserId).collect(Collectors.toList());
    }

    /**
     * 获取在一定时间内消费一定次数的会员信息
     *
     * @param isPayed      是否已支付
     * @param deleteStatus 用户订单删除状态，0：没有删除， 1：回收站， 2：永久删除
     * @param startDate    开始时间
     * @param endDate      结束时间
     * @param status       状态
     * @param minNum       最小数量
     * @param maxNum       最大数量
     * @return 会员id
     */
    public List<Long> listUserIdByPurchaseNum(Integer isPayed, Integer deleteStatus, Date startDate, Date endDate, Integer status, Long minNum, Long maxNum) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(isPayed);
        if (Objects.nonNull(deleteStatus)) {
            criteria.and("deleteStatus").is(deleteStatus);
        }
        if (Objects.nonNull(status)) {
            criteria.and("status").is(status);
        }
        String timeFiled = "finallyTime";
        if (Objects.equals(isPayed, 1)) {
            timeFiled = "payTime";
        }
        if (Objects.nonNull(startDate) || Objects.nonNull(endDate)) {
            if (Objects.nonNull(startDate) && Objects.nonNull(endDate)) {
                criteria.and(timeFiled).gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startDate))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endDate)));
            } else if (Objects.nonNull(startDate)) {
                criteria.and(timeFiled).gte(startDate);
            } else {
                criteria.and(timeFiled).lte(endDate);
            }
        }

        // 分组
        GroupOperation groupOperation = Aggregation.group("userId")
                .count().as("count");


        ProjectionOperation projectionOperation = Aggregation.project().and("_id").as("userId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        Aggregation.match(Criteria.where("count").gte(minNum).lte(maxNum)),
                        projectionOperation
                );
        logger.info("listUserIdByPurchaseNum()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return list.stream().map(MongoOrderStatisticsVO::getUserId).collect(Collectors.toList());
    }

    /**
     * 获取在一定时间内消费一定金额的会员信息
     *
     * @param isPayed      是否已支付
     * @param deleteStatus 用户订单删除状态，0：没有删除， 1：回收站， 2：永久删除
     * @param startDate    开始时间
     * @param endDate      结束时间
     * @param status       状态
     * @param minAmount    最小支付金额
     * @param maxAmount    最大支付金额
     * @return 会员id
     */
    public List<Long> listUserIdByAverageActualTotal(Integer isPayed, Integer deleteStatus, Date startDate, Date endDate, Integer status, Long minAmount, Long maxAmount) {
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(isPayed);
        if (Objects.nonNull(deleteStatus)) {
            criteria.and("deleteStatus").is(deleteStatus);
        }
        if (Objects.nonNull(status)) {
            criteria.and("status").is(status);
        }

        String timeField = "finallyTime";
        if (Objects.equals(isPayed, 1)) {
            timeField = "payTime";
        }
        if (Objects.nonNull(startDate) || Objects.nonNull(endDate)) {
            if (Objects.nonNull(startDate) && Objects.nonNull(endDate)) {
                criteria.and(timeField).gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startDate))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endDate)));
            } else if (Objects.nonNull(startDate)) {
                criteria.and(timeField).gte(startDate);
            } else {
                criteria.and(timeField).lte(endDate);
            }
        }

        // 分组
        GroupOperation groupOperation = Aggregation.group("userId")
                .avg("actualTotal").as("avgAmount");

        ProjectionOperation projectionOperation = Aggregation.project().and("_id").as("userId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        Aggregation.match(Criteria.where("avgAmount").gte(minAmount).lte(maxAmount)),
                        projectionOperation
                );
        logger.info("listUserIdByAverageActualTotal()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return list.stream().map(MongoOrderStatisticsVO::getUserId).collect(Collectors.toList());
    }

    /**
     * 根据参数获取数据分析中的订单信息
     *
     * @param spuIds    产品id列表
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 商品分析数据
     */
    public List<OrderProdEffectRespVO> getProdEffectByDateAndProdIds(List<Long> spuIds, Date startTime, Date endTime) {
        // 过滤
        Criteria criteria = Criteria.where("orderItems.spuId").in(spuIds)
                .and("createTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));

        UnwindOperation unwindOperation = Aggregation.unwind("orderItems");

        ProjectionOperation userProjectionOperation = Aggregation.project("userId", "isPayed")
                .and("orderItems.spuId").as("spuId")
                .and("orderItems.count").as("count")
                .and("orderItems.refundAmount").as("actualTotal")
                .and(ConditionalOperators.when(Criteria.where("isPayed").is(1)).then("$orderItems.count").otherwise(0)).as("payNum")
                .and(ConditionalOperators.when(Criteria.where("isPayed").is(1)).then("$orderItems.actualTotal").otherwise(0)).as("payAmount");

        // 分组
        GroupOperation userGroupOperation = Aggregation.group(new String[]{"spuId", "userId"})
                .first("isPayed").as("isPayed")
                .sum("count").as("placeOrderNum")
                .sum("payNum").as("payNum")
                .sum("actualTotal").as("placeOrderAmount")
                .sum("payAmount").as("payAmount")
                .sum("isPayed").as("payOrder");
        ProjectionOperation spuProjectionOperation = Aggregation.project("isPayed", "placeOrderNum", "payNum", "placeOrderAmount", "payAmount", "payOrder")
                .and(ConditionalOperators.when(Criteria.where("payOrder").gt(0)).then(1).otherwise(0)).as("payPerson")
                .and("_id.spuId").as("spuId");

        // 分组
        GroupOperation groupOperation = Aggregation.group("spuId")
                .count().as("placeOrderPerson")
                .first("spuId").as("spuId")
                .sum("payPerson").as("payPerson")
                .sum("payNum").as("payNum")
                .sum("placeOrderNum").as("placeOrderNum")
                .sum("placeOrderAmount").as("placeOrderAmount")
                .sum("payAmount").as("payAmount")
                .sum("payOrder").as("payOrderNum");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        unwindOperation,
                        userProjectionOperation,
                        userGroupOperation,
                        spuProjectionOperation,
                        groupOperation
                );
        logger.info("getProdEffectByDateAndProdIds()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<OrderProdEffectRespVO> orderProdEffectRespList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isEmpty(list)) {
            return orderProdEffectRespList;
        }
        return BeanUtil.mapAsList(list, OrderProdEffectRespVO.class);
    }

    /**
     * 根据订单id列表， 获取订单流量信息列表
     *
     * @param orderIds 订单id列表
     * @return 订单流量信息列表
     */
    public List<FlowOrderVO> listFlowOrderByOrderIds(Collection<Long> orderIds) {
        if (CollUtil.isEmpty(orderIds)) {
            return new ArrayList<>(0);
        }
        // 过滤
        Criteria criteria = Criteria.where("_id").in(orderIds);

        UnwindOperation unwindOperation = Aggregation.unwind("orderItems");

        // 分组
        GroupOperation groupOperation = Aggregation.group(new String[]{"_id", "orderItems.spuId"})
                .first("_id").as("orderId")
                .first("orderItems.userId").as("userId")
                .first("orderItems.spuId").as("spuId")
                .first("isPayed").as("isPayed")
                .sum("orderItems.actualTotal").as("actualAmount");


        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        unwindOperation,
                        groupOperation
                );
        logger.info("listFlowOrderByOrderIds()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<FlowOrderVO> flowOrderList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isEmpty(list)) {
            return flowOrderList;
        }
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            FlowOrderVO flowOrderVO = BeanUtil.map(mongoOrderStatisticsVO, FlowOrderVO.class);
            flowOrderVO.setOrderAmount(mongoOrderStatisticsVO.getActualAmount());
            flowOrderList.add(flowOrderVO);
        }
        return flowOrderList;
    }

    public List<Long> getInsightSpuIdsByDate(Integer begin, Integer size, Long shopId, Date startTime, Date endTime) {
        // 过滤掉多余的订单数据
        Criteria orderCriteria = Criteria.where("updateTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        if (Objects.nonNull(shopId) && shopId > 0) {
            orderCriteria.and("shopId").is(shopId);
        }

        UnwindOperation unwindOperation = Aggregation.unwind("orderItems");

        ProjectionOperation initProjectionOperation = Aggregation.project("orderId", "updateTime", "createTime")
                .and("orderItems.spuId").as("spuId");

        LookupOperation lookupOperation = Aggregation.lookup("order_refund", "orderId", "orderId", "refundList");

        // 过滤掉退款时间不在规定时间范围内的订单
        List<Criteria> criteriaList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        criteriaList.add(Criteria.where("refundList.refundTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime))));
        criteriaList.add(Criteria.where("createTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime))));

        // 保留创建时间或退款时间在规定时间范围内的订单
        Criteria criteria = Criteria.where("spuId").exists(Boolean.TRUE).orOperator(criteriaList);

        // 分组
        GroupOperation groupOperation = Aggregation.group("spuId").first("createTime").as("createTime");

        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "createTime");
        ProjectionOperation projectionOperation = Aggregation.project().and("_id").as("spuId");

        TypedAggregation<MongoOrderStatisticsVO> typedAggregation;
        if (size > 0) {
            // 按顺序组合每一个聚合步骤
            typedAggregation = Aggregation.newAggregation(
                    MongoOrderStatisticsVO.class,
                    Aggregation.match(orderCriteria),
                    unwindOperation,
                    initProjectionOperation,
                    lookupOperation,
                    Aggregation.match(criteria),
                    groupOperation,
                    sortOperation,
                    projectionOperation,
                    Aggregation.limit(size),
                    Aggregation.skip(begin)
            );
        } else {
            // 按顺序组合每一个聚合步骤
            typedAggregation = Aggregation.newAggregation(
                    MongoOrderStatisticsVO.class,
                    Aggregation.match(orderCriteria),
                    unwindOperation,
                    initProjectionOperation,
                    lookupOperation,
                    Aggregation.match(criteria),
                    groupOperation,
                    sortOperation,
                    projectionOperation
            );
        }


        logger.info("getInsightSpuIdsByDate()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return list.stream().map(MongoOrderStatisticsVO::getSpuId).collect(Collectors.toList());
    }

    /**
     * 根据用户id列表获取积累消费金额和积累消费笔数
     *
     * @param userIds 用户id列表
     * @return
     */
    public List<SumAmountVO> listSumDataByUserIds(List<Long> userIds) {
        // 过滤
        Criteria criteria = Criteria.where("userId").in(userIds).and("isPayed").is(1).and("actualTotal").gt(0);

        // 分组
        GroupOperation groupOperation = Aggregation.group("userId")
                .first("userId").as("userId")
                .sum("actualTotal").as("actualTotal")
                .count().as("payOrderCount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation
                );
        logger.info("listSumDataByUserIds()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<SumAmountVO> sumAmountList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            SumAmountVO sumAmountVO = new SumAmountVO();
            sumAmountVO.setUserId(mongoOrderStatisticsVO.getUserId());
            sumAmountVO.setExpenseNumber(mongoOrderStatisticsVO.getPayOrderNum());
            sumAmountVO.setSumOfConsumption(mongoOrderStatisticsVO.getActualTotal());
            sumAmountList.add(sumAmountVO);
        }
        return sumAmountList;
    }

    /**
     * 获取用户购买过的商品数量
     *
     * @param spuId  商品id
     * @param userId 分销员userId
     * @return
     */
    public Long hasBuySuccessProd(Long spuId, Long userId) {
        // 过滤
        Criteria criteria = Criteria.where("userId").is(userId)
                .and("orderItems.spuId").is(spuId)
                .and("status").is(OrderStatus.SUCCESS.value());
        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("payOrderCount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation
                );
        logger.info("hasBuySuccessProd()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return 0L;
        }
        Integer payOrderCount = list.get(0).getPayOrderCount();
        if (Objects.isNull(payOrderCount)) {
            payOrderCount = 0;
        }
        return Long.valueOf(payOrderCount);
    }

    /**
     * 统计用户购物数据
     *
     * @param userId 用户id
     * @return 用户购物数据
     */
    public UserShoppingDataVO calculateUserInShopData(Long userId) {
        List<Integer> statusList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        statusList.add(4);
        statusList.add(5);
        // 过滤
        Criteria criteria = Criteria.where("userId").is(userId).and("status").in(statusList);

        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("payOrderCount")
                .sum("actualTotal").as("actualAmount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation
                );
        logger.info("calculateUserInShopData()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        UserShoppingDataVO userShoppingDataVO = new UserShoppingDataVO();
        if (CollUtil.isEmpty(list) || Objects.isNull(list.get(0))) {
            userShoppingDataVO.setExpenseNumber(0L);
            userShoppingDataVO.setSumOfConsumption(0L);
        }
        MongoOrderStatisticsVO mongoOrderStatisticsVO = list.get(0);
        userShoppingDataVO.setExpenseNumber(Long.valueOf(mongoOrderStatisticsVO.getPayOrderCount()));
        userShoppingDataVO.setSumOfConsumption(mongoOrderStatisticsVO.getActualAmount());
        return userShoppingDataVO;
    }

    /**
     * 获取活动销售的商品数量
     *
     * @param activityIds
     * @param activityType
     * @return
     */
    public List<OrderActivityVO> getActivitySalesQuantity(List<Long> activityIds, Integer activityType) {
        // 过滤
        MatchOperation initMatchOperation = Aggregation.match(Criteria.where("isPayed").is(1).and("orderItems.activityId").in(activityIds).and("orderItems.activityType").is(activityType));

        MatchOperation matchOperation = Aggregation.match(Criteria.where("orderItems.activityId").in(activityIds).and("orderItems.activityType").is(activityType));
        // 分组
        GroupOperation groupOperation = Aggregation.group("orderItems.activityId")
                .sum("orderItems.count").as("payProdCount");

        ProjectionOperation projectionOperation = Aggregation.project("payProdCount").and("_id").as("activityId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        initMatchOperation,
                        Aggregation.unwind("orderItems"),
                        matchOperation,
                        groupOperation,
                        projectionOperation
                );
        logger.info("getActivitySalesQuantity()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<OrderActivityVO> activityList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isEmpty(list)) {
            return activityList;
        }
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            OrderActivityVO orderActivityVO = new OrderActivityVO();
            orderActivityVO.setActivityId(mongoOrderStatisticsVO.getActivityId());
            orderActivityVO.setCount(mongoOrderStatisticsVO.getPayProdCount());
            activityList.add(orderActivityVO);
        }
        return activityList;
    }


    /**
     * @param skuId
     * @param activityId
     * @param activityType
     * @return
     */
    public Long countActivityOrderSale(Long skuId, Long activityId, Integer activityType) {
        // 过滤
        Criteria criteria = Criteria.where("orderItems.activityId").is(activityId)
                .and("orderItems.activityType").is(activityType)
                .and("status").ne(OrderStatus.CLOSE.value());
        if (Objects.nonNull(skuId)) {
            criteria.and("orderItems.skuId").is(skuId);
        }

        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .sum("orderItems.count").as("payProdCount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.unwind("orderItems"),
                        Aggregation.match(criteria),
                        groupOperation
                );
        logger.info("countActivityOrderSale()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<OrderActivityVO> activityList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isEmpty(list) || Objects.isNull(list.get(0))) {
            return 0L;
        }
        return Long.valueOf(list.get(0).getPayProdCount());
    }

    private void filterQuery(Criteria criteria, Long shopId, Date startTime, Date endTime) {
        if (Objects.nonNull(shopId)) {
            criteria.and("shopId").is(shopId);
        }
        if (Objects.nonNull(startTime) || Objects.nonNull(endTime)) {
            if (Objects.nonNull(startTime) && Objects.nonNull(endTime)) {
                criteria.and("payTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
            } else if (Objects.nonNull(startTime)) {
                criteria.and("payTime").gte(startTime);
            } else {
                criteria.and("payTime").lte(endTime);
            }
        }
    }

    public AccountOrderDetailVO getAccountOrderDetail(Date startTime, Date endTime, String shopName, Long shopId) {
        // 过滤
        Criteria criteria = new Criteria();
        Integer paySysType = orderAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        criteria.and("paySysType").is(paySysType);
        criteria.and("isPayed").is(1);
        if (StrUtil.isNotBlank(shopName) && Objects.isNull(shopId)) {
            criteria.and("shopName").regex(Pattern.compile("^.*" + shopName + ".*$", Pattern.CASE_INSENSITIVE));
        }
        if (Objects.nonNull(shopId)) {
            criteria.and("shopId").is(shopId);
        }
        if (Objects.nonNull(startTime)) {
            criteria.and("payTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }

        ProjectionOperation projectionOperation = Aggregation.project("payType", "orderScore")
                .and(ConditionalOperators.when(Criteria.where("payType").in(2, 6, 7)).then("$actualTotal").otherwise(0)).as("alipayAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(1, 3, 4, 5, 8)).then("$actualTotal").otherwise(0)).as("wechatAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(9)).then("$actualTotal").otherwise(0)).as("balanceAmount");

        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .sum("alipayAmount").as("alipayAmount")
                .sum("wechatAmount").as("wechatAmount")
                .sum("balanceAmount").as("balanceAmount")
                .sum("orderScore").as("scoreCount");

        ProjectionOperation project = Aggregation.project("scoreCount", "alipayAmount", "wechatAmount", "balanceAmount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoAccountOrderDetailVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoAccountOrderDetailVO.class,
                        Aggregation.match(criteria),
                        projectionOperation,
                        groupOperation,
                        project
                );
        logger.info("getAccountOrderDetail()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoAccountOrderDetailVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoAccountOrderDetailVO.class);
        // 取出最终结果
        List<MongoAccountOrderDetailVO> list = aggregationResults.getMappedResults();
        return BeanUtil.map(CollUtil.isEmpty(list) ? new MongoAccountOrderDetailVO() : list.get(0), AccountOrderDetailVO.class);
    }

    public PageShopAccountOrderVO listShopAccountDetail(Date startTime, Date endTime, String shopName,
                                                        Integer pageSize, Integer pageNum) {
        // 过滤
        Criteria criteria = new Criteria();
        Integer paySysType = orderAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        criteria.and("paySysType").is(paySysType);
        if (StrUtil.isNotBlank(shopName)) {
            criteria.and("shopName").regex(Pattern.compile("^.*" + shopName + ".*$", Pattern.CASE_INSENSITIVE));
        }
        if (Objects.nonNull(startTime)) {
            criteria.and("payTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }
        criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);

        ProjectionOperation projectionOperation = Aggregation.project("shopId", "shopName", "payType", "orderScore")
                .and(ConditionalOperators.when(Criteria.where("payType").in(2, 6, 7)).then("$actualTotal").otherwise(0)).as("alipayAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(1, 3, 4, 5, 8)).then("$actualTotal").otherwise(0)).as("wechatAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(9)).then("$actualTotal").otherwise(0)).as("balanceAmount")
                .and("$actualTotal").as("total");
        // 分组
        GroupOperation shopGroupOperation = Aggregation.group("shopId")
                .first("shopId").as("shopId")
                .first("shopName").as("shopName")
                .sum("alipayAmount").as("alipayAmount")
                .sum("wechatAmount").as("wechatAmount")
                .sum("balanceAmount").as("balanceAmount")
                .sum("orderScore").as("scoreCount")
                .sum("total").as("total");

        ProjectionOperation project = Aggregation.project("shopId", "shopName", "scoreCount", "alipayAmount", "wechatAmount", "balanceAmount", "total");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "shopId");
        // 分页数据
        SkipOperation skipOperation = Aggregation.skip((long) pageSize * (pageNum - 1));
        LimitOperation limitOperation = Aggregation.limit(pageSize);

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoShopAccountOrderDetailVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoShopAccountOrderDetailVO.class,
                        Aggregation.match(criteria),
                        projectionOperation,
                        shopGroupOperation,
                        project,
                        sortOperation,
                        skipOperation,
                        limitOperation
                );
        logger.info("listShopAccountDetail()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoShopAccountOrderDetailVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoShopAccountOrderDetailVO.class);
        // 取出最终结果
        List<MongoShopAccountOrderDetailVO> list = aggregationResults.getMappedResults();

        // 总条数
        TypedAggregation<MongoShopAccountOrderDetailVO> aggregation = Aggregation.newAggregation(
                MongoShopAccountOrderDetailVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                shopGroupOperation,
                project
        );
        AggregationResults<MongoShopAccountOrderDetailVO> aggregate = mongoTemplate.aggregate(aggregation, MongoShopAccountOrderDetailVO.class);
        List<MongoShopAccountOrderDetailVO> mappedResults = aggregate.getMappedResults();
        PageShopAccountOrderVO pageShopAccountOrderVO = new PageShopAccountOrderVO();
        pageShopAccountOrderVO.setShopList(BeanUtil.mapAsList(list, ShopAccountOrderDetailVO.class));
        pageShopAccountOrderVO.setTotal((long) mappedResults.size());
        return pageShopAccountOrderVO;
    }

    public PageVO<FinanceDetailVO> pageOrderFinanceDetail(PageDTO pageDTO, FinanceDetailDTO financeDetailDTO) {
        // 过滤
        Criteria criteria = getFinanceCriteria(financeDetailDTO);
        Integer lang = I18nMessage.getLang();
        String spuName = "orderItems.spuNameZh";
        if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
            spuName = "orderItems.spuNameEn";
        }
        UnwindOperation unwindOperation = Aggregation.unwind("orderItems");
        // 分组
        GroupOperation groupOperation = Aggregation.group("_id")
                .first("_id").as("orderId")
                .first("payType").as("payType")
                .first("payTime").as("transDate")
                .first("createTime").as("placeTime")
                .first("actualTotal").as("transAmount")
                .first("shopId").as("shopId")
                .first("shopName").as("shopName")
                .first("userId").as("userId")
                .first("payId").as("payIds")
                .push(spuName).as("prodNames");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "transDate");
        // 分页数据
        SkipOperation skipOperation = Aggregation.skip((long) pageDTO.getPageSize() * (pageDTO.getPageNum() - 1));
        LimitOperation limitOperation = Aggregation.limit(pageDTO.getPageSize());
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoFinanceOrderDetailVO> typedAggregation = Aggregation.newAggregation(
                MongoFinanceOrderDetailVO.class,
                Aggregation.match(criteria),
                unwindOperation,
                groupOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );
        logger.info("pageOrderFinanceDetail()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoFinanceOrderDetailVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoFinanceOrderDetailVO.class);
        // 取出最终结果
        List<MongoFinanceOrderDetailVO> list = aggregationResults.getMappedResults();
        list.forEach(data -> {
            data.setBizType(FinanceBizType.ORDER.value());
            data.setProdName(data.getProdNames().toString().substring(1, data.getProdNames().toString().length() - 1));
        });
        // 总条数
        TypedAggregation<MongoFinanceOrderDetailVO> aggregation = Aggregation.newAggregation(
                MongoFinanceOrderDetailVO.class,
                Aggregation.match(criteria),
                unwindOperation,
                groupOperation
        );
        AggregationResults<MongoFinanceOrderDetailVO> aggregate = mongoTemplate.aggregate(aggregation, MongoFinanceOrderDetailVO.class);
        List<MongoFinanceOrderDetailVO> mappedResults = aggregate.getMappedResults();
        PageVO<FinanceDetailVO> page = new PageVO<>();
        page.setTotal((long) mappedResults.size());
        page.setList(BeanUtil.mapAsList(list, FinanceDetailVO.class));
        page.setPages(PageUtil.getPages(mappedResults.size(), pageDTO.getPageSize()));
        return page;
    }

    public List<FinanceDetailVO> listOrderFinanceDetail(FinanceDetailDTO financeDetailDTO) {
        // 过滤
        Criteria criteria = getFinanceCriteria(financeDetailDTO);
        Integer lang = I18nMessage.getLang();
        String spuName = "orderItems.spuNameZh";
        if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
            spuName = "orderItems.spuNameEn";
        }
        // 分组
        GroupOperation groupOperation = Aggregation.group("_id")
                .first("_id").as("orderId")
                .first("payTime").as("transDate")
                .first("payType").as("payType")
                .first("createTime").as("placeTime")
                .first("actualTotal").as("transAmount")
                .first("shopName").as("shopName")
                .first("userId").as("userId")
                .first("payId").as("payIds")
                .push(spuName).as("prodNames");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "transDate");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoFinanceOrderDetailVO> typedAggregation = Aggregation.newAggregation(
                MongoFinanceOrderDetailVO.class,
                Aggregation.match(criteria),
                Aggregation.unwind("orderItems"),
                groupOperation,
                sortOperation
        );
        logger.info("listOrderFinanceDetail()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoFinanceOrderDetailVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoFinanceOrderDetailVO.class);
        // 取出最终结果
        List<MongoFinanceOrderDetailVO> list = aggregationResults.getMappedResults();
        list.forEach(data -> {
            data.setBizType(FinanceBizType.ORDER.value());
            data.setProdName(data.getProdNames().toString().substring(1, data.getProdNames().toString().length() - 1));
        });
        return BeanUtil.mapAsList(list, FinanceDetailVO.class);
    }

    public PageShopAccountOrderVO pageOrderPayInfo(Date startTime, Date endTime, Long shopId, Integer pageSize, Integer pageNum) {
        // 过滤
        Criteria criteria = new Criteria();
        Integer paySysType = orderAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        criteria.and("paySysType").is(paySysType);
        criteria.and("isPayed").is(1);
        criteria.and("shopId").is(shopId);
        if (Objects.nonNull(startTime)) {
            criteria.and("payTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }
        ProjectionOperation projectionOperation = Aggregation.project("shopId", "shopName", "payType", "payId", "payTime", "updateTime")
                .and("actualTotal").as("payAmount")
                .and("_id").as("orderIds")
                .and("orderScore").as("scoreCount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(2, 6, 7)).then("$actualTotal").otherwise(0)).as("alipayAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(1, 3, 4, 5, 8)).then("$actualTotal").otherwise(0)).as("wechatAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(9)).then("$actualTotal").otherwise(0)).as("balanceAmount");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "updateTime");
        // 分页数据
        SkipOperation skipOperation = Aggregation.skip((long) pageSize * (pageNum - 1));
        LimitOperation limitOperation = Aggregation.limit(pageSize);
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderPayInfoVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderPayInfoVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );
        logger.info("pageOrderPayInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderPayInfoVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderPayInfoVO.class);
        // 取出最终结果
        List<MongoOrderPayInfoVO> list = aggregationResults.getMappedResults();
        list.forEach(data -> data.setPayEntry(0));
        // 总条数
        TypedAggregation<MongoOrderPayInfoVO> aggregation = Aggregation.newAggregation(
                MongoOrderPayInfoVO.class,
                Aggregation.match(criteria),
                projectionOperation
        );
        AggregationResults<MongoOrderPayInfoVO> aggregate = mongoTemplate.aggregate(aggregation, MongoOrderPayInfoVO.class);
        List<MongoOrderPayInfoVO> mappedResults = aggregate.getMappedResults();
        PageShopAccountOrderVO pageShopAccountOrderVO = new PageShopAccountOrderVO();
        pageShopAccountOrderVO.setPayInfoList(BeanUtil.mapAsList(list, OrderPayInfoVO.class));
        pageShopAccountOrderVO.setTotal((long) mappedResults.size());
        return pageShopAccountOrderVO;
    }

    public PageShopAccountOrderVO listOrderPayInfo(Date startTime, Date endTime, String shopName) {
        // 过滤
        Criteria criteria = new Criteria();
        Integer paySysType = orderAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        criteria.and("paySysType").is(paySysType);
        if (StrUtil.isNotBlank(shopName)) {
            criteria.and("shopName").regex(Pattern.compile("^.*" + shopName + ".*$", Pattern.CASE_INSENSITIVE));
        }
        criteria.and("isPayed").is(1);
        if (Objects.nonNull(startTime)) {
            criteria.and("payTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }
        ProjectionOperation projectionOperation = Aggregation.project("shopId", "shopName", "payType", "payId", "payTime")
                .and("_id").as("orderIds")
                .and("orderScore").as("scoreCount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(2, 6, 7)).then("$actualTotal").otherwise(0)).as("alipayAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(1, 3, 4, 5, 8)).then("$actualTotal").otherwise(0)).as("wechatAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(9)).then("$actualTotal").otherwise(0)).as("balanceAmount");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "payTime");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderPayInfoVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderPayInfoVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                sortOperation
        );
        logger.info("listOrderPayInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderPayInfoVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderPayInfoVO.class);
        // 取出最终结果
        List<MongoOrderPayInfoVO> list = aggregationResults.getMappedResults();
        list.forEach(data -> data.setPayEntry(0));
        PageShopAccountOrderVO pageShopAccountOrderVO = new PageShopAccountOrderVO();
        pageShopAccountOrderVO.setPayInfoList(BeanUtil.mapAsList(list, OrderPayInfoVO.class));
        return pageShopAccountOrderVO;
    }

    private Criteria getFinanceCriteria(FinanceDetailDTO financeDetailDTO) {
        Criteria criteria = new Criteria();
        criteria.and("isPayed").is(1);
        Integer paySysType = orderAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        criteria.and("paySysType").is(paySysType);
        if (Objects.nonNull(financeDetailDTO.getStartTime())) {
            criteria.and("payTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(financeDetailDTO.getStartTime()))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(financeDetailDTO.getEndTime())));
        }
        if (Objects.nonNull(financeDetailDTO.getPayType()) && financeDetailDTO.getPayType() > -1) {
            criteria.and("payType").is(financeDetailDTO.getPayType());
        }
        if (Objects.nonNull(financeDetailDTO.getOrderId())) {
            criteria.and("_id").is(financeDetailDTO.getOrderId());
        }
        return criteria;
    }

    public List<Long> getSoldNumRankCacheByShopIdAndTime(Long shopId, Integer dayNum, Integer esRenovationSpuSort, Long primaryCategoryId) {
        Date time = null;
        if (dayNum != 0) {
            time = DateUtil.offsetDay(new DateTime(), -dayNum);
        }
        // 过滤
        Criteria criteria = Criteria.where("isPayed").is(1).and("status").ne(6);
        if (Objects.nonNull(time)) {
            criteria.and("payTime").gt(time);
        }
        if (Objects.nonNull(shopId) && !Objects.equals(shopId, 0L)) {
            criteria.and("shopId").is(shopId);
        }
        if (Objects.nonNull(primaryCategoryId)) {
            criteria.and("orderItems.primaryCategoryId").is(primaryCategoryId);
        }
        // 分组
        GroupOperation spuIdGroupOperation = Aggregation.group("orderItems.spuId").sum("orderItems.count").as("totalNum");
        ProjectionOperation projectionOperation = Aggregation.project("totalNum")
                .and("_id").as("spuId");
        Sort.Direction sort;
        if (Objects.equals(esRenovationSpuSort, EsRenovationProductSortEnum.SALE_NUM_DESC.value())) {
            sort = Sort.Direction.DESC;
        } else {
            sort = Sort.Direction.ASC;
        }
        SortOperation sortOperation = Aggregation.sort(sort, "totalNum");
        TypedAggregation<MongoSpuSoldNumRankVO> typedAggregation;
        // 按顺序组合每一个聚合步骤
        typedAggregation =
                Aggregation.newAggregation(
                        MongoSpuSoldNumRankVO.class,
                        Aggregation.match(criteria),
                        Aggregation.unwind("orderItems"),
                        spuIdGroupOperation,
                        projectionOperation,
                        sortOperation,
                        Aggregation.limit(1000)
                );
        logger.info("getSoldNumRankCacheByShopIdAndTime()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoSpuSoldNumRankVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuSoldNumRankVO.class);
        // 取出最终结果
        List<MongoSpuSoldNumRankVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return list.stream().map(MongoSpuSoldNumRankVO::getSpuId).collect(Collectors.toList());
    }

    public GroupInfoVO getGroupInfo(Long groupActivityId) {
        Criteria criteria = new Criteria();
        // 获取该拼团活动
        criteria.and("orderItems.activityId").is(groupActivityId)
                .and("orderItems.activityType").is(OrderActivityType.GROUP.value())
                .and("isPayed").is(1)
                .and("orderType").is(OrderType.GROUP.value())
                .and("status").ne(OrderStatus.CLOSE.value());
        UnwindOperation unwindOperation = Aggregation.unwind("orderItems");
        GroupOperation groupOperation = Aggregation.group(new String[]{"orderItems.activityId", "userId"})
                .count().as("payUserCount")
                .count().as("payOrderCount");
        ProjectionOperation projectionOperation = Aggregation.project("payUserCount", "payOrderCount").and("_id.orderItems.activityId").as("activityId");
        GroupOperation operation = Aggregation.group("activityId")
                .count().as("payUserCount")
                .sum("payOrderCount").as("payOrderCount");
        // 按顺序组合操作
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderStatisticsVO.class,
                Aggregation.match(criteria),
                unwindOperation,
                groupOperation,
                projectionOperation,
                operation
        );
        logger.info("getGroupInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        MongoOrderStatisticsVO orderStatisticsVO = list.get(0);
        GroupInfoVO groupInfoVO = new GroupInfoVO();
        groupInfoVO.setOrderCount(orderStatisticsVO.getPayOrderCount());
        groupInfoVO.setNumberCount(orderStatisticsVO.getPayUserCount());
        return groupInfoVO;
    }

    public List<FormExcelDTO> listFormExcel(FormExcelParamVO formExcelParamVO) {
        Criteria criteria = new Criteria();
        criteria.and("createTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getStartTime()))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getEndTime())));
        if (Objects.nonNull(formExcelParamVO.getShopId())) {
            criteria.and("shopId").is(formExcelParamVO.getShopId());
        }

        String format = getFormatStr(formExcelParamVO);
        ProjectionOperation projectionOperation = Aggregation.project("userId", "actualTotal", "platformAmount", "allCount")
                .and(DateOperators.DateToString.dateOf("createTime").toString(format)).as("formatTime");

        // 根据用户进行分组
        GroupOperation groupOperation = Aggregation.group(new String[]{"formatTime", "userId"})
                .count().as("orderNums")
                .sum("actualTotal").as("orderAmount")
                .sum("platformAmount").as("platformAmount")
                .sum("allCount").as("productNums");

        ProjectionOperation operation = Aggregation.project("orderNums", "orderAmount", "platformAmount", "productNums", "userId").and("_id.formatTime").as("formatTime");
        // 根据时间分组
        GroupOperation timeGroupOperation = Aggregation.group("formatTime")
                .count().as("userNums")
                .sum("orderAmount").as("orderAmount")
                .sum("platformAmount").as("platformAmount")
                .sum("orderNums").as("orderNums")
                .sum("productNums").as("productNums");

        ProjectionOperation timeOperation = Aggregation.project("orderNums", "orderAmount", "platformAmount", "productNums", "userNums").and("_id").as("formatTime");
        // 按顺序组合操作
        TypedAggregation<MongoOrderFormExcelVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderFormExcelVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation,
                operation,
                timeGroupOperation,
                timeOperation
        );
        logger.info("listFormExcel()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderFormExcelVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderFormExcelVO.class);
        // 取出最终结果
        List<MongoOrderFormExcelVO> list = new ArrayList<>(aggregationResults.getMappedResults());
        // 店铺的订单金额要加上平台补贴金额
        if (CollUtil.isNotEmpty(list)) {
            MongoOrderFormExcelVO mongoOrderFormExcelVO = list.get(0);
            if (Objects.isNull(formExcelParamVO.getShopId())) {
                mongoOrderFormExcelVO.setOrderAmount(mongoOrderFormExcelVO.getOrderAmount());
            } else {
                mongoOrderFormExcelVO.setOrderAmount(mongoOrderFormExcelVO.getOrderAmount() + mongoOrderFormExcelVO.getPlatformAmount());
            }
        }

        Map<String, MongoOrderFormExcelVO> orderMap = list.stream().collect(Collectors.toMap(MongoOrderFormExcelVO::getFormatTime, l -> l));
        if (formExcelParamVO.getSelfOperatedAmount() || formExcelParamVO.getSelfOperatedOrderNums() || formExcelParamVO.getSelfOperatedProductNums() || formExcelParamVO.getSelfOperatedUserNums()) {
            ShopSimpleBO shopSimpleBO = new ShopSimpleBO();
            shopSimpleBO.setType(ShopType.SELF_SHOP.value());
            ServerResponseEntity<List<ShopSimpleBO>> serverResponse = shopDetailFeignClient.listSimple(shopSimpleBO);
            if (!serverResponse.isSuccess()) {
                throw new LuckException(serverResponse.getMsg());
            }
            List<ShopSimpleBO> shopList = serverResponse.getData();
            if (CollUtil.isNotEmpty(shopList)) {
                List<Long> shopIds = shopList.stream().map(ShopSimpleBO::getShopId).collect(Collectors.toList());
                // 自营信息
                setSelfInfo(list, formExcelParamVO, orderMap, shopIds);
            }
        }
        if (formExcelParamVO.getPayAmount() || formExcelParamVO.getPayOrderNums() || formExcelParamVO.getPayProductNums() || formExcelParamVO.getPayUserNums()) {
            // 支付信息
            setPayInfo(list, formExcelParamVO, orderMap);
        }
        if (formExcelParamVO.getRefundAmount() || formExcelParamVO.getRefundOrderNums() || formExcelParamVO.getPayProductNums() || formExcelParamVO.getPayUserNums()) {
            // 退款信息
            setRefundInfo(list, formExcelParamVO, orderMap);
        }
        if (CollUtil.isNotEmpty(list)) {
            for (MongoOrderFormExcelVO mongoOrderFormExcelVO : list) {
                // 设默认值
                setDefaultInfo(mongoOrderFormExcelVO);
            }
        }
        return BeanUtil.mapAsList(list, FormExcelDTO.class);
    }

    private void setDefaultInfo(MongoOrderFormExcelVO info) {
        if (Objects.isNull(info.getOrderAmount())) {
            info.setOrderAmount(0L);
        }
        if (Objects.isNull(info.getOrderNums())) {
            info.setOrderNums(0);
        }
        if (Objects.isNull(info.getProductNums())) {
            info.setProductNums(0);
        }
        if (Objects.isNull(info.getUserNums())) {
            info.setUserNums(0);
        }
        if (Objects.isNull(info.getSelfOperatedAmount())) {
            info.setSelfOperatedAmount(0L);
        }
        if (Objects.isNull(info.getSelfOperatedOrderNums())) {
            info.setSelfOperatedOrderNums(0);
        }
        if (Objects.isNull(info.getSelfOperatedProductNums())) {
            info.setSelfOperatedProductNums(0);
        }
        if (Objects.isNull(info.getSelfOperatedUserNums())) {
            info.setSelfOperatedUserNums(0);
        }
        if (Objects.isNull(info.getPayAmount())) {
            info.setPayAmount(0L);
        }
        if (Objects.isNull(info.getPayOrderNums())) {
            info.setPayOrderNums(0);
        }
        if (Objects.isNull(info.getPayProductNums())) {
            info.setPayProductNums(0);
        }
        if (Objects.isNull(info.getPayUserNums())) {
            info.setPayUserNums(0);
        }
        if (Objects.isNull(info.getRefundAmount())) {
            info.setRefundAmount(0L);
        }
        if (Objects.isNull(info.getRefundOrderNums())) {
            info.setRefundOrderNums(0);
        }
    }

    private void setSelfInfo(List<MongoOrderFormExcelVO> list, FormExcelParamVO formExcelParamVO, Map<String, MongoOrderFormExcelVO> orderMap, List<Long> shopIds) {
        // 自营过滤
        Criteria selfCriteria = Criteria.where("isPayed").is(1)
                .and("shopId").in(shopIds)
                .and("createTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getStartTime()))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getEndTime())));

        String format = getFormatStr(formExcelParamVO);
        ProjectionOperation projectionOperation = Aggregation.project("userId", "actualTotal", "allCount")
                .and(DateOperators.DateToString.dateOf("createTime").toString(format)).as("formatTime");

        // 根据用户进行分组
        GroupOperation groupOperation = Aggregation.group(new String[]{"formatTime", "userId"})
                .count().as("selfOperatedOrderNums")
                .sum("actualTotal").as("selfOperatedAmount")
                .sum("allCount").as("selfOperatedProductNums");

        ProjectionOperation operation = Aggregation.project(
                "selfOperatedAmount", "selfOperatedOrderNums", "selfOperatedProductNums").and("_id.formatTime").as("formatTime");
        // 根据时间分组
        GroupOperation timeGroupOperation = Aggregation.group("formatTime")
                .count().as("selfOperatedUserNums")
                .sum("selfOperatedAmount").as("selfOperatedAmount")
                .sum("selfOperatedOrderNums").as("selfOperatedOrderNums")
                .sum("selfOperatedProductNums").as("selfOperatedProductNums");

        ProjectionOperation timeOperation = Aggregation.project(
                "selfOperatedAmount", "selfOperatedOrderNums", "selfOperatedProductNums", "selfOperatedUserNums").and("_id").as("formatTime");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "formatTime");
        // 按顺序组合操作
        TypedAggregation<MongoOrderFormExcelVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderFormExcelVO.class,
                Aggregation.match(selfCriteria),
                projectionOperation,
                groupOperation,
                operation,
                timeGroupOperation,
                timeOperation,
                sortOperation
        );
        logger.info("setSelfInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderFormExcelVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderFormExcelVO.class);
        List<MongoOrderFormExcelVO> selfList = aggregationResults.getMappedResults();
        Map<String, MongoOrderFormExcelVO> selfOrderMap = selfList.stream().collect(Collectors.toMap(MongoOrderFormExcelVO::getFormatTime, l -> l));
        for (String time : selfOrderMap.keySet()) {
            MongoOrderFormExcelVO mongoOrderFormExcelVO = orderMap.get(time);
            boolean isNew = false;
            if (Objects.isNull(mongoOrderFormExcelVO)) {
                mongoOrderFormExcelVO = new MongoOrderFormExcelVO();
                isNew = true;
            }
            MongoOrderFormExcelVO selfInfo = selfOrderMap.get(time);
            mongoOrderFormExcelVO.setFormatTime(time);
            mongoOrderFormExcelVO.setSelfOperatedAmount(selfInfo.getSelfOperatedAmount());
            mongoOrderFormExcelVO.setSelfOperatedOrderNums(selfInfo.getSelfOperatedOrderNums());
            mongoOrderFormExcelVO.setSelfOperatedProductNums(selfInfo.getSelfOperatedProductNums());
            mongoOrderFormExcelVO.setSelfOperatedUserNums(selfInfo.getSelfOperatedUserNums());
            if (isNew) {
                list.add(mongoOrderFormExcelVO);
            }
        }
    }

    private void setPayInfo(List<MongoOrderFormExcelVO> list, FormExcelParamVO formExcelParamVO, Map<String, MongoOrderFormExcelVO> orderMap) {
        // 支付过滤
        Criteria payCriteria = new Criteria();
        payCriteria.and("isPayed").is(1).and("createTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getStartTime()))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getEndTime())));
        if (Objects.nonNull(formExcelParamVO.getShopId())) {
            payCriteria.and("shopId").is(formExcelParamVO.getShopId());
        }
        String format = getFormatStr(formExcelParamVO);
        ProjectionOperation projectionOperation = Aggregation.project("userId", "actualTotal", "allCount")
                .and(DateOperators.DateToString.dateOf("createTime").toString(format)).as("formatTime");

        // 根据用户进行分组
        GroupOperation groupOperation = Aggregation.group("formatTime", "userId")
                .sum("actualTotal").as("payAmount")
                .sum("allCount").as("payProductNums")
                .count().as("payOrderNums");

        ProjectionOperation operation = Aggregation.project(
                "payAmount", "payProductNums", "payOrderNums").and("_id.formatTime").as("formatTime");
        // 根据时间分组
        GroupOperation timeGroupOperation = Aggregation.group("formatTime")
                .count().as("payUserNums")
                .sum("payAmount").as("payAmount")
                .sum("payProductNums").as("payProductNums")
                .sum("payOrderNums").as("payOrderNums");

        ProjectionOperation timeOperation = Aggregation.project(
                "payUserNums", "payAmount", "payProductNums", "payOrderNums").and("_id").as("formatTime");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "formatTime");
        // 按顺序组合操作
        TypedAggregation<MongoOrderFormExcelVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderFormExcelVO.class,
                Aggregation.match(payCriteria),
                projectionOperation,
                groupOperation,
                operation,
                timeGroupOperation,
                timeOperation,
                sortOperation
        );
        logger.info("setPayInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderFormExcelVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderFormExcelVO.class);
        // 取出最终结果
        List<MongoOrderFormExcelVO> payList = aggregationResults.getMappedResults();
        Map<String, MongoOrderFormExcelVO> payOrderMap = payList.stream().collect(Collectors.toMap(MongoOrderFormExcelVO::getFormatTime, l -> l));
        for (String time : payOrderMap.keySet()) {
            MongoOrderFormExcelVO mongoOrderFormExcelVO = orderMap.get(time);
            boolean isNew = false;
            if (Objects.isNull(mongoOrderFormExcelVO)) {
                mongoOrderFormExcelVO = new MongoOrderFormExcelVO();
                isNew = true;
            }
            MongoOrderFormExcelVO payInfo = payOrderMap.get(time);
            mongoOrderFormExcelVO.setFormatTime(time);
            mongoOrderFormExcelVO.setPayUserNums(payInfo.getPayUserNums());
            mongoOrderFormExcelVO.setPayOrderNums(payInfo.getPayOrderNums());
            mongoOrderFormExcelVO.setPayAmount(payInfo.getPayAmount());
            mongoOrderFormExcelVO.setPayProductNums(payInfo.getPayProductNums());
            if (isNew) {
                list.add(mongoOrderFormExcelVO);
            }
        }
    }

    private void setRefundInfo(List<MongoOrderFormExcelVO> list, FormExcelParamVO formExcelParamVO, Map<String, MongoOrderFormExcelVO> orderMap) {
        Criteria criteria = new Criteria();
        criteria.and("createTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getStartTime()))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(formExcelParamVO.getEndTime())));
        if (Objects.nonNull(formExcelParamVO.getShopId())) {
            criteria.and("shopId").is(formExcelParamVO.getShopId());
        }
        criteria.and("returnMoneySts").is(ReturnProcessStatusEnum.SUCCESS.value());
        String format = getFormatStr(formExcelParamVO);
        ProjectionOperation projectionOperation = Aggregation.project("orderId", "refundAmount", "platformRefundAmount")
                .and(DateOperators.DateToString.dateOf("createTime").toString(format)).as("formatTime");

        // 根据用户进行分组
        GroupOperation groupOperation = Aggregation.group("orderId", "formatTime")
                .sum("platformRefundAmount").as("platformRefundAmount")
                .sum("refundAmount").as("refundAmount");

        ProjectionOperation operation = Aggregation.project("refundAmount", "platformRefundAmount").and("_id.formatTime").as("formatTime");

        GroupOperation timeGroupOperation = Aggregation.group("formatTime")
                .count().as("refundOrderNums")
                .sum("platformRefundAmount").as("platformRefundAmount")
                .sum("refundAmount").as("refundAmount");

        ProjectionOperation timeOperation = Aggregation.project("refundOrderNums", "refundAmount", "platformRefundAmount").and("_id").as("formatTime");

        // 按顺序组合操作
        TypedAggregation<MongoOrderRefundFormExcelVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderRefundFormExcelVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation,
                operation,
                timeGroupOperation,
                timeOperation
        );
        logger.info("setRefundInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderRefundFormExcelVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderRefundFormExcelVO.class);
        // 取出最终结果
        List<MongoOrderRefundFormExcelVO> refundList = aggregationResults.getMappedResults();
        Map<String, MongoOrderRefundFormExcelVO> refundOrderMap = refundList.stream().collect(Collectors.toMap(MongoOrderRefundFormExcelVO::getFormatTime, l -> l));
        for (String time : refundOrderMap.keySet()) {
            MongoOrderFormExcelVO mongoOrderFormExcelVO = orderMap.get(time);
            boolean isNew = false;
            if (Objects.isNull(mongoOrderFormExcelVO)) {
                mongoOrderFormExcelVO = new MongoOrderFormExcelVO();
                isNew = true;
            }
            MongoOrderRefundFormExcelVO refundInfo = refundOrderMap.get(time);
            mongoOrderFormExcelVO.setFormatTime(time);
            mongoOrderFormExcelVO.setRefundOrderNums(refundInfo.getRefundOrderNums());
            if (Objects.nonNull(formExcelParamVO.getShopId())) {
                // 商家退款金额 = 退货用户的金额 + 退还平台的补贴金额
                mongoOrderFormExcelVO.setRefundAmount(refundInfo.getRefundAmount() + refundInfo.getPlatformRefundAmount());
            } else {
                // 平台退款金额 = 退货用户的金额
                mongoOrderFormExcelVO.setRefundAmount(refundInfo.getRefundAmount());
            }
            if (isNew) {
                list.add(mongoOrderFormExcelVO);
            }
        }
    }

    private String getFormatStr(FormExcelParamVO formExcelParamVO) {
        String format = "";
        if (Objects.equals(formExcelParamVO.getTimeType(), TimeTypeEnum.DAY.value())) {
            // 天
            format = "%Y-%m-%d";
        } else if (Objects.equals(formExcelParamVO.getTimeType(), TimeTypeEnum.WEEK.value())) {
            // 周
            format = "%Y-%U";
        } else if (Objects.equals(formExcelParamVO.getTimeType(), TimeTypeEnum.MONTH.value())) {
            // 月
            format = "%Y-%m";
        }
        return format;
    }

    public List<UserOrderStatisticVO> getShopOrderCountByUserIdAndShopId(List<Long> userIds, Long shopId) {
        // 过滤
        Criteria criteria = Criteria.where("shopId").is(shopId)
                .and("userId").in(userIds);
        // 分组
        GroupOperation groupOperation = Aggregation.group("userId")
                .count().as("allCount");
        ProjectionOperation projectionOperation = Aggregation.project("allCount")
                .and("_id").as("userId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        projectionOperation
                );
        logger.info("getShopOrderCountByUserIdAndShopId()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderStatisticsVO> list = aggregationResults.getMappedResults();
        List<UserOrderStatisticVO> userOrderStatisticList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoOrderStatisticsVO mongoOrderStatisticsVO : list) {
            UserOrderStatisticVO userOrderStatistic = BeanUtil.map(mongoOrderStatisticsVO, UserOrderStatisticVO.class);
            userOrderStatisticList.add(userOrderStatistic);
        }
        return userOrderStatisticList;
    }

    /**
     * 获取订单商品流量统计数据
     * @param flowOrderItemDTO 查询参数
     * @return
     */
    public List<FlowOrderItemVO> listFlowOrderItems(FlowOrderItemDTO flowOrderItemDTO) {
        // 组装查询条件
        Criteria criteria = Criteria.where("isPayed").is(1)
                .and("orderItems.spuId").is(flowOrderItemDTO.getSpuId());
        if (!Objects.isNull(flowOrderItemDTO.getShopId())) {
            criteria.and("shopId").is(flowOrderItemDTO.getShopId());
        }
        if (!Objects.isNull(flowOrderItemDTO.getStartTime())
                && !Objects.isNull(flowOrderItemDTO.getEndTime())) {
            criteria.and("payTime").gte(flowOrderItemDTO.getStartTime()).lte(flowOrderItemDTO.getEndTime());
        }
        Query query = new Query(criteria).with(Sort.by(Sort.Direction.ASC, "payTime"));
        logger.info("listFlowOrderItems()的mongodb查询语句：{}", query);
        // 查询数据
        List<MongoOrderBO> orders = mongoTemplate.find(query, MongoOrderBO.class);
        if (CollectionUtils.isEmpty(orders)) {
            return new ArrayList<>();
        }
        // 封装为VO类
        FlowOrderItemVO flowOrderItem;
        List<FlowOrderItemVO> flowOrderItems = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoOrderBO order : orders) {
            for (MongoOrderItemBO orderItem : order.getOrderItems()) {
                if (!Objects.equals(orderItem.getSpuId(), flowOrderItemDTO.getSpuId())) {
                    continue;
                }
                flowOrderItem = new FlowOrderItemVO();
                flowOrderItem.setSpuId(orderItem.getSpuId());
                flowOrderItem.setCount(orderItem.getCount());
                flowOrderItem.setAmount(orderItem.getActualTotal());
                flowOrderItem.setPayTime(order.getPayTime());
                flowOrderItems.add(flowOrderItem);
            }
        }
        return flowOrderItems;
    }

    public List<ShopCustomerReqDTO> listUserAndShopDataByTime(Date startTime, Date endTime) {
        Criteria criteria = Criteria.where("shopId").ne(Constant.PLATFORM_SHOP_ID);
        if (Objects.nonNull(startTime)) {
            criteria.and("create_time").gte(startTime).lt(endTime);
        }

        GroupOperation groupOperation = Aggregation.group("shopId", "userId")
                .first("shopId").as("shopId")
                .first("userId").as("userId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<ShopCustomerReqDTO> typedAggregation =
                Aggregation.newAggregation(
                        ShopCustomerReqDTO.class,
                        Aggregation.match(criteria),
                        groupOperation
                );
        logger.info("listUserAndShopDataByTime()的mongodb查询语句： {}", typedAggregation);

        AggregationResults<ShopCustomerReqDTO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, ShopCustomerReqDTO.class);
        return aggregationResults.getMappedResults();
    }
}
