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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.tmerclub.cloud.api.feign.SearchOrderRefundFeignClient;
import com.tmerclub.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.tmerclub.cloud.api.multishop.vo.ShopDetailVO;
import com.tmerclub.cloud.api.order.constant.BuyerReasonType;
import com.tmerclub.cloud.api.order.dto.FormExcelDTO;
import com.tmerclub.cloud.api.order.vo.*;
import com.tmerclub.cloud.api.user.bo.UserOrderStatisticBO;
import com.tmerclub.cloud.api.user.bo.UserOrderStatisticListBO;
import com.tmerclub.cloud.api.user.dto.MemberReqDTO;
import com.tmerclub.cloud.api.vo.EsPageVO;
import com.tmerclub.cloud.common.cache.constant.CacheNames;
import com.tmerclub.cloud.common.cache.util.RedisUtil;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.order.dto.OrderRefundSearchDTO;
import com.tmerclub.cloud.common.order.dto.OrderSearchDTO;
import com.tmerclub.cloud.common.order.vo.EsOrderRefundVO;
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.security.AuthUserContext;
import com.tmerclub.cloud.common.util.Arith;
import com.tmerclub.cloud.common.util.DateUtils;
import com.tmerclub.cloud.order.manager.MongoOrderManager;
import com.tmerclub.cloud.order.manager.MongoOrderRefundManager;
import com.tmerclub.cloud.order.mapper.OrderSettlementMapper;
import com.tmerclub.cloud.order.service.OrderStatisticsService;
import com.tmerclub.cloud.order.vo.OrderOverviewVO;
import com.tmerclub.cloud.order.vo.OrderRefundStatisticsVO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * @author lth
 */
@Service
public class OrderStatisticsServiceImpl implements OrderStatisticsService {

    @Autowired
    private MongoOrderRefundManager mongoOrderRefundManager;

    @Autowired
    private MongoOrderManager mongoOrderManager;

    @DubboReference
    private ShopDetailFeignClient shopDetailFeignClient;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private OrderSettlementMapper orderSettlementMapper;
    @DubboReference
    private SearchOrderRefundFeignClient searchOrderRefundFeignClient;

    public static final Logger log = LoggerFactory.getLogger(OrderStatisticsServiceImpl.class);

    @Override
    public OrderCountVO getOrderCountOfStatusByShopId(Long shopId) {
        OrderCountVO orderCountVO = mongoOrderManager.getOrderCountOfStatusByShopId(shopId);
        // 暂时改为es进行统计退款数量
        OrderRefundSearchDTO searchDTO = new OrderRefundSearchDTO();
        searchDTO.setShopId(shopId);
        searchDTO.setReturnMoneyStsList(new ArrayList<>(Arrays.asList(1, 2, 3, 4)));
        searchDTO.setSysType(AuthUserContext.get().getSysType());
        EsPageVO<EsOrderRefundVO> data = searchOrderRefundFeignClient.pageOrderRefund(searchDTO).getData();
        // 获取退款数量
        orderCountVO.setRefund(Integer.parseInt(String.valueOf(data.getTotal())));
        return orderCountVO;
    }

