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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.constant.EsRenovationProductSortEnum;
import com.tmerclub.cloud.api.feign.SearchSpuFeignClient;
import com.tmerclub.cloud.api.order.feign.OrderCommFeignClient;
import com.tmerclub.cloud.api.product.bo.SpuSimpleBO;
import com.tmerclub.cloud.api.user.feign.UserFeignClient;
import com.tmerclub.cloud.api.user.vo.UserApiVO;
import com.tmerclub.cloud.api.vo.EsPageVO;
import com.tmerclub.cloud.common.cache.util.RedisUtil;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.constant.StatusEnum;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.i18n.I18nMessage;
import com.tmerclub.cloud.common.i18n.LanguageEnum;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.mongodb.util.MongoPageUtil;
import com.tmerclub.cloud.common.order.vo.OrderItemLangVO;
import com.tmerclub.cloud.common.order.vo.OrderItemVO;
import com.tmerclub.cloud.common.product.dto.ProductSearchDTO;
import com.tmerclub.cloud.common.product.vo.SpuVO;
import com.tmerclub.cloud.common.product.vo.search.SpuAdminVO;
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.BeanUtil;
import com.tmerclub.cloud.common.util.IpHelper;
import com.tmerclub.cloud.product.bo.mongo.MongoSpuCommBO;
import com.tmerclub.cloud.product.constant.DistributedIdKey;
import com.tmerclub.cloud.product.dto.SpuCommDTO;
import com.tmerclub.cloud.product.model.SpuComm;
import com.tmerclub.cloud.product.service.SpuCommService;
import com.tmerclub.cloud.product.service.SpuExtensionService;
import com.tmerclub.cloud.product.service.SpuService;
import com.tmerclub.cloud.product.vo.SpuCommStatisticsStarVO;
import com.tmerclub.cloud.product.vo.SpuCommStatisticsVO;
import com.tmerclub.cloud.product.vo.SpuCommVO;
import com.tmerclub.cloud.product.vo.mongo.MongoSpuCommStatisticsVO;
import io.seata.spring.annotation.GlobalTransactional;
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.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.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

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

/**
 * 商品评论
 *
 * @author YXF
 * @date 2021-01-11 13:47:33
 */
@Service
public class SpuCommServiceImpl implements SpuCommService {

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


    @Autowired
    private SpuService spuService;


    @Autowired
    private SpuExtensionService spuExtensionService;

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private MongoPageUtil mongoPageUtil;
    @Autowired
    private RedissonClient redissonClient;
    @DubboReference
    private SearchSpuFeignClient searchSpuFeignClient;
    @DubboReference
    private UserFeignClient userFeignClient;
    @DubboReference
    private OrderCommFeignClient orderCommFeignClient;

