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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.multishop.bo.ShopSimpleBO;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
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.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.mongodb.util.MongoPageUtil;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.security.AuthUserContext;
import com.tmerclub.cloud.common.util.DateUtils;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.common.util.PriceUtil;
import com.tmerclub.cloud.constant.DistributedIdKey;
import com.tmerclub.cloud.multishop.constant.ShopWalletAmountType;
import com.tmerclub.cloud.multishop.constant.ShopWalletChangeReason;
import com.tmerclub.cloud.multishop.constant.ShopWalletIoType;
import com.tmerclub.cloud.multishop.dto.ShopWalletLogSearchDTO;
import com.tmerclub.cloud.multishop.mapper.ShopWalletMapper;
import com.tmerclub.cloud.multishop.model.ShopDetail;
import com.tmerclub.cloud.multishop.mongo.MongoShopWalletLogBO;
import com.tmerclub.cloud.multishop.service.ShopAllinpayService;
import com.tmerclub.cloud.multishop.service.ShopDetailService;
import com.tmerclub.cloud.multishop.service.ShopWalletLogService;
import com.tmerclub.cloud.multishop.vo.ShopWalletLogExcelVO;
import com.tmerclub.cloud.multishop.vo.ShopWalletVO;
import com.tmerclub.cloud.multishop.vo.mongo.MongoShopWalletLogVO;
import com.tmerclub.cloud.multishop.vo.mongo.MongoShopWalletVO;
import com.mongodb.client.result.UpdateResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
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.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

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

/**
 * 商家钱包记录
 * @author tmerclub
 */
@Service
public class ShopWalletLogServiceImpl implements ShopWalletLogService {

    private static final Logger logger = LoggerFactory.getLogger(ShopWalletLogServiceImpl.class);

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private ShopDetailService shopDetailService;
    @Autowired
    private MongoPageUtil mongoPageUtil;
    @Autowired
    private ShopWalletMapper shopWalletMapper;
    @Autowired
    private ShopAllinpayService shopAllinpayService;

    @Override
    public PageVO<MongoShopWalletLogBO> pageByParam(PageDTO pageDTO, ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        if (Objects.isNull(shopWalletLogSearchDTO.getReason()) && Objects.nonNull(shopWalletLogSearchDTO.getShopId())) {
            criteria.and("reason").ne(ShopWalletChangeReason.APPLY_RECHARGE.value());
        }
        // 平台搜索
        if (Objects.isNull(shopWalletLogSearchDTO.getShopId())) {
            if (Objects.isNull(shopWalletLogSearchDTO.getAmountType())) {
                // 平台搜索只需要查待结算金额和结算金额的记录
                criteria.and("amountType").in(ShopWalletAmountType.UNSETTLED_AMOUNT.value(), ShopWalletAmountType.SETTLED_AMOUNT.value());
            }
            if (Objects.isNull(shopWalletLogSearchDTO.getReason())) {
                // 平台搜索只需要查订单支付，订单结算，用户退款， 预售失败结算（超时未支付尾款）的记录
                criteria.and("reason")
                        .in(ShopWalletChangeReason.PAY.value(), ShopWalletChangeReason.SETTLED.value(), ShopWalletChangeReason.ORDER_REFUND.value(), ShopWalletChangeReason.PRE_SALE_FAIL.value());
            }
            Criteria platformCriteria = new Criteria();
            // 筛选掉，用户退款且为平台收入金额的记录，因为只是退款不会导致平台收入金额的变化
            platformCriteria.and("reason").is(ShopWalletChangeReason.ORDER_REFUND.value()).and("amountType").is(ShopWalletAmountType.SETTLED_AMOUNT.value());
            criteria.norOperator(platformCriteria);
        }
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return mongoPageUtil.doPage(MongoShopWalletLogBO.class, pageDTO, query);
    }