    @Override
    public List<OrderRefundStatisticsVO> listOrderRefundInfoInDateRange(Long shopId, Date endTime, Integer dayCount) {
        Date startTime = this.getStartTime(endTime, dayCount);
        // 根据时间范围查询订单概况与退款数据
        List<OrderRefundStatisticsVO> orderRefundStatisticsVOList = mongoOrderRefundManager.listOrderRefundInfoByShopIdAndDateRange(shopId, startTime, endTime, dayCount);
        List<OrderOverviewVO> orderOverviewVOList = mongoOrderManager.listOrderOverviewInfoByShopIdAndDateRange(shopId, startTime, endTime, dayCount);
        Map<?, OrderRefundStatisticsVO> orderRefundStatisticsMap = orderRefundStatisticsVOList.stream().collect(Collectors.toMap(OrderRefundStatisticsVO::getRefundDateToString, o -> o));
        Map<?, OrderOverviewVO> orderOverviewMap = orderOverviewVOList.stream().collect(Collectors.toMap(OrderOverviewVO::getTimeData, o -> o));
        // 返回结果
        List<OrderRefundStatisticsVO> refundStatisticsVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        // 处理数据
        for (int i = 0; i < dayCount; i++) {
            OrderRefundStatisticsVO resItem = new OrderRefundStatisticsVO();
            // 日期key
            String dateKey = DateUtils.dateToStrYmd(startTime);
            OrderOverviewVO orderOverviewVO = orderOverviewMap.get(dateKey);
            OrderRefundStatisticsVO refundStatisticsVO = orderRefundStatisticsMap.get(dateKey);

            if (Objects.nonNull(orderOverviewVO)) {
                resItem.setTotalOrderCount(orderOverviewVO.getPayOrderCount());
                if (Objects.nonNull(refundStatisticsVO)) {
                    resItem.setRefundRate(this.getRatioRate(refundStatisticsVO.getRefundCount(), orderOverviewVO.getPayOrderCount()));
                    resItem.setPayActualTotal(refundStatisticsVO.getPayActualTotal());
                    resItem.setRefundCount(refundStatisticsVO.getRefundCount());
                } else {
                    resItem.setRefundRate(0.0);
                    resItem.setPayActualTotal(0L);
                    resItem.setRefundCount(0);
                }
            } else {
                resItem.setTotalOrderCount(0);
                if (Objects.nonNull(refundStatisticsVO)) {
                    resItem.setRefundRate(100.00);
                    resItem.setPayActualTotal(refundStatisticsVO.getPayActualTotal());
                    resItem.setRefundCount(refundStatisticsVO.getRefundCount());
                } else {
                    resItem.setRefundRate(0.0);
                    resItem.setPayActualTotal(0L);
                    resItem.setRefundCount(0);
                }
            }
            resItem.setRefundDateToString(dateKey);
            refundStatisticsVOList.add(resItem);
            startTime = getNextTime(startTime);
        }
        return refundStatisticsVOList;
    }

    @Override
    public List<OrderRefundStatisticsVO> listRefundRankingByProd(Long shopId, Date endTime, Integer dayCount) {
        Date startTime = this.getStartTime(endTime, dayCount);
        return mongoOrderRefundManager.listRefundRankingByProd(shopId, startTime, endTime);
    }

    @Override
    public OrderOverviewVO getToDayInfoByHour(Long shopId, Date startTime, Date endTime) {
        OrderOverviewVO orderOverviewVO = this.getOrderStatisticsByShopIdAndDateRange(shopId, startTime, endTime);
        List<Double> actualList = this.getActualByHour(shopId, startTime, endTime);
        orderOverviewVO.setPayActualList(actualList);
        return orderOverviewVO;
    }

    @Override
    public OrderOverviewVO getCurrentMonthByDay(Long shopId, Date startTime, Date endTime) {
        OrderOverviewVO orderOverviewVO = this.getOrderStatisticsByShopIdAndDateRange(shopId, startTime, endTime);
        this.getOrderInfoByDayCount(shopId, endTime, Calendar.getInstance().get(Calendar.DAY_OF_MONTH), orderOverviewVO);
        return orderOverviewVO;
    }

    @Override
    public List<OrderRefundStatisticsVO> listRefundRankingByReason(Long shopId, Date endTime, int dayCount) {
        Date startTime = this.getStartTime(endTime, dayCount);
        List<OrderRefundStatisticsVO> ranking = mongoOrderRefundManager.listRefundRankingByReason(shopId, startTime, endTime);
        Integer totalRefundCount = 0;
        for (OrderRefundStatisticsVO orderRefundStatisticsVO : ranking) {
            totalRefundCount += orderRefundStatisticsVO.getRefundCount();
        }
        for (OrderRefundStatisticsVO orderRefundStatisticsVO : ranking) {
            try {
                BuyerReasonType reasonType = BuyerReasonType.instance(Integer.valueOf(orderRefundStatisticsVO.getBuyerReason()));
                if (Objects.nonNull(reasonType)) {
                    orderRefundStatisticsVO.setBuyerReason(reasonType.getCn());
                }
            } catch (NumberFormatException numberFormatException) {
                log.error(numberFormatException.getMessage(), numberFormatException);
            }
            orderRefundStatisticsVO.setRefundRate(Arith.div(Arith.mul(orderRefundStatisticsVO.getRefundCount(), 100), totalRefundCount, 2));
        }
        ranking.sort(Comparator.comparing(OrderRefundStatisticsVO::getRefundRate).reversed());
        return ranking;
    }