    @Override
    public PageVO<SpuCommVO> page(PageDTO pageDTO, SpuCommDTO spuCommDTO) {
        PageVO<SpuCommVO> result = shopPage(pageDTO, spuCommDTO);
        if (CollUtil.isEmpty(result.getList())) {
            return result;
        }
        List<Long> spuIds = result.getList().stream().map(SpuCommVO::getSpuId).collect(Collectors.toList());
        List<Long> userIds = result.getList().stream().map(SpuCommVO::getUserId).collect(Collectors.toList());
        List<SpuVO> spus = spuService.listSpuBySpuIds(spuIds);
        Map<Long, String> spuNameMap = spus.stream().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getName));
        Map<Long, String> spuImgMap = spus.stream().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getMainImgUrl));
        ServerResponseEntity<List<UserApiVO>> userResponse = userFeignClient.getUserByUserIds(userIds);
        Map<Long, String> nickNameMap = userResponse.getData().stream().collect(Collectors.toMap(UserApiVO::getUserId, UserApiVO::getNickName));
        for (SpuCommVO spuCommVO : result.getList()) {
            spuCommVO.setSpuName(spuNameMap.get(spuCommVO.getSpuId()));
            spuCommVO.setNickName(Objects.equals(spuCommVO.getIsAnonymous(), 1) ? null : nickNameMap.get(spuCommVO.getUserId()));
            spuCommVO.setSpuImg(spuImgMap.get(spuCommVO.getSpuId()));
        }
        return result;
    }

    @Override
    public SpuCommVO getBySpuCommId(Long spuCommId) {
        Criteria criteria = Criteria.where("_id").is(spuCommId);

        return getOne(criteria);
    }

    @Override
    public void save(MongoSpuCommBO mongoSpuCommBO) {
        // 设置id
        // 获取订单id列表
        if (StrUtil.isBlank(mongoSpuCommBO.getPics())) {
            mongoSpuCommBO.setPics(null);
        }
        mongoSpuCommBO.setSpuCommId(segmentManager.getSegmentId(DistributedIdKey.tmerclub_PRODUCT_COMM));
        mongoTemplate.save(mongoSpuCommBO);
    }

    @Override
    public void update(SpuCommDTO spuComm) {
        spuComm.setStatus(1);
        spuComm.setReplyTime(new Date());
        MongoSpuCommBO mongoSpuCommBO = BeanUtil.map(spuComm, MongoSpuCommBO.class);
        mongoTemplate.save(mongoSpuCommBO);
    }

    @Override
    public void deleteById(Long spuCommId) {
        Criteria criteria = Criteria.where("_id").is(spuCommId);
        mongoTemplate.remove(new Query(criteria), MongoSpuCommBO.class);
    }

    @Override
    public PageVO<SpuCommVO> spuCommPage(PageDTO pageDTO, Long spuId, Integer evaluate) {
        PageVO<SpuCommVO> spuCommPage = spuPage(pageDTO, spuId, evaluate);
        if (CollectionUtil.isEmpty(spuCommPage.getList())) {
            return spuCommPage;
        }
        //
        Set<Long> userIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        Set<Long> orderItemIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        spuCommPage.getList().forEach(spuComm -> {
            orderItemIds.add(spuComm.getOrderItemId());
            // 非匿名评价
            if (!Objects.equals(spuComm.getIsAnonymous(), SpuComm.ANONYMOUS)) {
                userIds.add(spuComm.getUserId());
            }
        });
        ServerResponseEntity<List<OrderItemLangVO>> orderItemRes = orderCommFeignClient.listOrderItemLangByIds(new ArrayList<>(orderItemIds));
        ServerResponseEntity<List<UserApiVO>> userResponse = userFeignClient.getUserByUserIds(new ArrayList<>(userIds));
        Map<Long, UserApiVO> userMap = userResponse.getData().stream().collect(Collectors.toMap(UserApiVO::getUserId, u -> u));
        Map<Long, List<OrderItemLangVO>> orderItemLangMap;
        Integer lang = I18nMessage.getLang();
        if (CollUtil.isNotEmpty(orderItemRes.getData())) {
            orderItemLangMap = orderItemRes.getData().stream().collect(Collectors.groupingBy(OrderItemLangVO::getOrderItemId));
        } else {
            orderItemLangMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        }
        spuCommPage.getList().forEach(spuComm -> {
            // 获取sku名称
            if(orderItemLangMap.containsKey(spuComm.getOrderItemId())) {
                List<OrderItemLangVO> orderItemLangList = orderItemLangMap.get(spuComm.getOrderItemId());
                Map<Integer, OrderItemLangVO> itemLangMap = orderItemLangList.stream().collect(Collectors.toMap(OrderItemLangVO::getLang, o -> o));
                // 如果不存在当前语言，直接默认用中文
                if (itemLangMap.containsKey(lang)) {
                    spuComm.setSkuName(itemLangMap.get(lang).getSkuName());
                } else if (itemLangMap.containsKey(LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
                    spuComm.setSkuName(itemLangMap.get(LanguageEnum.LANGUAGE_ZH_CN.getLang()).getSkuName());
                }
            }
            if (!Objects.equals(spuComm.getIsAnonymous(), SpuComm.ANONYMOUS)) {
                UserApiVO userVO = userMap.get(spuComm.getUserId());
                if (Objects.nonNull(userVO)) {
                    spuComm.setNickName(userVO.getNickName());
                    spuComm.setUserPic(userVO.getPic());
                } else {
                    // 没有用户信息说明用户已注销
                    spuComm.setIsWriteOff(true);
                }
            }
        });
        return spuCommPage;
    }

    @Override
    public SpuCommStatisticsVO getProdCommDataBySpuId(Long spuId) {
        MatchOperation matchOperation = Aggregation.match(Criteria.where("spuId").is(spuId).and("status").is(1));

        ProjectionOperation projectionOperation = Aggregation.project()
                .and(ConditionalOperators.when(Criteria.where("evaluate").in(1)).then(1).otherwise(0)).as("praiseNumber")
                .and(ConditionalOperators.when(Criteria.where("evaluate").in(2)).then(1).otherwise(0)).as("secondaryNumber")
                .and(ConditionalOperators.when(Criteria.where("evaluate").in(3)).then(1).otherwise(0)).as("negativeNumber")
                .and(ConditionalOperators.ifNull("pics").then(1)).as("picNumber");


        GroupOperation groupOperation = Aggregation.group()
                .count().as("number")
                .sum("praiseNumber").as("praiseNumber")
                .sum("secondaryNumber").as("secondaryNumber")
                .sum("negativeNumber").as("negativeNumber")
                .sum(ConditionalOperators.when(Criteria.where("picNumber").ne(1)).then(1).otherwise(0)).as("picNumber");

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

        // 执行聚合操作
        AggregationResults<MongoSpuCommStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuCommStatisticsVO.class);
        // 取出最终结果
        List<MongoSpuCommStatisticsVO> list = aggregationResults.getMappedResults();
        SpuCommStatisticsVO spuCommStatistics;
        if (CollUtil.isEmpty(list)) {
            spuCommStatistics = new SpuCommStatisticsVO();
        } else {
            spuCommStatistics = BeanUtil.map(list.get(0), SpuCommStatisticsVO.class);
        }
        if (Objects.isNull(spuCommStatistics.getNumber())) {
            spuCommStatistics.setNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getPraiseNumber())) {
            spuCommStatistics.setPraiseNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getSecondaryNumber())) {
            spuCommStatistics.setSecondaryNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getNegativeNumber())) {
            spuCommStatistics.setNegativeNumber(0);
        }
        if (Objects.isNull(spuCommStatistics.getPositiveRating())) {
            spuCommStatistics.setPositiveRating(0.0D);
        }
        if (Objects.isNull(spuCommStatistics.getPicNumber())) {
            spuCommStatistics.setPicNumber(0);
        }

        //计算出好评率
        if (spuCommStatistics.getPraiseNumber() == 0 || spuCommStatistics.getNumber() == 0) {
            spuCommStatistics.setPositiveRating(0.0);
            return spuCommStatistics;
        }
        spuCommStatistics.setPositiveRating(Arith.div(Arith.mul(spuCommStatistics.getPraiseNumber(), 100), spuCommStatistics.getNumber(), 2));
        return spuCommStatistics;
    }

    @Override
    public SpuCommStatisticsStarVO getProdCommDataByStar(Long spuId) {

        MatchOperation matchOperation = Aggregation.match(Criteria.where("spuId").is(spuId).and("status").is(1));

        ProjectionOperation projectionOperation = Aggregation.project("actualTotal", "platformAmount", "refundStatus")
                .and(ConditionalOperators.when(Criteria.where("score").in(5)).then(1).otherwise(0)).as("fiveStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(4)).then(1).otherwise(0)).as("fourStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(3)).then(1).otherwise(0)).as("threeStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(2)).then(1).otherwise(0)).as("twoStarsNumber")
                .and(ConditionalOperators.when(Criteria.where("score").in(1)).then(1).otherwise(0)).as("oneStarsNumber");

        GroupOperation groupOperation = Aggregation.group()
                .sum("fiveStarsNumber").as("fiveStarsNumber")
                .sum("fourStarsNumber").as("fourStarsNumber")
                .sum("threeStarsNumber").as("threeStarsNumber")
                .sum("twoStarsNumber").as("twoStarsNumber")
                .sum("oneStarsNumber").as("oneStarsNumber");

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

        // 执行聚合操作
        AggregationResults<MongoSpuCommStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuCommStatisticsVO.class);
        // 取出最终结果
        List<MongoSpuCommStatisticsVO> list = aggregationResults.getMappedResults();
        SpuCommStatisticsStarVO spuCommStatisticsStarVO;
        if (CollUtil.isEmpty(list)) {
            spuCommStatisticsStarVO = new SpuCommStatisticsStarVO();
        } else {
            spuCommStatisticsStarVO = BeanUtil.map(list.get(0), SpuCommStatisticsStarVO.class);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getFiveStarsNumber())) {
            spuCommStatisticsStarVO.setFiveStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getTwoStarsNumber())) {
            spuCommStatisticsStarVO.setTwoStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getThreeStarsNumber())) {
            spuCommStatisticsStarVO.setThreeStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getFourStarsNumber())) {
            spuCommStatisticsStarVO.setFourStarsNumber(0);
        }
        if (Objects.isNull(spuCommStatisticsStarVO.getOneStarsNumber())) {
            spuCommStatisticsStarVO.setOneStarsNumber(0);
        }
        return spuCommStatisticsStarVO;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Long> getCommNumRankShopIdAndTime(String key, Long shopId, Integer dayNum, 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:comm_num_rank:" + key);
        try {
            int lockWait = 30;
            if (rLock.tryLock(lockWait, lockWait, TimeUnit.SECONDS)) {
                spuIds = RedisUtil.getListRange(key, 0L, 10L);
                if (CollectionUtil.isEmpty(spuIds)) {
                    List<MongoSpuCommStatisticsVO> list = listMongoSpuCommStatistics(shopId, dayNum, esRenovationSpuSort);
                    spuIds = list.stream().map(MongoSpuCommStatisticsVO::getSpuId).collect(Collectors.toList());
                    if (CollUtil.isEmpty(spuIds) || Objects.isNull(primaryCategoryId)) {
                        return spuIds;
                    }
                    handleCategoryId(primaryCategoryId, spuIds);
                }
            } else {
                throw new LuckException("网络繁忙，请稍后再试");
            }
        } catch (InterruptedException e) {
            logger.error("InterruptedException:", e);
        } finally {
            try {
                if (rLock.isLocked()) {
                    rLock.unlock();
                }
            } catch (Exception e) {
                logger.error("Exception:", e);
            }
        }
        return spuIds;
    }

    private void handleCategoryId(Long primaryCategoryId, List<Long> spuIds) {
        ProductSearchDTO productSearchDTO = new ProductSearchDTO();
        productSearchDTO.setPageSize(PageDTO.MAX_PAGE_SIZE);
        productSearchDTO.setPageNum(PageDTO.DEFAULT_PAGE_NUM);
        productSearchDTO.setSpuIds(spuIds);
        ServerResponseEntity<EsPageVO<SpuAdminVO>> searchResponse = searchSpuFeignClient.adminPage(productSearchDTO);
        if (!searchResponse.isSuccess()) {
            throw new LuckException(searchResponse.getMsg());
        }
        List<SpuAdminVO> spuList = searchResponse.getData().getList();
        if (CollUtil.isNotEmpty(spuList)) {
            Map<Long, Long> categoryMap = spuList.stream().collect(Collectors.toMap(SpuAdminVO::getSpuId, SpuAdminVO::getPrimaryCategoryId));
            Iterator<Long> iterator = spuIds.iterator();
            while (iterator.hasNext()) {
                Long spuId = iterator.next();
                Long categoryId = categoryMap.get(spuId);
                if (!Objects.equals(categoryId, primaryCategoryId)) {
                    iterator.remove();
                }
            }
        }
    }

    private List<MongoSpuCommStatisticsVO> listMongoSpuCommStatistics(Long shopId, Integer dayNum, Integer esRenovationSpuSort) {
        Date time = null;
        if (dayNum != 0) {
            time = DateUtil.offsetDay(new DateTime(), -dayNum);
        }
        // 过滤
        Criteria criteria = null;
        if (Objects.nonNull(time)) {
            if (Objects.isNull(shopId) || Objects.equals(shopId, 0L)) {
                criteria = Criteria.where("createTime").gt(time);
            } else {
                criteria = Criteria.where("createTime").gt(time).and("shopId").is(shopId);
            }
        } else {
            if (Objects.nonNull(shopId) && !Objects.equals(shopId, 0L)) {
                criteria = Criteria.where("shopId").is(shopId);
            }
        }
        // 分组
        GroupOperation spuIdGroupOperation = Aggregation.group("spuId").count().as("number");
        ProjectionOperation projectionOperation = Aggregation.project("number")
                .and("_id").as("spuId");
        Sort.Direction sort;
        if (Objects.equals(esRenovationSpuSort, EsRenovationProductSortEnum.COMMENT_NUM_DESC.value())) {
            sort = Sort.Direction.DESC;
        } else {
            sort = Sort.Direction.ASC;
        }
        SortOperation sortOperation = Aggregation.sort(sort, "number");
        TypedAggregation<MongoSpuCommStatisticsVO> typedAggregation;
        if (Objects.nonNull(criteria)) {
            // 按顺序组合每一个聚合步骤
            typedAggregation =
                    Aggregation.newAggregation(
                            MongoSpuCommStatisticsVO.class,
                            Aggregation.match(criteria),
                            spuIdGroupOperation,
                            projectionOperation,
                            sortOperation,
                            Aggregation.limit(1000)
                    );
        } else {
            // 按顺序组合每一个聚合步骤
            typedAggregation =
                    Aggregation.newAggregation(
                            MongoSpuCommStatisticsVO.class,
                            spuIdGroupOperation,
                            sortOperation,
                            projectionOperation,
                            Aggregation.limit(1000)
                    );
        }
        logger.info("getTradeRetained()的mongodb查询语句： {}", typedAggregation);

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

    @Override
    public PageVO<SpuCommVO> spuCommPageByUserId(PageDTO pageDTO, Long userId) {
        PageVO<SpuCommVO> page = userPage(pageDTO, userId);
        ServerResponseEntity<UserApiVO> userResponse = userFeignClient.getUserData(userId);
        UserApiVO userApiVO = userResponse.getData();
        for (SpuCommVO spuCommVO : page.getList()) {
            spuCommVO.setNickName(userApiVO.getNickName());
            spuCommVO.setPics(userApiVO.getPic());
        }
        return page;
    }

    @Override
    public List<SpuCommVO> getSpuCommByOrderItemId(List<Long> orderItemIds, Long userId) {
        Criteria criteria = Criteria.where("userId").is(userId).and("orderItemId").in(orderItemIds);
        List<SpuCommVO> spuCommVOList = getList(criteria);
        logger.info("语言lang{}:" + I18nMessage.getLang().toString());
        ServerResponseEntity<List<OrderItemVO>> orderItemVOList = orderCommFeignClient.getListByOrderItemId(orderItemIds, I18nMessage.getLang());
        if (!orderItemVOList.isSuccess()) {
            throw new LuckException("获取订单信息出错");
        }
        Map<Long, OrderItemVO> itemMap = orderItemVOList.getData().stream().collect(Collectors.toMap(OrderItemVO::getOrderItemId, s -> s));
        List<Long> spuIds = spuCommVOList.stream().map(SpuCommVO::getSpuId).collect(Collectors.toList());
        List<SpuVO> spuVOList = spuService.listSpuDetailBySpuIds(spuIds);
        Map<Long, String> spuMap = spuVOList.stream().distinct().collect(Collectors.toMap(SpuVO::getSpuId, SpuVO::getMainImgUrl));
        for (SpuCommVO spuCommVO : spuCommVOList) {
            spuCommVO.setSpuImg(spuMap.get(spuCommVO.getSpuId()));
            spuCommVO.setTransactionTime(itemMap.get(spuCommVO.getOrderItemId()).getCreateTime());
            if (!itemMap.containsKey(spuCommVO.getOrderItemId())){
                continue;
            }
            spuCommVO.setSkuName(itemMap.get(spuCommVO.getOrderItemId()).getSkuName());
        }
        return spuCommVOList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @GlobalTransactional(rollbackFor = Exception.class)
    public void comm(SpuCommDTO spuCommDTO) {
        OrderItemVO orderItemVO = this.checkAndSetComm(spuCommDTO);
        orderCommFeignClient.updateOrderItemComm(orderItemVO);
    }

    @Override
    public void comms(List<SpuCommDTO> spuCommDTOList) {
        if (CollectionUtils.isEmpty(spuCommDTOList)) {
            return;
        }
        List<OrderItemVO> orderItemVOList = spuCommDTOList.stream()
                .map(this::checkAndSetComm)
                .collect(Collectors.toList());
        orderCommFeignClient.updateOrderItemComms(orderItemVOList);
    }

    /**
     * 校验并设置评论
     * @param spuCommDTO
     * @return
     */
    private OrderItemVO checkAndSetComm(SpuCommDTO spuCommDTO) {
        ServerResponseEntity<OrderItemVO> orderItemResponse = orderCommFeignClient.getByOrderItemId(spuCommDTO.getOrderItemId());
        if (!orderItemResponse.isSuccess()) {
            throw new LuckException(orderItemResponse.getMsg());
        }
        OrderItemVO orderItemVO = orderItemResponse.getData();
        int refundStatus = 4;
        if (Objects.nonNull(orderItemVO.getRefundStatus()) && !Objects.equals(refundStatus, orderItemVO.getRefundStatus())) {
            throw new LuckException("[" + orderItemVO.getSpuName() + "]" + "该商品正在进行售后处理，不能进行评价!");
        }
        Long spuId = getSpuIdByOrderItemId(spuCommDTO.getOrderItemId());
        // 保存商品评论
        MongoSpuCommBO spuComm = saveSpuComm(spuId, spuCommDTO);
        // 更新商品扩展信息表商品评价数量
        spuExtensionService.changeCommentNum(spuId, 1);

        // 更新订单项评论状态
        orderItemVO.setUpdateTime(spuComm.getCreateTime());
        orderItemVO.setCommTime(spuComm.getCreateTime());
        orderItemVO.setIsComm(Constant.IS_COMM);
        orderItemVO.setUpdateTime(new Date());
        return orderItemVO;
    }

    @Override
    public Integer countGoodReview(Long spuId) {
        // 过滤
        Criteria criteria = Criteria.where("spuId").is(spuId).and("evaluate").is(1).and("status").is(1);
        MatchOperation matchOperation = Aggregation.match(criteria);
        // 分组计算数量
        GroupOperation groupOperation = Aggregation.group()
                .count().as("number");

        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoSpuCommStatisticsVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoSpuCommStatisticsVO.class,
                        matchOperation,
                        groupOperation
                );
        logger.info("构建countGoodReview()的mongodb查询语句： {}", typedAggregation);
        // 执行聚合操作
        AggregationResults<MongoSpuCommStatisticsVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoSpuCommStatisticsVO.class);
        // 取出最终结果
        List<MongoSpuCommStatisticsVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return 0;
        }
        Integer number = list.get(0).getNumber();
        if (Objects.isNull(number)) {
            number = 0;
        }
        return number;
    }

    /**
     * 获取评论的spuId，同时校验评论的
     *
     * @param orderItemId
     * @return
     */
    private Long getSpuIdByOrderItemId(Long orderItemId) {
        ServerResponseEntity<Long> orderResponse = orderCommFeignClient.getSpuIdByOrderItemId(orderItemId);
        if (!orderResponse.isSuccess()) {
            throw new LuckException(orderResponse.getMsg());
        }
        long spuId;
        try {
            spuId = Long.parseLong(orderResponse.getData().toString());
        } catch (Exception e) {
            throw new LuckException("评论的商品数据有误，请刷新后重试");
        }
        return spuId;
    }

    /**
     * 保存商品评论信息
     *
     * @param spuId
     * @param spuCommDTO
     */
    private MongoSpuCommBO saveSpuComm(Long spuId, SpuCommDTO spuCommDTO) {
        if (StrUtil.length(spuCommDTO.getContent()) > Constant.MAX_FIELD_LIMIT) {
            // 截取字符串
            spuCommDTO.setContent(StrUtil.subWithLength(spuCommDTO.getContent(), 0, 500));
        }
        if (StrUtil.isBlank(spuCommDTO.getPics())) {
            spuCommDTO.setPics(null);
        }
        //查询是否已经评论
        ServerResponseEntity<OrderItemVO> byOrderItemId = orderCommFeignClient.getByOrderItemId(spuCommDTO.getOrderItemId());
        if (Objects.nonNull(byOrderItemId.getData())) {
            OrderItemVO data = byOrderItemId.getData();
            Criteria criteria = Criteria.where("userId").is(data.getUserId()).and("orderItemId").is(spuCommDTO.getOrderItemId()).and("status").is(1);
            SpuCommVO spuCommVO = getOne(criteria);
            if (Objects.nonNull(spuCommVO)) {
                throw new LuckException("该商品已评论");
            }
        }

        SpuVO spuVO = spuService.getBySpuId(spuId);
        // 插入评论
        MongoSpuCommBO spuComm = new MongoSpuCommBO();
        spuComm.setShopId(spuVO.getShopId());
        spuComm.setSpuId(spuId);
        spuComm.setOrderItemId(spuCommDTO.getOrderItemId());
        spuComm.setUserId(AuthUserContext.get().getUserId());
        spuComm.setScore(spuCommDTO.getScore());
        spuComm.setContent(spuCommDTO.getContent());
        spuComm.setPics(spuCommDTO.getPics());
        spuComm.setIsAnonymous(spuCommDTO.getIsAnonymous());
        spuComm.setCreateTime(new Date());
        // 平台不需要控评
        spuComm.setStatus(StatusEnum.ENABLE.value());
        spuComm.setPostip(IpHelper.getIpAddr());
        if (Objects.nonNull(spuComm.getScore()) && spuComm.getScore() > Constant.MEDIUM_RATING) {
            spuComm.setEvaluate(1);
        } else if (Objects.nonNull(spuComm.getScore()) && spuComm.getScore().equals(Constant.MEDIUM_RATING)) {
            spuComm.setEvaluate(2);
        } else if (Objects.nonNull(spuComm.getScore()) && spuComm.getScore() < Constant.MEDIUM_RATING) {
            spuComm.setEvaluate(3);
        }
        save(spuComm);
        return spuComm;
    }

    private PageVO<SpuCommVO> spuPage(PageDTO pageDTO, Long spuId, Integer evaluate) {
        Criteria criteria = Criteria.where("spuId").is(spuId).and("status").is(1);
        boolean writtenComm = Objects.nonNull(evaluate) && !Objects.equals(evaluate, 0) && !Objects.equals(evaluate, 4);
        // 文字评论
        if (writtenComm) {
            criteria.and("evaluate").is(evaluate);
        }
        // 图片评论
        else if (Objects.equals(evaluate, Constant.FOUR)) {
            criteria.and("pics").exists(true);
        }
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return page(pageDTO, query);
    }

    private PageVO<SpuCommVO> shopPage(PageDTO pageDTO, SpuCommDTO spuCommDTO) {

        Criteria criteria = Criteria.where("shopId").is(spuCommDTO.getShopId()).and("status").is(1);

        //根据商品名称搜索
        if (StrUtil.isNotBlank(spuCommDTO.getSpuName())) {
            SpuSimpleBO spuSimpleBO = new SpuSimpleBO();
            spuSimpleBO.setLang(I18nMessage.getLang());
            spuSimpleBO.setShopId(AuthUserContext.get().getTenantId());
            spuSimpleBO.setSpuName(spuCommDTO.getSpuName());
            List<SpuSimpleBO> spuList = spuService.listSimple(spuSimpleBO);
            List<Long> spuIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
            if (CollUtil.isNotEmpty(spuList)) {
                spuIds = spuList.stream().map(SpuSimpleBO::getSpuId).collect(Collectors.toList());
            }
            criteria.and("spuId").in(spuIds);
        }

        //根据用户昵称搜索
        if (StrUtil.isNotBlank(spuCommDTO.getNickName())) {
            UserApiVO userApiVO = new UserApiVO();
            userApiVO.setNickName(spuCommDTO.getNickName());
            ServerResponseEntity<List<UserApiVO>> userResponse = userFeignClient.getUserByParam(userApiVO);
            List<UserApiVO> users = userResponse.getData();
            List<Long> userIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
            if (CollUtil.isNotEmpty(users)) {
                userIds = users.stream().map(UserApiVO::getUserId).collect(Collectors.toList());
            }
            criteria.and("userId").in(userIds)
                    .and("isAnonymous").is(0);
        }


        if (Objects.nonNull(spuCommDTO.getCreateStartTime())) {
            criteria.and("createTime").gt(spuCommDTO.getCreateStartTime()).lt(spuCommDTO.getCreateEndTime());
        }
        if (Objects.nonNull(spuCommDTO.getReplyStartTime())) {
            criteria.and("replyTime").gt(spuCommDTO.getReplyStartTime()).lt(spuCommDTO.getReplyEndTime());
        }

        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return page(pageDTO, query);
    }

    private PageVO<SpuCommVO> userPage(PageDTO pageDTO, Long userId) {
        Criteria criteria = Criteria.where("userId").is(userId);
        return page(pageDTO, new Query(criteria));
    }

    private PageVO<SpuCommVO> page(PageDTO pageDTO, Query query) {
        PageVO<MongoSpuCommBO> mongoSpuCommPageVO = mongoPageUtil.doPage(MongoSpuCommBO.class, pageDTO, query);
        PageVO<SpuCommVO> pageVO = new PageVO<>();
        pageVO.setPages(mongoSpuCommPageVO.getPages());
        pageVO.setTotal(mongoSpuCommPageVO.getTotal());
        pageVO.setList(BeanUtil.mapAsList(mongoSpuCommPageVO.getList(), SpuCommVO.class));
        return pageVO;
    }

    private SpuCommVO getOne(Criteria criteria) {
        List<MongoSpuCommBO> list = mongoTemplate.find(new Query(criteria), MongoSpuCommBO.class);
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        return BeanUtil.map(list.get(0), SpuCommVO.class);
    }

    private List<SpuCommVO> getList(Criteria criteria) {
        List<MongoSpuCommBO> list = mongoTemplate.find(new Query(criteria), MongoSpuCommBO.class);
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        List<SpuCommVO> spuCommVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (MongoSpuCommBO mongoSpuCommBO : list) {
            SpuCommVO spuCommVO = BeanUtil.map(mongoSpuCommBO, SpuCommVO.class);
            spuCommVOList.add(spuCommVO);
        }
        return spuCommVOList;
    }
}