    @Override
    public MongoShopWalletLogBO getByWalletLogId(Long walletLogId) {
        MongoShopWalletLogBO shopWalletLogDb = mongoTemplate.findById(walletLogId, MongoShopWalletLogBO.class);
        if (Objects.isNull(shopWalletLogDb)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        return shopWalletLogDb;
    }

    @Override
    public List<MongoShopWalletLogBO> listByOrderIdAndReason(Long orderId, Integer shopWalletChangeReason) {
        Criteria criteria = new Criteria();
        criteria.and("orderId").is(orderId);
        if (Objects.nonNull(shopWalletChangeReason)) {
            criteria.and("reason").is(shopWalletChangeReason);
        }
        Query query = new Query(criteria);
        return mongoTemplate.find(query, MongoShopWalletLogBO.class);
    }

    @Override
    public List<MongoShopWalletLogBO> listByParam(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        setShopIdAndReason(shopWalletLogSearchDTO, criteria);
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return mongoTemplate.find(query, MongoShopWalletLogBO.class);
    }

    private static void setShopIdAndReason(ShopWalletLogSearchDTO shopWalletLogSearchDTO, Criteria criteria) {
        if (Objects.isNull(shopWalletLogSearchDTO.getShopId()) && StrUtil.isBlank(shopWalletLogSearchDTO.getShopName())) {
            criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        }
        if (Objects.isNull(shopWalletLogSearchDTO.getReason())) {
            criteria.and("reason").ne(ShopWalletChangeReason.APPLY_RECHARGE.value());
        }
    }

    @Override
    public void saveMongo(MongoShopWalletLogBO mongoShopWalletLogBO) {
        logger.info("插入日志：{}", mongoShopWalletLogBO);
        Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
        mongoShopWalletLogBO.setWalletLogId(walletLogId);
        mongoTemplate.insert(mongoShopWalletLogBO);
    }

    @Override
    public void saveMongoBatch(List<MongoShopWalletLogBO> list) {
        logger.info("插入日志：{}", Json.toJsonString(list));
        if (CollUtil.isEmpty(list)) {
            return;
        }
        // 批量操作
        try {
            mongoTemplate.insert(list, MongoShopWalletLogBO.class);
        } catch (DuplicateKeyException e) {
            logger.info("重复数据插入异常：{}", Json.toJsonString(list));
        }
    }

    @Override
    public PageVO<MongoShopWalletLogBO> pageAllShop(PageDTO pageDTO, ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        setShopIdAndReason(shopWalletLogSearchDTO, criteria);
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));

        PageVO<MongoShopWalletLogBO> page = mongoPageUtil.doPage(MongoShopWalletLogBO.class, pageDTO, query);
        // 店铺名称
        Map<Long, String> shopMap = getShopMap(page.getList().stream().map(MongoShopWalletLogBO::getShopId));