    @Override
    public OrderOverviewVO getDetailInfoByHour(Long shopId) {
        Date now = new Date();
        // 获取今天的订单概况
        OrderOverviewVO toDayInfoByHour = this.getOrderStatisticsByShopIdAndDateRange(shopId, DateUtil.beginOfDay(now), DateUtil.endOfDay(now));
        // 获取昨天的订单概况
        OrderOverviewVO yesToDayInfoByHour = this.getOrderStatisticsByShopIdAndDateRange(shopId, DateUtil.beginOfDay(DateUtils.getBeforeDay(now, -1)), DateUtil.endOfDay(DateUtils.getBeforeDay(now, -1)));
        // 获取今天的支付金额列表
        List<Double> toDayActualList = this.getActualByHour(shopId, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date()));
        // 获取昨天的支付金额列表
        List<Double> yesToDayActualList = this.getActualByHour(shopId, DateUtil.beginOfDay(DateUtils.getBeforeDay(now, -1)), DateUtil.endOfDay(DateUtils.getBeforeDay(now, -1)));
        toDayInfoByHour.setPayActualList(toDayActualList);
        toDayInfoByHour.setYesterdayPayActualList(yesToDayActualList);
        // 计算今天与昨天数值变化的比率
        toDayInfoByHour.setYesterdayPayUserRate(this.getChangeRate(toDayInfoByHour.getPayUserCount(), yesToDayInfoByHour.getPayUserCount()));
        toDayInfoByHour.setYesterdayPayOrderRate(this.getChangeRate(toDayInfoByHour.getPayOrderCount(), yesToDayInfoByHour.getPayOrderCount()));
        toDayInfoByHour.setYesterdayRefundRate(this.getChangeRate(toDayInfoByHour.getRefund(), yesToDayInfoByHour.getRefund()));
        toDayInfoByHour.setYesterdayOnePriceRate(this.getChangeRate(toDayInfoByHour.getOnePrice(), yesToDayInfoByHour.getOnePrice()));
        toDayInfoByHour.setYesterdayPayActualRate(this.getChangeRate(toDayInfoByHour.getPayActual(), yesToDayInfoByHour.getPayActual()));
        return toDayInfoByHour;
    }

    @Override
    public List<OrderOverviewVO> listSpuRankingByOrderCount(Long shopId, Date endTime, int dayCount, int limit) {
        Date startTime = this.getStartTime(endTime, dayCount);
        return mongoOrderManager.listSpuRankingByOrderCount(shopId, startTime, endTime, limit);
    }

    @Override
    public List<OrderOverviewVO> listShopRankingByPayActual(Date endTime, int dayCount, int limit) {
        Date startTime = this.getStartTime(endTime, dayCount);
        List<OrderOverviewVO> orderOverviewVOList = mongoOrderManager.listShopRankingByPayActual(startTime, endTime, limit);
        if (CollUtil.isEmpty(orderOverviewVOList)) {
            // 数据为空，直接返回
            log.info("listShopRankingByPayActual 数据为空，直接返回");
            return orderOverviewVOList;
        }
        List<Long> shopIds = orderOverviewVOList.stream().map(OrderOverviewVO::getShopId).collect(Collectors.toList());
        ServerResponseEntity<List<ShopDetailVO>> listServerResponseEntity = shopDetailFeignClient.listByShopIds(shopIds);
        if (!listServerResponseEntity.isSuccess()) {
            throw new LuckException(listServerResponseEntity.getMsg());
        }
        if (CollUtil.isEmpty(listServerResponseEntity.getData())) {
            return new ArrayList<>(0);
        }
        // 赋值店铺名称
        Map<Long, ShopDetailVO> shopDetailMap = listServerResponseEntity.getData().stream().collect(Collectors.toMap(ShopDetailVO::getShopId, p -> p));
        Iterator<OrderOverviewVO> iterator = orderOverviewVOList.iterator();
        while (iterator.hasNext()) {
            OrderOverviewVO orderOverviewVO = iterator.next();
            if (Objects.isNull(shopDetailMap.get(orderOverviewVO.getShopId()))) {
                iterator.remove();
                continue;
            }
            orderOverviewVO.setShopName(shopDetailMap.get(orderOverviewVO.getShopId()).getShopName());
        }
        return orderOverviewVOList;
    }

    @Override
    public List<OrderRefundStatisticsVO> listShopRankingByRefundCount(Date endTime, Integer dayCount, Integer limit) {
        Date startTime = this.getStartTime(endTime, dayCount);
        List<OrderRefundStatisticsVO> orderRefundStatisticsVOList = mongoOrderRefundManager.listShopRankingByRefundCount(startTime, endTime, limit);
        if (CollUtil.isEmpty(orderRefundStatisticsVOList)) {
            // 数据为空，直接返回
            log.info("listShopRankingByRefundCount 数据为空，直接返回");
            return orderRefundStatisticsVOList;
        }
        List<Long> shopIds = orderRefundStatisticsVOList.stream().map(OrderRefundStatisticsVO::getShopId).collect(Collectors.toList());
        ServerResponseEntity<List<ShopDetailVO>> listServerResponseEntity = shopDetailFeignClient.listByShopIds(shopIds);
        if (!listServerResponseEntity.isSuccess()) {
            throw new LuckException(listServerResponseEntity.getMsg());
        }
        if (CollUtil.isEmpty(listServerResponseEntity.getData())) {
            log.info("listShopRankingByRefundCount 店铺数据为空，直接返回");
            return new ArrayList<>(0);
        }
        // 赋值店铺名称
        Map<Long, ShopDetailVO> shopDetailMap = listServerResponseEntity.getData().stream().collect(Collectors.toMap(ShopDetailVO::getShopId, p -> p));
        Iterator<OrderRefundStatisticsVO> iterator = orderRefundStatisticsVOList.iterator();
        while (iterator.hasNext()) {
            OrderRefundStatisticsVO orderRefundStatisticsVO = iterator.next();
            if (Objects.isNull(shopDetailMap.get(orderRefundStatisticsVO.getShopId()))) {
                iterator.remove();
                continue;
            }
            orderRefundStatisticsVO.setShopName(shopDetailMap.get(orderRefundStatisticsVO.getShopId()).getShopName());
        }
        return orderRefundStatisticsVOList;
    }

    @Override
    public OrderOverviewVO getOrderInfoByDayCountAndShopId(Long shopId, Integer dayCount) {
        Date endTime = DateUtil.endOfDay(DateUtil.date());
        Date startTime = this.getStartTime(endTime, dayCount);
        OrderOverviewVO orderOverviewVO = this.getOrderStatisticsByShopIdAndDateRange(shopId, startTime, endTime);
        this.getOrderInfoByDayCount(shopId, endTime, dayCount, orderOverviewVO);
        return orderOverviewVO;
    }

    @Override
    public List<UserOrderStatisticVO> countOrderByUserIds(List<Long> userIds, Long shopId) {
        // 统计用户的订单相关信息
        // es一个月内的，直接查数据库(一个月后的数据进行冷备)，一个月之前的数据查es
        // 所以需要将数据库中的订单信息和es中的订单加起来才行完整的订单信息

        // 查询数据库中订单信息
        List<UserOrderStatisticVO> orderStatisticList = mongoOrderManager.statisticalUserOrderData(userIds, shopId);
        List<UserOrderStatisticVO> orderRefundStatisticList = mongoOrderRefundManager.statisticalUserOrderData(userIds, shopId);
        List<UserOrderStatisticVO> totalOrderTimesList = mongoOrderManager.getShopOrderCountByUserIdAndShopId(userIds, shopId);

        if (CollUtil.isEmpty(orderStatisticList)) {
            return orderStatisticList;
        }

        Map<Long, UserOrderStatisticVO> map = orderRefundStatisticList.stream().collect(Collectors.toMap(UserOrderStatisticVO::getUserId, u -> u));
        Map<Long, UserOrderStatisticVO> orderStatisticMap = orderStatisticList.stream().collect(Collectors.toMap(UserOrderStatisticVO::getUserId, u -> u));
        for (UserOrderStatisticVO userOrderStatisticVO : totalOrderTimesList) {
            if(!orderStatisticMap.containsKey(userOrderStatisticVO.getUserId())){
                userOrderStatisticVO.setConsTimes(0);
                userOrderStatisticVO.setConsAmount(new BigDecimal(0));
                userOrderStatisticVO.setActualAmount(new BigDecimal(0));
                userOrderStatisticVO.setReduceAmount(new BigDecimal(0));
                userOrderStatisticVO.setReConsTime(null);
                userOrderStatisticVO.setAverDiscount(new BigDecimal(0));
            }else{
                UserOrderStatisticVO userOrderStatistic = orderStatisticMap.get(userOrderStatisticVO.getUserId());
                userOrderStatisticVO.setConsTimes(userOrderStatistic.getConsTimes());
                userOrderStatisticVO.setConsAmount(userOrderStatistic.getConsAmount());
                userOrderStatisticVO.setActualAmount(userOrderStatistic.getActualAmount());
                userOrderStatisticVO.setReduceAmount(userOrderStatistic.getReduceAmount());
                userOrderStatisticVO.setReConsTime(userOrderStatistic.getReConsTime());
                userOrderStatisticVO.setAverDiscount(userOrderStatistic.getAverDiscount());

            }
            if (!map.containsKey(userOrderStatisticVO.getUserId())) {
                userOrderStatisticVO.setAfterSaleAmount(new BigDecimal(0));
                userOrderStatisticVO.setAfterSaleTimes(0);
            }else{
                UserOrderStatisticVO userOrderRefund = map.get(userOrderStatisticVO.getUserId());
                userOrderStatisticVO.setAfterSaleAmount(userOrderRefund.getAfterSaleAmount());
                userOrderStatisticVO.setAfterSaleTimes(userOrderRefund.getAfterSaleTimes());
            }
        }

        return totalOrderTimesList;
    }

    @Override
    public UserOrderStatisticBO getPaidMemberByParam(MemberReqDTO param) {
        return mongoOrderManager.getPaidMemberByParam(param);
    }

    @Override
    public UserOrderStatisticListBO getMemberPayData(MemberReqDTO param) {
        return mongoOrderManager.getMemberPayData(param);
    }

    @Override
    public List<Long> getOrderUserIdsBySearchDTO(OrderSearchDTO orderSearchDTO) {
        return mongoOrderManager.getOrderUserIdsBySearchDTO(orderSearchDTO);
    }

    @Override
    public List<Long> listUserIdByPurchaseNum(Integer isPayed, Integer deleteStatus, Date startDate, Date endDate, Integer status, Long minNum, Long maxNum) {
        return mongoOrderManager.listUserIdByPurchaseNum(isPayed, deleteStatus, startDate, endDate, status, minNum, maxNum);
    }

    @Override
    public List<Long> listUserIdByAverageActualTotal(Integer isPayed, Integer deleteStatus, Date startDate, Date endDate, Integer status, Long minAmount, Long maxAmount) {
        return mongoOrderManager.listUserIdByAverageActualTotal(isPayed, deleteStatus, startDate, endDate, status, minAmount, maxAmount);
    }

    @Override
    public List<OrderProdEffectRespVO> getProdEffectByDateAndProdIds(List<Long> spuIds, Date startTime, Date endTime) {
        return mongoOrderManager.getProdEffectByDateAndProdIds(spuIds, startTime, endTime);
    }

    @Override
    public List<FlowOrderVO> listFlowOrderByOrderIds(Collection<Long> orderIds) {
        return mongoOrderManager.listFlowOrderByOrderIds(orderIds);
    }

    @Override
    public List<Long> getInsightSpuIdsByDate(Integer begin, Integer size, Long shopId, Date startTime, Date endTime) {
        return mongoOrderManager.getInsightSpuIdsByDate(begin, size, shopId, startTime, endTime);
    }

    @Override
    public List<SumAmountVO> listSumDataByUserIds(List<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return new ArrayList<>(0);
        }
        return mongoOrderManager.listSumDataByUserIds(userIds);
    }

    @Override
    public Long hasBuySuccessProd(Long spuId, Long userId) {
        return mongoOrderManager.hasBuySuccessProd(spuId, userId);
    }

    @Override
    public UserShoppingDataVO calculateUserInShopData(Long userId) {
        return mongoOrderManager.calculateUserInShopData(userId);
    }

    @Override
    public List<OrderActivityVO> getActivitySalesQuantity(List<Long> activityIds, Integer activityType) {
        return mongoOrderManager.getActivitySalesQuantity(activityIds, activityType);
    }

    @Override
    public Long countActivityOrderSale(Long skuId, Long activityId, Integer activityType) {
        // 查询sku销量不需要缓存
        if (Objects.nonNull(skuId)) {
            return mongoOrderManager.countActivityOrderSale(skuId, activityId, activityType);
        }
        // 查询活动的销量，实时性要求不高，可以通过缓存获取
        String key = "ACTIVETY_ORDER_SALE" + CacheNames.UNION_KEY + activityId + CacheNames.UNION_KEY + activityType;
        return getActivityOrderSaleCache(key, activityId, activityType);
    }

    @Override
    public AccountOrderDetailVO getAccountOrderDetail(Date startTime, Date endTime, String shopName, Long shopId) {
        return mongoOrderManager.getAccountOrderDetail(startTime, endTime, shopName, shopId);
    }

    @Override
    public PageShopAccountOrderVO listShopAccountDetail(Date startTime, Date endTime, String shopName, Integer pageSize, Integer pageNum) {
        return mongoOrderManager.listShopAccountDetail(startTime, endTime, shopName, pageSize, pageNum);
    }

    @Override
    public PageShopAccountOrderVO listOrderPayInfo(Date startTime, Date endTime, Long shopId, String shopName, Integer pageSize, Integer pageNum) {
        if (Objects.isNull(pageNum) && Objects.isNull(pageSize)) {
            return mongoOrderManager.listOrderPayInfo(startTime, endTime, shopName);
        }
        return mongoOrderManager.pageOrderPayInfo(startTime, endTime, shopId, pageSize, pageNum);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Long> addSoldNumRankCacheByShopIdAndTime(String key, Long shopId, Integer time, Integer expireTime, Integer esRenovationSpuSort, Long primaryCategoryId) {
        List<Long> spuIds = RedisUtil.getListRange(key, 0L, 10L);

        if (CollectionUtil.isNotEmpty(spuIds)) {
            return spuIds;
        }

        // 加锁，防止缓存击穿
        RLock rLock = redissonClient.getLock("redisson_lock:sold_num_rank:" + key);
        try {
            int lockWait = 30;
            if (rLock.tryLock(lockWait, lockWait, TimeUnit.SECONDS)) {
                spuIds = RedisUtil.getListRange(key, 0L, 10L);
                if (CollectionUtil.isEmpty(spuIds)) {
                    spuIds = mongoOrderManager.getSoldNumRankCacheByShopIdAndTime(shopId, time, esRenovationSpuSort, primaryCategoryId);
                    return spuIds;
                }
            } else {
                throw new LuckException("网络繁忙，请稍后再试");
            }
        } catch (InterruptedException e) {
            log.error("InterruptedException:", e);
        } finally {
            try {
                if (Objects.requireNonNull(rLock).isLocked()) {
                    rLock.unlock();
                }
            } catch (Exception e) {
                log.error("Exception:", e);
            }
        }
        return spuIds;
    }

    @Override
    public GroupInfoVO getGroupInfo(Long groupActivityId) {
        return mongoOrderManager.getGroupInfo(groupActivityId);
    }

    @Override
    public List<FormExcelDTO> listFormExcel(FormExcelParamVO formExcelParamVO) {
        return mongoOrderManager.listFormExcel(formExcelParamVO);
    }

    private Long getActivityOrderSaleCache(String key, Long activityId, Integer activityType) {
        Long activityOrderSale = RedisUtil.getLongValue(key);

        if (Objects.nonNull(activityOrderSale)) {
            return activityOrderSale;
        }

        // 加锁，防止缓存击穿
        RLock rLock = redissonClient.getLock("redisson_lock:order_sale:" + activityType + CacheNames.UNION_KEY + activityId);
        try {
            int lockWait = 1;
            if (rLock.tryLock(lockWait, lockWait, TimeUnit.SECONDS)) {
                activityOrderSale = RedisUtil.getLongValue(key);
                if (activityOrderSale == null) {
                    activityOrderSale = mongoOrderManager.countActivityOrderSale(null, activityId, activityType);
                    RedisUtil.setLongValue(Objects.requireNonNull(key), activityOrderSale, 1);
                }
            } else {
                throw new LuckException("网络繁忙，请稍后再试");
            }
        } catch (InterruptedException e) {
            log.error("InterruptedException:", e);
        } finally {
            try {
                if (Objects.requireNonNull(rLock).isLocked()) {
                    rLock.unlock();
                }
            } catch (Exception e) {
                Objects.requireNonNull(log).error("Exception:", e);
            }
        }
        return activityOrderSale;
    }


    /**
     * 根据店铺Id与时间范围获取订单概况信息
     *
     * @param shopId
     * @param startTime
     * @param endTime
     * @return
     */
    private OrderOverviewVO getOrderStatisticsByShopIdAndDateRange(Long shopId, Date startTime, Date endTime) {
        OrderOverviewVO orderOverviewVO = mongoOrderManager.getOrderOverviewInfoByShopId(shopId, startTime, endTime);
        OrderRefundStatisticsVO orderRefundStatisticsVO = mongoOrderRefundManager.getOrderRefundInfoByShopId(shopId, startTime, endTime);
        double onePrice = (orderOverviewVO.getPayUserCount() == 0) ?
                0 : (Arith.div(orderOverviewVO.getPayActual(), orderOverviewVO.getPayUserCount(), 2));
        orderOverviewVO.setOnePrice(onePrice);
        orderOverviewVO.setRefund(orderRefundStatisticsVO.getPayActualTotal());
        orderOverviewVO.setChargebackCount(orderRefundStatisticsVO.getRefundCount());
        return orderOverviewVO;
    }

    /**
     * 根据店铺Id与时间范围按小时分段获取数据支付金额列表
     *
     * @param shopId
     * @param startTime
     * @param endTime
     */
    private List<Double> getActualByHour(Long shopId, Date startTime, Date endTime) {
        List<OrderOverviewVO> orderOverviewVOList = mongoOrderManager.listActualByHour(shopId, startTime, endTime);
        Map<Integer, Double> payMap = new HashMap<>(orderOverviewVOList.size());
        // mongo格式化后的时间字符串存在时差，需转化为对应时区的时间
        int offset = TimeZone.getDefault().getRawOffset() / 3600000;
        for (OrderOverviewVO temp : orderOverviewVOList) {
            int hour = (Integer.parseInt(temp.getTimeData()) + offset) % 24;
            payMap.put(hour, temp.getPayActual());
        }
        List<Double> nowActual = new ArrayList<>(Constant.INITIAL_CAPACITY);
        double sum = 0.00;
        for (int i = 0; i < Constant.MAX_HOUR_NUM_BY_DAY; i++) {
            if (payMap.get(i) != null) {
                sum = Arith.add(sum, Arith.div(payMap.get(i), 100, 2));
            }
            nowActual.add(Arith.round(sum, 2));
        }
        return nowActual;
    }

    /**
     * 根据店铺Id与时间范围按天分段获取支付金额列表
     *
     * @param shopId
     * @param dayCount
     * @param endTime
     * @param orderOverviewVO
     */
    private void getOrderInfoByDayCount(Long shopId, Date endTime, Integer dayCount, OrderOverviewVO orderOverviewVO) {
        Date startTime = this.getStartTime(endTime, dayCount);
        // 获取支付金额列表
        List<OrderOverviewVO> orderOverviewVOList = mongoOrderManager.listOrderOverviewInfoByShopIdAndDateRange(shopId, startTime, endTime, dayCount);
        Map<?, OrderOverviewVO> orderOverviewMap = orderOverviewVOList.stream().collect(Collectors.toMap(OrderOverviewVO::getTimeData, o -> o));

        List<Double> actualList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        List<String> dateToStringList = new ArrayList<>(Constant.INITIAL_CAPACITY);

        for (int i = 0; i < dayCount; i++) {
            if (orderOverviewMap.containsKey(DateUtils.dateToStrYmd(startTime))) {
                actualList.add(Arith.div(orderOverviewMap.get(DateUtils.dateToStrYmd(startTime)).getPayActual(), 100, 2));
            } else {
                actualList.add(0.0);
            }
            dateToStringList.add(DateUtils.dateToStrYmd(startTime));
            startTime = getNextTime(startTime);
        }

        orderOverviewVO.setDateToStringList(dateToStringList);
        orderOverviewVO.setPayActualList(actualList);

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(endTime);
        // 获取本月的第几天
        int monthDay = calendar.get(Calendar.DAY_OF_MONTH);

        if (dayCount <= monthDay) {
            orderOverviewVO.setCurrentMonthPayActual(orderOverviewVO.getPayActual());
        } else {
            double currentMonthPayActual = 0L;
            // 累加本月支付金额
            for (int i = actualList.size() - 1, j = 0; j < monthDay && i >= 0; j++, i--) {
                currentMonthPayActual = Arith.add(currentMonthPayActual, actualList.get(i));
            }
            orderOverviewVO.setCurrentMonthPayActual(currentMonthPayActual);
        }
    }

    /**
     * 根据结束时间与天数获取起始时间
     *
     * @param endTime
     * @param dayCount
     * @return
     */
    private Date getStartTime(Date endTime, Integer dayCount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(endTime);
        calendar.add(Calendar.DATE, -dayCount + 1);
        return DateUtil.beginOfDay(calendar.getTime());
    }

    private Double getChangeRate(double nowValue, double oldValue) {
        if (Objects.equals(oldValue, 0.0)) {
            return Objects.equals(nowValue, 0.0) ? 0.0 : 1.0;
        }
        double minus = Arith.sub(nowValue, oldValue);
        return Arith.div(minus, oldValue, 4);
    }

    /**
     * 获取下一次循环的开始时间，以及设置后台显示的时间字符串
     *
     * @param startTime 开始时间
     * @return
     */
    private Date getNextTime(Date startTime) {
        startTime = DateUtil.offsetDay(startTime, 1);
        return startTime;
    }

    /**
     * 计算两个数的百分比
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return
     */
    private Double getRatioRate(Integer dividend, Integer divisor) {
        if (Objects.isNull(dividend) || dividend == 0.0) {
            return 0.0;
        }
        if (Objects.isNull(divisor) || divisor == 0.0) {
            return 100.0;
        }
        return Arith.mul(Arith.div(dividend, divisor, 4), 100.00);
    }
}
