
package com.tmerclub.cloud.order.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.order.vo.*;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
import com.tmerclub.cloud.api.user.dto.FinanceDetailDTO;
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.i18n.I18nMessage;
import com.tmerclub.cloud.common.i18n.LanguageEnum;
import com.tmerclub.cloud.common.order.vo.UserOrderStatisticVO;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.DateUtils;
import com.tmerclub.cloud.order.bo.mongo.MongoOrderRefundBO;
import com.tmerclub.cloud.order.constant.FinanceBizType;
import com.tmerclub.cloud.order.dto.multishop.OrderRefundDTO;
import com.tmerclub.cloud.order.service.OrderAllinpayService;
import com.tmerclub.cloud.order.vo.OrderRefundStatisticsVO;
import com.tmerclub.cloud.order.vo.mongo.*;
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.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

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

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private OrderAllinpayService orderAllinpayService;

    public void save(MongoOrderRefundBO mongoOrderRefundBO) {
        mongoTemplate.save(mongoOrderRefundBO);
    }

    public List<OrderRefundStatisticsVO> listRefundRankingByProd(Long shopId, Date startTime, Date endTime) {

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

        //分组
        GroupOperation groupOperation = Aggregation.group("orderItems.spuId")
                .sum("refundCount").as("refundSpuCount")
                .first(spuName).as("refundProdName");
        //过滤
        Criteria criteria = Criteria.where("returnMoneySts").ne(-1);
        filterQuery(criteria, shopId, startTime, endTime);
        // 显示refundSpuCount, refundProdName，字段_id取别名spuId
        ProjectionOperation project = Aggregation.project("refundSpuCount", "refundProdName").and("_id").as("spuId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundStatisticsVO> typedAggregation =
                Aggregation.newAggregation(MongoOrderRefundStatisticsVO.class,
                        Aggregation.match(criteria),
                        Aggregation.unwind("orderItems"),
                        groupOperation,
                        project,
                        Aggregation.sort(Sort.Direction.DESC, "refundSpuCount"),
                        Aggregation.limit(10)
                );
        logger.info("构建listRefundRankingByProd()}的mongodb查询语句： {}", typedAggregation);

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

    public List<OrderRefundStatisticsVO> listRefundRankingByReason(Long shopId, Date startTime, Date endTime) {
        // 分组
        GroupOperation groupOperation = Aggregation.group("buyerReason")
                .count().as("refundCount")
                .sum("refundAmount").as("refundAmount");
        // 过滤
        Criteria criteria = Criteria.where("returnMoneySts").is(5);
        filterQuery(criteria, shopId, startTime, endTime);
        // 显示refundSpuCount, refundProdName，字段_id取别名spuId
        ProjectionOperation project = Aggregation.project("refundCount", "refundAmount").and("_id").as("buyerReason");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundStatisticsVO> typedAggregation =
                Aggregation.newAggregation(MongoOrderRefundStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        project,
                        Aggregation.sort(Sort.Direction.DESC, "refundAmount"),
                        Aggregation.limit(5)
                );
        logger.info("构建listRefundRankingByReason()的mongodb查询语句： {}", typedAggregation);

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

    public OrderRefundStatisticsVO getOrderRefundInfoByShopId(Long shopId, Date startTime, Date endTime) {
        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("refundCount")
                .sum("refundAmount").as("refundAmount")
                .sum("platformRefundAmount").as("platformRefundAmount");
        // 过滤
        Criteria criteria = Criteria.where("returnMoneySts").is(5);
        filterQuery(criteria, shopId, startTime, endTime);

        // 显示refundSpuCount, refundProdName，字段_id取别名spuId
        ProjectionOperation project = Aggregation.project("refundCount", "refundAmount", "platformRefundAmount");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundStatisticsVO> typedAggregation =
                Aggregation.newAggregation(MongoOrderRefundStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        project,
                        Aggregation.sort(Sort.Direction.DESC, "refundAmount")
                );
        logger.info("构建listRefundRankingByReason()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoOrderRefundStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderRefundStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderRefundStatisticsVO> list = aggregationResults.getMappedResults();
        OrderRefundStatisticsVO orderRefundStatisticsVO;
        if (CollUtil.isNotEmpty(list)) {
            MongoOrderRefundStatisticsVO mongoOrderRefundStatisticsVO = list.get(0);
            orderRefundStatisticsVO = BeanUtil.map(mongoOrderRefundStatisticsVO, OrderRefundStatisticsVO.class);
            if (Objects.isNull(shopId)) {
                orderRefundStatisticsVO.setPayActualTotal(mongoOrderRefundStatisticsVO.getRefundAmount());
            } else {
                orderRefundStatisticsVO.setPayActualTotal(mongoOrderRefundStatisticsVO.getRefundAmount() + mongoOrderRefundStatisticsVO.getPlatformRefundAmount());
            }
        } else {
            orderRefundStatisticsVO = new OrderRefundStatisticsVO();
            orderRefundStatisticsVO.setRefundCount(0);
            orderRefundStatisticsVO.setPayActualTotal(0L);
        }
        return orderRefundStatisticsVO;
    }

    public List<OrderRefundStatisticsVO> listOrderRefundInfoByShopIdAndDateRange(Long shopId, Date startTime, Date endTime, Integer dayCount) {
        // 过滤
        Criteria criteria = Criteria.where("returnMoneySts").is(5);
        filterQuery(criteria, shopId, startTime, endTime);

        // 分组前，先处理下要用于分组的时间
        ProjectionOperation projectionOperation = Aggregation.project("refundAmount", "platformRefundAmount", "orderId")
                .and(DateOperators.DateToString.dateOf("refundTime").toString("%Y-%m-%d")).as("refundTime");

        // 根据退款时间和订单id进行分组
        GroupOperation refundTimeAndOrderIdGroupOperation = Aggregation.group("refundTime", "orderId")
                .sum("refundAmount").as("refundAmount")
                .sum("platformRefundAmount").as("platformRefundAmount");
        //
        GroupOperation groupOperation = Aggregation.group("_id.refundTime")
                .count().as("refundCount")
                .sum("refundAmount").as("refundAmount")
                .sum("platformRefundAmount").as("platformRefundAmount");

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

        // 执行聚合操作
        AggregationResults<MongoOrderRefundStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderRefundStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderRefundStatisticsVO> list = aggregationResults.getMappedResults();
        List<OrderRefundStatisticsVO> orderRefundStatisticsList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isNotEmpty(list)) {
            for (MongoOrderRefundStatisticsVO mongoOrderRefundStatisticsVO : list) {
                OrderRefundStatisticsVO orderRefundStatisticsVO = new OrderRefundStatisticsVO();
                orderRefundStatisticsVO.setRefundCount(mongoOrderRefundStatisticsVO.getRefundCount());
                orderRefundStatisticsVO.setRefundDateToString(mongoOrderRefundStatisticsVO.getTimeData());
                orderRefundStatisticsVO.setPayActualTotal(mongoOrderRefundStatisticsVO.getRefundAmount() + mongoOrderRefundStatisticsVO.getPlatformRefundAmount());
                orderRefundStatisticsList.add(orderRefundStatisticsVO);
            }
        }

        return orderRefundStatisticsList;
    }

    public Integer getOrderRefundCountOfStatusByShopId(Long shopId) {
        // 分组前，先处理下要用于分组的时间
        ProjectionOperation projectionOperation = Aggregation.project("_id", "orderId");

        // 根据订单id进行分组
        GroupOperation groupOperation = Aggregation.group("orderId")
                .count().as("refundCount");
        //
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderRefundStatisticsVO.class,
                        Aggregation.match(Criteria.where("shopId").is(shopId)),
                        projectionOperation,
                        groupOperation
                );
        logger.info("listOrderOverviewInfoByShopIdAndDateRange()的mongodb查询语句： {}", typedAggregation);

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

    public List<OrderRefundStatisticsVO> listShopRankingByRefundCount(Date startTime, Date endTime, Integer limit) {
        // 过滤
        Criteria criteria = Criteria.where("returnMoneySts").is(5);
        filterQuery(criteria, null, startTime, endTime);
        // 根据订单id进行分组
        GroupOperation groupOperation = Aggregation.group("shopId")
                .count().as("refundCount");
        ProjectionOperation projectionOperation = Aggregation.project("refundCount").and("_id").as("shopId");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderRefundStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        projectionOperation,
                        Aggregation.sort(Sort.Direction.DESC, "refundCount"),
                        Aggregation.limit(limit)
                );
        logger.info("listShopRankingByRefundCount()的mongodb查询语句： {}", typedAggregation);

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

    public List<UserOrderStatisticVO> statisticalUserOrderData(List<Long> userIds, Long shopId) {
        // 根据订单id进行分组
        GroupOperation groupOperation = Aggregation.group("userId")
                .sum("refundAmount").as("afterSaleAmount")
                .count().as("afterSaleTimes");
        ProjectionOperation projectionOperation = Aggregation.project("afterSaleAmount", "afterSaleTimes").
                and("_id").as("userId");
        Criteria criteria;
        if (Objects.nonNull(shopId) && !Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) {
            criteria = Criteria.where("returnMoneySts").is(5).and("userId").in(userIds).and("shopId").is(shopId);
        } else {
            criteria = Criteria.where("returnMoneySts").is(5).and("userId").in(userIds);
        }
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderRefundStatisticsVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        projectionOperation
                );
        logger.info("statisticalUserOrderData()的mongodb查询语句： {}", typedAggregation);

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

    public OrderRefundStatisticsVO getOrderRefundInfoBySupplierId(Long supplierId, Date startTime, Date endTime) {
        // 过滤
        Criteria criteria = Criteria.where("returnMoneySts").is(5)
                .and("supplierHandleStatus").is(1);
        filterQuery(criteria, null, startTime, endTime);
        if (Objects.nonNull(supplierId)) {
            criteria.and("supplierId").is(supplierId);
        }
        // 根据订单id进行分组
        GroupOperation orderGroupOperation = Aggregation.group("orderId")
                .sum("refundAmount").as("refundAmount")
                .sum("platformRefundAmount").as("platformRefundAmount");

        // 根据订单id进行分组
        GroupOperation groupOperation = Aggregation.group("orderId")
                .count().as("refundCount")
                .sum("refundAmount").as("refundAmount")
                .sum("platformRefundAmount").as("platformRefundAmount");

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

        // 执行聚合操作
        AggregationResults<MongoOrderRefundStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderRefundStatisticsVO.class);
        // 取出最终结果
        List<MongoOrderRefundStatisticsVO> list = aggregationResults.getMappedResults();
        OrderRefundStatisticsVO orderRefundStatisticsVO = new OrderRefundStatisticsVO();
        if (CollUtil.isEmpty(list)) {
            orderRefundStatisticsVO.setPayActualTotal(0L);
            orderRefundStatisticsVO.setRefundCount(0);
            return orderRefundStatisticsVO;
        }

        MongoOrderRefundStatisticsVO mongoOrderRefundStatisticsVO = list.get(0);
        if (Objects.isNull(supplierId)) {
            orderRefundStatisticsVO.setPayActualTotal(mongoOrderRefundStatisticsVO.getRefundAmount());
        } else {
            orderRefundStatisticsVO.setPayActualTotal(mongoOrderRefundStatisticsVO.getRefundAmount() + mongoOrderRefundStatisticsVO.getPlatformRefundAmount());
        }
        orderRefundStatisticsVO.setRefundCount(mongoOrderRefundStatisticsVO.getRefundCount());
        return orderRefundStatisticsVO;
    }

    public Integer getOrderCountBySupplierId(Long supplierId) {
        // 过滤
        Criteria criteria = Criteria.where("returnMoneySts").is(1)
                .and("supplierHandleStatus").is(0)
                .and("supplierId").is(supplierId);

        // 根据订单id进行分组
        GroupOperation orderGroupOperation = Aggregation.group("orderId");

        LookupOperation lookupOperation = Aggregation.lookup("order", "_id", "orderId", "order");

        Criteria lookupCriteria = Criteria.where("order.supplierId").is(supplierId)
                .and("order.supplierDeliveryType").is(1)
                .and("order.waitPurchase").ne(1);

        // 根据订单id进行分组
        GroupOperation groupOperation = Aggregation.group()
                .count().as("refundCount");

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

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

    /**
     * 根据参数获取商品退款订单数据分析
     *
     * @param spuIds    商品id列表
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 商品退款订单数据
     */
    public List<OrderRefundProdEffectRespVO> getProdRefundEffectByDateAndProdIds(List<Long> spuIds, Date startTime, Date endTime) {
        // 过滤
        Criteria criteria = Criteria.where("createTime")
                .gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime)))
                .lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)))
                .and("returnMoneySts")
                .gte(1)
                .lte(5)
                .and("orderItems.spuId")
                .in(spuIds);
        // 筛选退款项数据字段
        ProjectionOperation refundProjectionOperation = Aggregation.project("refundId", "refundType", "returnMoneySts", "userId")
                .and("orderItems.spuId").as("spuId")
                .and("orderItems.count").as("count")
                .and(ConditionalOperators.when(Criteria.where("returnMoneySts").is(5)).then("$orderItems.refundAmount").otherwise(0)).as("refundAmount")
                .and(ConditionalOperators.when(Criteria.where("returnMoneySts").is(5)).then(1).otherwise(0)).as("refundSuccessNum")
                .and(ConditionalOperators.when(Criteria.where("returnMoneySts").is(5)).then(1).otherwise(0)).as("refundSuccessPerson");


        // 根据用户id进行分组
        GroupOperation userGroupOperation = Aggregation.group("spuId", "userId")
                .sum("refundAmount").as("refundSuccessAmount")
                .sum("count").as("refundNum")
                .sum("refundSuccessNum").as("refundSuccessNum")
                .sum("refundSuccessPerson").as("refundSuccessPerson");

        // 筛选用户id分类后的数据字段
        ProjectionOperation userProjectionOperation = Aggregation.project("refundSuccessAmount", "refundNum", "refundSuccessNum", "refundSuccessPerson")
                .and("_id.spuId").as("spuId")
                .and(ConditionalOperators.when(Criteria.where("refundSuccessPerson").gte(1)).then(1).otherwise(0)).as("refundSuccessPerson");

        // 根据用户id进行分组
        GroupOperation groupOperation = Aggregation.group("spuId")
                .sum("refundSuccessAmount").as("refundSuccessAmount")
                .sum("refundNum").as("refundNum")
                .sum("refundSuccessNum").as("refundSuccessNum")
                .count().as("refundPerson")
                .sum("refundSuccessPerson").as("refundSuccessPerson");

        ProjectionOperation projectionOperation = Aggregation.project("refundSuccessAmount", "refundNum", "refundSuccessNum", "refundPerson", "refundSuccessPerson")
                .and("_id").as("spuId");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoOrderRefundStatisticsVO.class,
                        Aggregation.match(criteria),
                        Aggregation.unwind("orderItems"),
                        refundProjectionOperation,
                        Aggregation.match(Criteria.where("spuId").in(spuIds)),
                        userGroupOperation,
                        userProjectionOperation,
                        groupOperation,
                        projectionOperation
                );
        logger.info("getOrderCountBySupplierId()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderRefundStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderRefundStatisticsVO.class);
        List<MongoOrderRefundStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return BeanUtil.mapAsList(list, OrderRefundProdEffectRespVO.class);
    }

    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("refundTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
            } else if (Objects.nonNull(startTime)) {
                criteria.and("refundTime").gte(startTime);
            } else {
                criteria.and("refundTime").lte(endTime);
            }
        }
    }

    public AccountOrderDetailVO getAccountRefundOrderDetail(Date startTime, Date endTime, String shopName, Long shopId) {
        // 过滤
        Criteria criteria = getShopNameCriteria(Objects.isNull(shopId) && StrUtil.isNotBlank(shopName), shopName);
        if (Objects.nonNull(shopId)) {
            criteria.and("shopId").is(shopId);
        }
        if (Objects.nonNull(startTime)) {
            criteria.and("refundTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }
        // 统计退款成功
        criteria.and("returnMoneySts").is(5);

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

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

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

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

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

    public PageShopAccountOrderVO listShopRefundAccountDetail(Date startTime, Date endTime, String shopName,
                                                              Integer pageSize, Integer pageNum) {
        // 过滤
        Criteria criteria = getShopNameCriteria(StrUtil.isNotBlank(shopName), shopName);
        if (Objects.nonNull(startTime)) {
            criteria.and("refundTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }
        criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);

        // 主表返回字段
        ProjectionOperation itemProjectionOperation = Aggregation.project("orderId", "refundScore", "refundAmount", "shopId", "shopName", "payType");

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

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

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

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

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

    private Criteria getShopNameCriteria(boolean shopName, String shopName1) {
        Criteria criteria = new Criteria();
        Integer paySysType = orderAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        criteria.and("paySysType").is(paySysType);
        if (shopName) {
            criteria.and("shopName").regex(Pattern.compile("^.*" + shopName1 + ".*$", Pattern.CASE_INSENSITIVE));
        }
        return criteria;
    }

    public PageVO<FinanceDetailVO> pageRefundFinanceDetail(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("refundId")
                .first("orderId").as("orderId")
                .first("refundTime").as("transDate")
                .first("payType").as("payType")
                .first("decisionTime").as("placeTime")
                .first("refundAmount").as("transAmount")
                .first("shopId").as("shopId")
                .first("shopName").as("shopName")
                .first("userId").as("userId")
                .first("_id").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<MongoFinanceOrderRefundDetailVO> typedAggregation = Aggregation.newAggregation(
                MongoFinanceOrderRefundDetailVO.class,
                Aggregation.match(criteria),
                unwindOperation,
                groupOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );
        logger.info("pageRefundFinanceDetail()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        List<MongoFinanceOrderRefundDetailVO> list = getList(typedAggregation);
        // 总条数
        TypedAggregation<MongoFinanceOrderRefundDetailVO> aggregation = Aggregation.newAggregation(
                MongoFinanceOrderRefundDetailVO.class,
                Aggregation.match(criteria),
                unwindOperation,
                groupOperation
        );
        AggregationResults<MongoFinanceOrderRefundDetailVO> aggregate = mongoTemplate.aggregate(aggregation, MongoFinanceOrderRefundDetailVO.class);
        List<MongoFinanceOrderRefundDetailVO> 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;
    }

    private List<MongoFinanceOrderRefundDetailVO> getList(TypedAggregation<MongoFinanceOrderRefundDetailVO> typedAggregation) {
        AggregationResults<MongoFinanceOrderRefundDetailVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoFinanceOrderRefundDetailVO.class);
        // 取出最终结果
        List<MongoFinanceOrderRefundDetailVO> list = aggregationResults.getMappedResults();
        list.forEach(data -> {
            data.setBizType(FinanceBizType.REFUND.value());
            data.setProdName(data.getProdNames().toString().substring(1, data.getProdNames().toString().length() - 1));
        });
        return list;
    }

    public List<FinanceDetailVO> listRefundFinanceDetail(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("_id").as("refundId")
                .first("refundTime").as("transDate")
                .first("payType").as("payType")
                .first("decisionTime").as("placeTime")
                .first("refundAmount").as("transAmount")
                .first("shopName").as("shopName")
                .first("userId").as("userId")
                .first("_id").as("payIds")
                .push(spuName).as("prodNames");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "transDate");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoFinanceOrderRefundDetailVO> typedAggregation = Aggregation.newAggregation(
                MongoFinanceOrderRefundDetailVO.class,
                Aggregation.match(criteria),
                Aggregation.unwind("orderItems"),
                groupOperation,
                sortOperation
        );
        logger.info("listRefundFinanceDetail()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        List<MongoFinanceOrderRefundDetailVO> list = getList(typedAggregation);
        return BeanUtil.mapAsList(list, FinanceDetailVO.class);
    }

    public PageShopAccountOrderVO pageOrderRefundInfo(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("returnMoneySts").is(5);
        criteria.and("shopId").is(shopId);
        if (Objects.nonNull(startTime)) {
            criteria.and("refundTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }
        ProjectionOperation projectionOperation = getProjectionOperation();
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "refundTime");
        // 分页数据
        SkipOperation skipOperation = Aggregation.skip((long) pageSize * (pageNum - 1));
        LimitOperation limitOperation = Aggregation.limit(pageSize);
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundInfoVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderRefundInfoVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );
        logger.info("pageOrderRefundInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderRefundInfoVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderRefundInfoVO.class);
        // 取出最终结果
        List<MongoOrderRefundInfoVO> list = aggregationResults.getMappedResults();
        list.forEach(data -> data.setPayEntry(0));
        // 总条数
        TypedAggregation<MongoOrderRefundInfoVO> aggregation = Aggregation.newAggregation(
                MongoOrderRefundInfoVO.class,
                Aggregation.match(criteria),
                projectionOperation
        );
        AggregationResults<MongoOrderRefundInfoVO> aggregate = mongoTemplate.aggregate(aggregation, MongoOrderRefundInfoVO.class);
        List<MongoOrderRefundInfoVO> mappedResults = aggregate.getMappedResults();
        PageShopAccountOrderVO pageShopAccountOrderVO = new PageShopAccountOrderVO();
        pageShopAccountOrderVO.setRefundInfoList(BeanUtil.mapAsList(list, OrderRefundInfoVO.class));
        pageShopAccountOrderVO.setTotal((long) mappedResults.size());
        return pageShopAccountOrderVO;
    }

    private static ProjectionOperation getProjectionOperation() {
        return Aggregation.project("shopId", "shopName", "payType", "refundTime", "payId", "refundAmount", "orderId")
                .and("_id").as("refundId")
                .and("refundScore").as("scoreCount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(2, 6, 7)).then("$refundAmount").otherwise(0)).as("alipayAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(1, 3, 4, 5, 8)).then("$refundAmount").otherwise(0)).as("wechatAmount")
                .and(ConditionalOperators.when(Criteria.where("payType").in(9)).then("$refundAmount").otherwise(0)).as("balanceAmount");
    }

    public PageShopAccountOrderVO listOrderRefundInfo(Date startTime, Date endTime, String shopName) {
        Criteria criteria = getShopNameCriteria(StrUtil.isNotBlank(shopName), shopName);
        criteria.and("returnMoneySts").is(5);
        if (Objects.nonNull(startTime)) {
            criteria.and("refundTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(startTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(endTime)));
        }
        ProjectionOperation projectionOperation = getProjectionOperation();
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "refundTime");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoOrderRefundInfoVO> typedAggregation = Aggregation.newAggregation(
                MongoOrderRefundInfoVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                sortOperation
        );
        logger.info("listOrderRefundInfo()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoOrderRefundInfoVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoOrderRefundInfoVO.class);
        // 取出最终结果
        List<MongoOrderRefundInfoVO> list = aggregationResults.getMappedResults();
        list.forEach(data -> data.setPayEntry(0));
        PageShopAccountOrderVO pageShopAccountOrderVO = new PageShopAccountOrderVO();
        pageShopAccountOrderVO.setRefundInfoList(BeanUtil.mapAsList(list, OrderRefundInfoVO.class));
        return pageShopAccountOrderVO;
    }

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

    public List<Long> listShouldUpdateRefund(Date decisionStartTime, Date decisionEndTime, Integer payType, Integer returnMoneySts) {
        //过滤
        Criteria criteria = Criteria.where("returnMoneySts").is(returnMoneySts).and("payType").is(payType);
        criteria.and("decisionTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(decisionStartTime))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(decisionEndTime)));
        criteria.and("refundTime").isNull();
        ProjectionOperation project = Aggregation.project().and("_id").as("refundId");

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

        //执行聚合操作
        AggregationResults<MongoOrderRefundInfoVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoOrderRefundInfoVO.class);
        // 取出最终结果
        List<MongoOrderRefundInfoVO> list = aggregationResults.getMappedResults();
        List<Long> refundIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isNotEmpty(list)) {
            refundIds = list.stream().map(MongoOrderRefundInfoVO::getRefundId).collect(Collectors.toList());
        }
        return refundIds;
    }

    /**
     * 统计退款单数据量
     * @param orderRefundDTO 退款单DTO
     * @return 退款单数量
     */
    public Long countRefundOrder(OrderRefundDTO orderRefundDTO) {
        Criteria criteria = new Criteria();
        if (!Objects.isNull(orderRefundDTO.getUserId())) {
            criteria.and("userId").is(orderRefundDTO.getUserId());
        }
        if (!CollectionUtils.isEmpty(orderRefundDTO.getNotInStatuses())) {
            criteria.and("returnMoneySts").nin(orderRefundDTO.getNotInStatuses());
        }
        Query query = new Query(criteria);
        logger.info("countRefundOrder()的mongodb查询语句： {}", query);
        return mongoTemplate.count(query, MongoOrderRefundBO.class);
    }
}