        for (MongoShopWalletLogBO mongoShopWalletLogVO : page.getList()) {
            if (shopMap.containsKey(mongoShopWalletLogVO.getShopId())) {
                mongoShopWalletLogVO.setShopName(shopMap.get(mongoShopWalletLogVO.getShopId()));
            }
        }
        return page;
    }

    @Override
    public List<ShopWalletLogExcelVO> listShopWalletLogExcel(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria;
        if (CollUtil.isNotEmpty(shopWalletLogSearchDTO.getShopIds())) {
            Integer paySysType = shopAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
            criteria = Criteria.where("shopId").in(shopWalletLogSearchDTO.getShopIds()).and("paySysType").is(paySysType);
        } else {
            criteria = getCriteria(shopWalletLogSearchDTO);
        }
        criteria.and("reason").ne(ShopWalletChangeReason.APPLY_RECHARGE.value());
        criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        ProjectionOperation project = Aggregation.project("shopId", "orderId", "refundId", "amountType", "shopIoType", "shopChangeAmount", "reason",
                "userAmount", "platformAmount", "distributionAmount", "shopPlatformCommission", "platformCommission", "shopReduceAmount", "createTime");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "shopId").and(Sort.Direction.DESC, "createTime");
        TypedAggregation<MongoShopWalletLogBO> typedAggregation = Aggregation.newAggregation(
                MongoShopWalletLogBO.class,
                Aggregation.match(criteria),
                project,
                sortOperation
        );

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

        // 执行聚合操作
        AggregationResults<MongoShopWalletLogBO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoShopWalletLogBO.class);
        // 取出最终结果
        List<MongoShopWalletLogBO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        Map<Long, String> shopMap = getShopMap(list.stream().map(MongoShopWalletLogBO::getShopId));
        List<ShopWalletLogExcelVO> result = new ArrayList<>(Constant.INITIAL_CAPACITY);
        int index = 1;
        for (MongoShopWalletLogBO mongoShopWalletLogBO : list) {
            ShopWalletLogExcelVO shopWalletLogExcel = new ShopWalletLogExcelVO();
            shopWalletLogExcel.setSeq(String.valueOf(index));
            shopWalletLogExcel.setShopName(shopMap.get(mongoShopWalletLogBO.getShopId()));
            shopWalletLogExcel.setAmountType(ShopWalletAmountType.getAmountTypeName(mongoShopWalletLogBO.getAmountType()));
            shopWalletLogExcel.setIoType(ShopWalletIoType.getIoName(mongoShopWalletLogBO.getShopIoType()));
            shopWalletLogExcel.setChangeAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getShopChangeAmount()).toString());
            shopWalletLogExcel.setOrderId(Objects.isNull(mongoShopWalletLogBO.getOrderId()) ? "" : mongoShopWalletLogBO.getOrderId().toString());
            shopWalletLogExcel.setRefundId(Objects.isNull(mongoShopWalletLogBO.getRefundId()) ? "" : mongoShopWalletLogBO.getRefundId().toString());
            shopWalletLogExcel.setUserAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getUserAmount()).toString());
            shopWalletLogExcel.setShopReduceAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getShopReduceAmount()).toString());
            shopWalletLogExcel.setPlatformAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getPlatformAmount()).toString());
            shopWalletLogExcel.setDistributionAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getDistributionAmount()).toString());
            shopWalletLogExcel.setPlatformCommission(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getPlatformCommission()).toString());
            shopWalletLogExcel.setShopPlatformCommission(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getShopPlatformCommission()).toString());
            shopWalletLogExcel.setReasonStr(ShopWalletChangeReason.getReason(mongoShopWalletLogBO.getReason()));
            shopWalletLogExcel.setCreateTime(mongoShopWalletLogBO.getCreateTime());
            result.add(shopWalletLogExcel);
            index++;
        }
        return result;
    }

    private Map<Long, String> getShopMap(Stream<Long> list) {
        List<Long> shopIds = list.collect(Collectors.toList());
        List<ShopDetail> shopDetails = shopDetailService.listByShopIds(shopIds);
        return shopDetails.stream().collect(Collectors.toMap(ShopDetail::getShopId, ShopDetail::getShopName));
    }

    @Override
    public PageVO<MongoShopWalletLogBO> getRechargePageByShopId(PageDTO pageDTO, Long shopId) {
        Integer paySysType = shopAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        Query query = new Query();
        query.addCriteria(Criteria.where("shopId").is(shopId)
                .and("paySysType").is(paySysType)
                .and("reason").is(ShopWalletChangeReason.SUCCESS_RECHARGE.value()));
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return mongoPageUtil.doPage(MongoShopWalletLogBO.class, pageDTO, query);
    }

    @Override
    public long countInAmountByOrderId(Long orderId, Integer shopWalletChangeReason) {
        return countInAmountByOrderIds(Collections.singletonList(orderId), shopWalletChangeReason);
    }

    @Override
    public Long countInAmountByOrderIds(List<Long> orderIds, Integer shopWalletChangeReason) {
        Criteria criteria = Criteria.where("orderId").in(orderIds).and("reason").is(shopWalletChangeReason);
        return countByMongoDb(criteria);
    }

    @Override
    public long countOutAmountByRefundId(Long refundId, Integer shopWalletChangeReason) {
        Criteria criteria = Criteria.where("refundId").is(refundId).and("reason").is(shopWalletChangeReason);
        return countByMongoDb(criteria);
    }

    @Override
    public void updateRechargeSuccess(Long shopWalletLogId, Long orderId) {
        MongoShopWalletLogBO shopWalletLogDb = mongoTemplate.findById(shopWalletLogId, MongoShopWalletLogBO.class);
        if (Objects.isNull(shopWalletLogDb)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        // 幂等
        if (Objects.equals(shopWalletLogDb.getReason(), ShopWalletChangeReason.SUCCESS_RECHARGE.value())) {
            return;
        }

        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(shopWalletLogId).and("reason").is(ShopWalletChangeReason.APPLY_RECHARGE.value()));
        Update update = new Update();
        update.set("orderId", orderId);
        update.set("reason", ShopWalletChangeReason.SUCCESS_RECHARGE.value());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, MongoShopWalletLogBO.class);
        if (updateResult.getModifiedCount() < 1) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public ShopWalletVO getShopWalletByTime(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        ProjectionOperation projectionOperation = Aggregation.project()
                // 待结算金额收入： 用户支付 拒绝用户退款
                .and(ConditionalOperators.when(Criteria.where("reason").in(0, 3)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inUnsettleAmount")
                // 待结算金额支出： 用户退款成功 系统扣除订单中需要颁发给用户的分销金额
                .and(ConditionalOperators.when(Criteria.where("reason").in(2, 9)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outUnsettleAmount")
                // 总结算金额：订单结算 余额充值成功 预售失败结算（超时未支付尾款）
                .and(ConditionalOperators.when(Criteria.where("reason").in(1, 11, 14)).then(ConditionalOperators.when(Criteria.where("amountType").is(1)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("totalSettleAmount")
                // 冻结金额收入：提现申请
                .and(ConditionalOperators.when(Criteria.where("reason").in(4)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inFreezeAmount")
                // 冻结金额支出：提现申请被拒绝 提现申请通过
                .and(ConditionalOperators.when(Criteria.where("reason").in(5, 6)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outFreezeAmount");
        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .sum("inUnsettleAmount").as("inUnsettleAmount")
                .sum("outUnsettleAmount").as("outUnsettleAmount")
                .sum("totalSettleAmount").as("totalSettleAmount")
                .sum("inFreezeAmount").as("inFreezeAmount")
                .sum("outFreezeAmount").as("outFreezeAmount");

        TypedAggregation<MongoShopWalletVO> typedAggregation = Aggregation.newAggregation(
                MongoShopWalletVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation
        );

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

        // 执行聚合操作
        AggregationResults<MongoShopWalletVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoShopWalletVO.class);
        // 取出最终结果
        List<MongoShopWalletVO> list = aggregationResults.getMappedResults();
        ShopWalletVO shopWalletVO = new ShopWalletVO();
        if (CollUtil.isEmpty(list)) {
            return shopWalletVO;
        }
        MongoShopWalletVO mongoShopWalletVO = list.get(0);
        shopWalletVO.setUnsettledAmount(mongoShopWalletVO.getInUnsettleAmount() - mongoShopWalletVO.getOutUnsettleAmount());
        shopWalletVO.setTotalSettledAmount(mongoShopWalletVO.getTotalSettleAmount());
        shopWalletVO.setFreezeAmount(mongoShopWalletVO.getInFreezeAmount() - mongoShopWalletVO.getOutFreezeAmount());
        return shopWalletVO;
    }

    @Override
    public PageVO<ShopWalletVO> pageShopWalletByTime(PageDTO pageDTO, ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        ProjectionOperation projectionOperation = Aggregation.project("shopId")
                // 待结算金额收入： 用户支付 拒绝用户退款
                .and(ConditionalOperators.when(Criteria.where("reason").in(0, 3)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inUnsettleAmount")
                // 待结算金额支出： 用户退款成功 系统扣除订单中需要颁发给用户的分销金额
                .and(ConditionalOperators.when(Criteria.where("reason").in(2, 9)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outUnsettleAmount")
                // 总结算金额：订单结算 余额充值成功 预售失败结算（超时未支付尾款）
                .and(ConditionalOperators.when(Criteria.where("reason").in(1, 11, 14)).then(ConditionalOperators.when(Criteria.where("amountType").is(1)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("totalSettleAmount")
                // 冻结金额收入：提现申请
                .and(ConditionalOperators.when(Criteria.where("reason").in(4)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inFreezeAmount")
                // 冻结金额支出：提现申请被拒绝 提现申请通过
                .and(ConditionalOperators.when(Criteria.where("reason").in(5, 6)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outFreezeAmount");
        // 分组
        GroupOperation groupOperation = Aggregation.group("shopId")
                .first("shopId").as("shopId")
                .sum("inUnsettleAmount").as("inUnsettleAmount")
                .sum("outUnsettleAmount").as("outUnsettleAmount")
                .sum("totalSettleAmount").as("totalSettleAmount")
                .sum("inFreezeAmount").as("inFreezeAmount")
                .sum("outFreezeAmount").as("outFreezeAmount");

        SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "shopId");
        // 分页数据
        SkipOperation skipOperation = Aggregation.skip((long) pageDTO.getPageSize() * (pageDTO.getPageNum() - 1));
        LimitOperation limitOperation = Aggregation.limit(pageDTO.getPageSize());
        TypedAggregation<MongoShopWalletVO> typedAggregation = Aggregation.newAggregation(
                MongoShopWalletVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );

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

        // 执行聚合操作
        AggregationResults<MongoShopWalletVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoShopWalletVO.class);
        // 取出最终结果
        List<MongoShopWalletVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new PageVO<>();
        }
        // 总条数
        TypedAggregation<MongoShopWalletVO> aggregation = Aggregation.newAggregation(
                MongoShopWalletVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation
        );

        // 执行聚合操作
        AggregationResults<MongoShopWalletVO> aggregate = mongoTemplate.aggregate(aggregation, MongoShopWalletVO.class);
        // 取出最终结果
        List<MongoShopWalletVO> mappedResults = aggregate.getMappedResults();
        // 组装数据
        List<ShopWalletVO> result = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Map<Long, String> shopMap = getShopMap(list.stream().map(MongoShopWalletVO::getShopId));
        for (MongoShopWalletVO mongoShopWalletVO : list) {
            ShopWalletVO shopWalletVO = new ShopWalletVO();
            String shopName = shopMap.get(mongoShopWalletVO.getShopId());
            shopWalletVO.setShopId(mongoShopWalletVO.getShopId());
            shopWalletVO.setShopName(shopName);
            shopWalletVO.setUnsettledAmount(mongoShopWalletVO.getInUnsettleAmount() - mongoShopWalletVO.getOutUnsettleAmount());
            shopWalletVO.setTotalSettledAmount(mongoShopWalletVO.getTotalSettleAmount());
            shopWalletVO.setFreezeAmount(mongoShopWalletVO.getInFreezeAmount() - mongoShopWalletVO.getOutFreezeAmount());
            result.add(shopWalletVO);
        }
        PageVO<ShopWalletVO> page = new PageVO<>();
        page.setTotal((long) mappedResults.size());
        page.setList(result);
        page.setPages(PageUtil.getPages(mappedResults.size(), pageDTO.getPageSize()));
        return page;
    }

    @Override
    public ShopWalletVO getPlatformWallet() {
        // 统计reason在0,1,2的所有平台待结算金额,再统计结算金额，减掉就是真正的待结算金额
        Integer paySysType = shopAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        Criteria criteria = Criteria.where("reason")
                .in(ShopWalletChangeReason.PAY.value(), ShopWalletChangeReason.ORDER_REFUND.value())
                .and("paySysType").is(paySysType)
                .and("amountType").is(ShopWalletAmountType.UNSETTLED_AMOUNT.value());
        Aggregation aggregation = getAggregation(criteria);
        AggregationResults<MongoShopWalletLogVO> results = mongoTemplate.aggregate(aggregation, MongoShopWalletLogBO.class, MongoShopWalletLogVO.class);
        List<MongoShopWalletLogVO> unSettleList = results.getMappedResults();
        if (CollUtil.isEmpty(unSettleList)) {
            return new ShopWalletVO();
        }
        Criteria settleCriteria = Criteria.where("reason").is(ShopWalletChangeReason.SETTLED.value())
                .and("paySysType").is(paySysType)
                .and("amountType").is(ShopWalletAmountType.SETTLED_AMOUNT.value());
        Aggregation settledAggregation = getAggregation(settleCriteria);
        AggregationResults<MongoShopWalletLogVO> settledResults = mongoTemplate.aggregate(settledAggregation, MongoShopWalletLogBO.class, MongoShopWalletLogVO.class);
        List<MongoShopWalletLogVO> settleList = settledResults.getMappedResults();
        Long outUnAmount = 0L;
        Long inUnAmount = 0L;
        for (MongoShopWalletLogVO mongoShopWalletLogVO : unSettleList) {
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.INCOME.value())) {
                // 待结算收入金额
                inUnAmount = mongoShopWalletLogVO.getTotalAmount();
            }
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.EXPENDITURE.value())) {
                // 待结算支出金额
                outUnAmount = mongoShopWalletLogVO.getTotalAmount();
            }
        }
        Long outAmount = 0L;
        Long inAmount = 0L;
        for (MongoShopWalletLogVO mongoShopWalletLogVO : settleList) {
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.EXPENDITURE.value())) {
                // 结算支出金额
                outAmount = mongoShopWalletLogVO.getTotalAmount();
            }
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.INCOME.value())) {
                // 结算收入金额
                inAmount = mongoShopWalletLogVO.getTotalAmount();
            }
        }
        long settledAmount = inAmount - outAmount;
        long unsettleAmount = inUnAmount - outUnAmount - settledAmount;
        ShopWalletVO shopWalletVO = new ShopWalletVO();
        shopWalletVO.setUnsettledAmount(unsettleAmount);
        shopWalletVO.setSettledAmount(settledAmount);
        return shopWalletVO;
    }

    private static Aggregation getAggregation(Criteria criteria) {
        return Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("platformIoType").sum("platformChangeAmount").as("totalAmount")
                        .last("platformIoType").as("platformIoType")
        );
    }

    @Override
    public List<MongoShopWalletLogBO> listByOrderIdsAndChangeReason(List<Long> orderIds, Integer shopWalletChangeReason) {
        Criteria criteria = Criteria.where("orderId").in(orderIds).and("reason").is(shopWalletChangeReason);
        Query query = new Query(criteria);
        return mongoTemplate.find(query, MongoShopWalletLogBO.class);
    }



    /**
     * 获取MongoDB的搜索条件
     */
    private Criteria getCriteria(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = new Criteria();
        Integer paySysType = shopAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        Integer sysType = Objects.nonNull(AuthUserContext.get()) ? AuthUserContext.get().getSysType() : null;
        criteria.and("paySysType").is(paySysType);
        if (Objects.nonNull(shopWalletLogSearchDTO.getRefundId())) {
            criteria.and("refundId").is(shopWalletLogSearchDTO.getRefundId());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getOrderId())) {
            criteria.and("orderId").is(shopWalletLogSearchDTO.getOrderId());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getAmountType())) {
            criteria.and("amountType").is(shopWalletLogSearchDTO.getAmountType());
        }
        if(Objects.nonNull(shopWalletLogSearchDTO.getReason())){
            criteria.and("reason").is(shopWalletLogSearchDTO.getReason());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getShopId())) {
            criteria.and("shopId").is(shopWalletLogSearchDTO.getShopId());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getShopIoType())) {
            criteria.and("shopIoType").is(shopWalletLogSearchDTO.getShopIoType());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getPlatformIoType())) {
            criteria.and("platformIoType").is(shopWalletLogSearchDTO.getPlatformIoType());
        }
        if (StrUtil.isNotBlank(shopWalletLogSearchDTO.getShopName())) {
            ShopSimpleBO shopSimpleBO = new ShopSimpleBO();
            shopSimpleBO.setShopName(shopWalletLogSearchDTO.getShopName());
            List<ShopSimpleBO> shopSimpleList = shopDetailService.listSimple(shopSimpleBO);
            if (CollUtil.isNotEmpty(shopSimpleList)) {
                List<Long> shopIds = shopSimpleList.stream().map(ShopSimpleBO::getShopId).collect(Collectors.toList());
                criteria.and("shopId").in(shopIds);
            }
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getStartTime())) {
            criteria.and("createTime").gte(Objects.requireNonNull(DateUtils.dateToIsoDate(shopWalletLogSearchDTO.getStartTime()))).lte(Objects.requireNonNull(DateUtils.dateToIsoDate(shopWalletLogSearchDTO.getEndTime())));
        }
        return criteria;
    }

    private long countByMongoDb(Criteria criteria) {
        return mongoTemplate.count(new Query(criteria), MongoShopWalletLogBO.class);
    }
}

