package com.zbkj.front.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Maps;
import com.zbkj.common.constants.*;
import com.zbkj.common.enums.BizEnum;
import com.zbkj.common.exception.CrmebException;
import com.zbkj.common.model.bcx.*;
import com.zbkj.common.model.cat.Cart;
import com.zbkj.common.model.coupon.Coupon;
import com.zbkj.common.model.coupon.CouponProduct;
import com.zbkj.common.model.coupon.CouponUser;
import com.zbkj.common.model.express.ShippingTemplates;
import com.zbkj.common.model.express.ShippingTemplatesFree;
import com.zbkj.common.model.express.ShippingTemplatesRegion;
import com.zbkj.common.model.merchant.Merchant;
import com.zbkj.common.model.order.*;
import com.zbkj.common.model.product.Product;
import com.zbkj.common.model.product.ProductAttrValue;
import com.zbkj.common.model.product.ProductCategory;
import com.zbkj.common.model.product.ProductReply;
import com.zbkj.common.model.seckill.SeckillProduct;
import com.zbkj.common.model.user.User;
import com.zbkj.common.model.user.UserAddress;
import com.zbkj.common.model.user.UserIntegralRecord;
import com.zbkj.common.model.wechat.video.PayComponentProduct;
import com.zbkj.common.model.wechat.video.PayComponentProductSku;
import com.zbkj.common.page.CommonPage;
import com.zbkj.common.request.*;
import com.zbkj.common.request.bcx.OrderSaveUrlRequest;
import com.zbkj.common.request.bcx.OrderSaveUserRequest;
import com.zbkj.common.response.*;
import com.zbkj.common.token.TokenUtils;
import com.zbkj.common.utils.*;
import com.zbkj.common.vo.*;
import com.zbkj.front.service.FrontOrderService;
import com.zbkj.front.service.SeckillService;
import com.zbkj.service.factory.PaymentFactory;
import com.zbkj.service.service.*;
import com.zbkj.service.service.bcx.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.zbkj.common.constants.OrderConstants.ORDER_EXPIRED_MINUTES;
import static com.zbkj.common.constants.PayConstants.PAY_TYPE_OFFLINE;
import static com.zbkj.common.constants.ProductConstants.*;

/**
 * H5端订单操作
 * +----------------------------------------------------------------------
 * | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
 * +----------------------------------------------------------------------
 * | Author: CRMEB Team <admin@crmeb.com>
 * +----------------------------------------------------------------------
 */
@Service
public class FrontOrderServiceImpl implements FrontOrderService {

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

    @Autowired
    private UserService userService;
    @Autowired
    private CartService cartService;
    @Autowired
    private ProductService productService;
    @Autowired
    private BcxProductQuotaService bcxProductQuotaService;
    @Autowired
    private ProductAttrValueService productAttrValueService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private UserAddressService userAddressService;
    @Autowired
    private SystemConfigService systemConfigService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ShippingTemplatesService shippingTemplatesService;
    @Autowired
    private ShippingTemplatesFreeService shippingTemplatesFreeService;
    @Autowired
    private ShippingTemplatesRegionService shippingTemplatesRegionService;
    @Autowired
    private CouponUserService couponUserService;
    @Autowired
    private CouponProductService couponProductService;
    @Autowired
    private MerchantService merchantService;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private MerchantOrderService merchantOrderService;
    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private OrderStatusService orderStatusService;
    @Autowired
    private SystemAttachmentService systemAttachmentService;
    @Autowired
    private ProductReplyService productReplyService;
    @Autowired
    private RefundOrderService refundOrderService;
    @Autowired
    private RefundOrderInfoService refundOrderInfoService;
    @Autowired
    private LogisticService logisticService;
    @Autowired
    private PayService payService;
    @Autowired
    private PayComponentProductService payComponentProductService;
    @Autowired
    private PayComponentProductSkuService payComponentProductSkuService;
    @Autowired
    private UserIntegralRecordService userIntegralRecordService;
    @Autowired
    private SystemGroupDataService systemGroupDataService;
    @Autowired
    private SeckillService seckillService;
    @Autowired
    private VirtualOrderService virtualOrderService;
    @Autowired
    private SeckillProductService seckillProductService;
    @Autowired
    private CouponService couponService;
    @Autowired
    private ProductCategoryService productCategoryService;
    @Autowired
    private BcxPerformanceReportService bcxPerformanceReportService;
    @Resource
    private BcxPlatformFeeConfigService bcxPlatformFeeConfigService;
    @Resource
    RestTemplateUtil restTemplateUtil;
    @Resource
    BcxOrderRelationService bcxOrderRelationService;
    @Resource
    SmsService smsService;
    @Resource
    BcxPlatformFeeService bcxPlatformFeeService;
    @Resource
    BcxDepartmentMemberService bcxDepartmentMemberService;
    @Resource
    BcxOrderFapiaoService bcxOrderFapiaoService;
    @Resource
    private BcxPayOfflineService bcxPayOfflineService;
    @Autowired
    private PaymentFactory paymentFactory;
    @Autowired
    TaskResidualService taskResidualService;

    /**
     * 订单预下单
     *
     * @param request 预下单请求参数
     * @return PreOrderResponse
     */
    @Override
    public OrderNoResponse preOrder(PreOrderRequest request) {
        logger.info("preOrder:{}", JSON.toJSONString(request));
        User user = userService.getInfo();
        // 校验预下单商品信息
        PreOrderInfoVo preOrderInfoVo = validatePreOrderRequest(request, user);
        logger.info("preOrder 预下单 检查后:{} ", JSON.toJSONString(preOrderInfoVo));
        List<PreOrderInfoDetailVo> orderInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : preOrderInfoVo.getMerchantOrderVoList()) {
            orderInfoList.addAll(merchantOrderVo.getOrderInfoList());
            BigDecimal merTotalPrice = merchantOrderVo.getOrderInfoList().stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrderVo.setProTotalFee(merTotalPrice);
            merchantOrderVo.setProTotalNum(merchantOrderVo.getOrderInfoList().stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum());
        }
        // 商品总计金额
        BigDecimal totalPrice = orderInfoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        preOrderInfoVo.setProTotalFee(totalPrice);
        // 购买商品总数量
        int orderProNum = orderInfoList.stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum();
        preOrderInfoVo.setOrderProNum(orderProNum);
        // 获取默认地址
        UserAddress userAddress = userAddressService.getDefaultByUid(user.getId());
        if (ObjectUtil.isNotNull(userAddress)) {
            // 计算运费
            getFreightFee(preOrderInfoVo, userAddress);
            preOrderInfoVo.setAddressId(userAddress.getId());
        } else {
            preOrderInfoVo.setFreightFee(BigDecimal.ZERO);
            preOrderInfoVo.setAddressId(0);
        }
        // 实际支付金额
        preOrderInfoVo.setPayFee(preOrderInfoVo.getProTotalFee().add(preOrderInfoVo.getFreightFee()));
        preOrderInfoVo.setUserIntegral(user.getIntegral());
        preOrderInfoVo.setUserBalance(user.getNowMoney());
        preOrderInfoVo.setIntegralDeductionSwitch(false);
        preOrderInfoVo.setIsUseIntegral(false);
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
        if ("true".equals(integralDeductionSwitch) && preOrderInfoVo.getProTotalFee().compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
            preOrderInfoVo.setIntegralDeductionSwitch(true);
        }
        // 缓存订单
        String key = user.getId() + CrmebDateUtil.getNowTime().toString() + CrmebUtil.getUuid();
        redisUtil.set(OrderConstants.PRE_ORDER_CACHE_PREFIX + key, JSONObject.toJSONString(preOrderInfoVo), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
        OrderNoResponse response = new OrderNoResponse();
        response.setOrderNo(key);
        response.setOrderType(preOrderInfoVo.getType());
        logger.info("preOrder response:{}", JSON.toJSONString(response));
        return response;
    }

    /**
     * 订单预下单V1.3
     *
     * @param request 预下单请求参数
     * @return PreOrderResponse
     */
    @Override
    public OrderNoResponse preOrder_V1_3(PreOrderRequest request) {
        User user = userService.getInfo();
        // 校验预下单商品信息
        PreOrderInfoVo preOrderInfoVo = validatePreOrderRequest(request, user);
        List<PreOrderInfoDetailVo> orderInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : preOrderInfoVo.getMerchantOrderVoList()) {
            orderInfoList.addAll(merchantOrderVo.getOrderInfoList());
            BigDecimal merTotalPrice = merchantOrderVo.getOrderInfoList().stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrderVo.setProTotalFee(merTotalPrice);
            merchantOrderVo.setProTotalNum(merchantOrderVo.getOrderInfoList().stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum());
        }
        // 商品总计金额
        BigDecimal totalPrice = orderInfoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        preOrderInfoVo.setProTotalFee(totalPrice);
        // 购买商品总数量
        int orderProNum = orderInfoList.stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum();
        preOrderInfoVo.setOrderProNum(orderProNum);
        // 获取默认地址
        UserAddress userAddress = userAddressService.getDefaultByUid(user.getId());
        if (ObjectUtil.isNotNull(userAddress)) {
            // 计算运费
            getFreightFee(preOrderInfoVo, userAddress);
            preOrderInfoVo.setAddressId(userAddress.getId());
        } else {
            preOrderInfoVo.setFreightFee(BigDecimal.ZERO);
            preOrderInfoVo.setAddressId(0);
        }
        // 实际支付金额
        preOrderInfoVo.setPayFee(preOrderInfoVo.getProTotalFee().add(preOrderInfoVo.getFreightFee()));
        preOrderInfoVo.setUserIntegral(user.getIntegral());
        preOrderInfoVo.setUserBalance(user.getNowMoney());
        preOrderInfoVo.setIntegralDeductionSwitch(false);
        preOrderInfoVo.setIsUseIntegral(false);
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
        if ("true".equals(integralDeductionSwitch) && preOrderInfoVo.getProTotalFee().compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
            preOrderInfoVo.setIntegralDeductionSwitch(true);
        }

        if (request.getPreOrderType().equals("video") || request.getPreOrderType().equals("seckill")) {
            // 活动商品不使用优惠券
            // 缓存订单
            String key = user.getId() + CrmebDateUtil.getNowTime().toString() + CrmebUtil.getUuid();
            redisUtil.set(OrderConstants.PRE_ORDER_CACHE_PREFIX + key, JSONObject.toJSONString(preOrderInfoVo), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
            OrderNoResponse response = new OrderNoResponse();
            response.setOrderNo(key);
            response.setOrderType(preOrderInfoVo.getType());
            logger.info("preOrder response:{}", JSON.toJSONString(response));
            return response;
        }
        preOrderSetCouponPrice(preOrderInfoVo, orderInfoList, user);
        preOrderInfoVo.setPayFee(preOrderInfoVo.getProTotalFee().add(preOrderInfoVo.getFreightFee()).subtract(preOrderInfoVo.getCouponFee()));

        // 缓存订单
        String key = user.getId() + CrmebDateUtil.getNowTime().toString() + CrmebUtil.getUuid();
        redisUtil.set(OrderConstants.PRE_ORDER_CACHE_PREFIX + key, JSONObject.toJSONString(preOrderInfoVo), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
        OrderNoResponse response = new OrderNoResponse();
        response.setOrderNo(key);
        response.setOrderType(preOrderInfoVo.getType());
        logger.info("preOrder response:{}", JSON.toJSONString(response));
        return response;
    }

    private void preOrderSetCouponPrice(PreOrderInfoVo preOrderInfoVo, List<PreOrderInfoDetailVo> orderInfoList, User user) {
        // 自动领券计算
        BigDecimal couponPrice = BigDecimal.ZERO;
        List<Integer> proIdsList = orderInfoList.stream().map(PreOrderInfoDetailVo::getProductId).distinct().collect(Collectors.toList());
        List<Integer> merIdList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : preOrderInfoVo.getMerchantOrderVoList()) {
            Integer merId = merchantOrderVo.getMerId();
            merIdList.add(merId);
            List<Integer> proIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
            BigDecimal merPrice = merchantOrderVo.getProTotalFee();
            List<Coupon> merCouponList = couponService.findManyByMerIdAndMoney(merId, proIdList, merPrice);
            for (int i = 0; i < merCouponList.size(); ) {
                Coupon coupon = merCouponList.get(i);
                if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    i++;
                    continue;
                }
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    merCouponList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    merCouponList.remove(i);
                    continue;
                }
                i++;
            }
            // 查询适用的用户优惠券
            List<CouponUser> merCouponUserList = couponUserService.findManyByUidAndMerIdAndMoneyAndProList(user.getId(), merchantOrderVo.getMerId(), proIdList, merPrice);
            for (int i = 0; i < merCouponUserList.size(); ) {
                CouponUser couponUser = merCouponUserList.get(i);
                if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    i++;
                    continue;
                }
                Coupon coupon = couponService.getById(couponUser.getCouponId());
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                i++;
            }

            List<Integer> cidList = merCouponUserList.stream().map(CouponUser::getCouponId).collect(Collectors.toList());
            for (int i = 0; i < merCouponList.size(); ) {
                if (cidList.contains(merCouponList.get(i).getId())) {
                    merCouponList.remove(i);
                    continue;
                }
                if (!couponUserService.userIsCanReceiveCoupon(merCouponList.get(i), user.getId())) {
                    merCouponList.remove(i);
                    continue;
                }
                i++;
            }

            if (CollUtil.isEmpty(merCouponList) && CollUtil.isEmpty(merCouponUserList)) {
                continue;
            }
            if (CollUtil.isEmpty(merCouponList)) {
                CouponUser couponUser = merCouponUserList.get(0);
                merchantOrderVo.setUserCouponId(couponUser.getId());
                merchantOrderVo.setCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, null, couponUser);
                couponPrice = couponPrice.add(merchantOrderVo.getMerCouponFee());
                continue;
            }
            if (CollUtil.isEmpty(merCouponUserList)) {
                Coupon coupon = merCouponList.get(0);
                Integer couponUserId = couponUserService.autoReceiveCoupon(coupon, user.getId());
                merchantOrderVo.setUserCouponId(couponUserId);
                merchantOrderVo.setCouponFee(new BigDecimal(coupon.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(coupon.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, coupon, null);
                couponPrice = couponPrice.add(merchantOrderVo.getMerCouponFee());
                continue;
            }

            Coupon coupon = merCouponList.get(0);
            CouponUser couponUser = merCouponUserList.get(0);
            if (couponUser.getMoney() >= coupon.getMoney()) {
                // 使用用户优惠券
                merchantOrderVo.setUserCouponId(merCouponUserList.get(0).getId());
                merchantOrderVo.setCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, null, couponUser);
            } else {
                // 领取优惠券下单
                Integer couponUserId = couponUserService.autoReceiveCoupon(merCouponList.get(0), user.getId());
                merchantOrderVo.setUserCouponId(couponUserId);
                merchantOrderVo.setCouponFee(new BigDecimal(coupon.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(coupon.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, coupon, null);
            }
            couponPrice = couponPrice.add(merchantOrderVo.getMerCouponFee());
        }

        preOrderInfoVo.setMerCouponFee(couponPrice);
        // 自动领平台券计算
        // 查所有适用的平台优惠券
        BigDecimal remainPrice = preOrderInfoVo.getProTotalFee().subtract(preOrderInfoVo.getMerCouponFee());
        List<Product> productList = productService.findByIds(proIdsList);
        List<Integer> proCategoryIdList = productList.stream().map(Product::getCategoryId).collect(Collectors.toList());
        List<Integer> secondParentIdList = productCategoryService.findParentIdByChildIds(proCategoryIdList);
        List<Integer> firstParentIdList = productCategoryService.findParentIdByChildIds(secondParentIdList);
        proCategoryIdList.addAll(secondParentIdList);
        proCategoryIdList.addAll(firstParentIdList);
        List<Integer> brandIdList = productList.stream().map(Product::getBrandId).collect(Collectors.toList());
        List<Coupon> platCouponList = couponService.findManyPlatByMerIdAndMoney(proIdsList, proCategoryIdList, merIdList, brandIdList, preOrderInfoVo.getProTotalFee());
        for (int i = 0; i < platCouponList.size(); ) {
            Coupon coupon = platCouponList.get(i);
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (remainPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            i++;
        }
        // 查询适用的用户平台优惠券
        List<CouponUser> platCouponUserList = couponUserService.findManyPlatByUidAndMerIdAndMoneyAndProList(user.getId(), proIdsList, proCategoryIdList, merIdList, brandIdList, preOrderInfoVo.getProTotalFee());
        for (int i = 0; i < platCouponUserList.size(); ) {
            CouponUser couponUser = platCouponUserList.get(i);
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (remainPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            Coupon coupon = couponService.getById(couponUser.getCouponId());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            i++;
        }

        List<Integer> platCidList = platCouponUserList.stream().map(CouponUser::getCouponId).collect(Collectors.toList());
        for (int i = 0; i < platCouponList.size(); ) {
            if (platCidList.contains(platCouponList.get(i).getId())) {
                platCouponList.remove(i);
                continue;
            }
            if (!couponUserService.userIsCanReceiveCoupon(platCouponList.get(i), user.getId())) {
                platCouponList.remove(i);
                continue;
            }
            i++;
        }

        if (CollUtil.isNotEmpty(platCouponUserList) && CollUtil.isEmpty(platCouponList)) {
            // 使用用户优惠券
            preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponUserList.get(0).getMoney().toString()));
            preOrderInfoVo.setPlatUserCouponId(platCouponUserList.get(0).getId());
        }
        if (CollUtil.isEmpty(platCouponUserList) && CollUtil.isNotEmpty(platCouponList)) {
            // 领取优惠券下单
            preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponList.get(0).getMoney().toString()));
            Integer couponUserId = couponUserService.autoReceiveCoupon(platCouponList.get(0), user.getId());
            preOrderInfoVo.setPlatUserCouponId(couponUserId);
        }
        if (CollUtil.isNotEmpty(platCouponUserList) && CollUtil.isNotEmpty(platCouponList)) {
            Long platCouponMoney = platCouponList.get(0).getMoney();
            Long platCouponUserMoney = platCouponUserList.get(0).getMoney();
            if (platCouponUserMoney >= platCouponMoney) {
                // 使用用户优惠券
                preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponUserList.get(0).getMoney().toString()));
                preOrderInfoVo.setPlatUserCouponId(platCouponUserList.get(0).getId());
            } else {
                // 领取优惠券下单
                preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponList.get(0).getMoney().toString()));
                Integer couponUserId = couponUserService.autoReceiveCoupon(platCouponList.get(0), user.getId());
                preOrderInfoVo.setPlatUserCouponId(couponUserId);
            }
        }
        preOrderInfoVo.setCouponFee(preOrderInfoVo.getMerCouponFee().add(preOrderInfoVo.getPlatCouponFee()));
    }

    /**
     * 设置订单商品详情商户优惠价格
     */
    private void setMerProCouponPrice(PreMerchantOrderVo merchantOrderVo, Coupon coupon, CouponUser couponUser) {
        if (ObjectUtil.isNull(coupon)) {
            coupon = couponService.getById(couponUser.getCouponId());
        }
        List<PreOrderInfoDetailVo> proDtoList = new ArrayList<>();
        if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
            proDtoList = merchantOrderVo.getOrderInfoList();
        }
        if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
            List<Integer> proIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
            proDtoList = merchantOrderVo.getOrderInfoList().stream().filter(d -> proIdList.contains(d.getProductId())).collect(Collectors.toList());
        }
        BigDecimal proTotalPrice = proDtoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal couponPrice = merchantOrderVo.getCouponFee();
        for (int i = 0; i < proDtoList.size(); i++) {
            PreOrderInfoDetailVo d = proDtoList.get(i);
            if (proDtoList.size() == (i + 1)) {
                d.setMerCouponPrice(couponPrice);
                break;
            }
            BigDecimal proPrice = d.getPrice().multiply(new BigDecimal(d.getPayNum().toString()));
            BigDecimal ratio = proPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
            BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
            couponPrice = couponPrice.subtract(detailCouponFee);
            d.setMerCouponPrice(detailCouponFee);
        }
    }

    /**
     * 加载预下单信息
     *
     * @param preOrderNo 预下单号
     * @return 预下单信息
     */
    @Override
    public PreOrderResponse loadPreOrder(String preOrderNo) {
        UserInfoResponse userInfo = userService.getUserInfo();
        // 通过缓存获取预下单对象
        String key = OrderConstants.PRE_ORDER_CACHE_PREFIX + preOrderNo;
        boolean exists = redisUtil.exists(key);
        if (!exists) {
            throw new CrmebException("预下单订单不存在");
        }
        String orderVoString = redisUtil.get(key);
        PreOrderInfoVo orderInfoVo = JSONObject.parseObject(orderVoString, PreOrderInfoVo.class);
        PreOrderResponse preOrderResponse = new PreOrderResponse();
        BeanUtils.copyProperties(orderInfoVo, preOrderResponse);

        List<PreOrderMerchantInfoResponse> infoResponseList = new ArrayList<>();
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        if (orderInfoVo.getType() == 1 || orderInfoVo.getType() == 2) {
            for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
                PreOrderMerchantInfoResponse infoResponse = new PreOrderMerchantInfoResponse();
                BeanUtils.copyProperties(merchantOrderVo, infoResponse);
                infoResponseList.add(infoResponse);
            }
            preOrderResponse.setMerchantInfoList(infoResponseList);
            return preOrderResponse;
        }
        List<Integer> merIdList = new ArrayList<>();
        List<Integer> proIdList = new ArrayList<>();
        List<Product> productList = new ArrayList<>();
        List<PreOrderInfoDetailVo> orderDetailInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            merIdList.add(merchantOrderVo.getMerId());
            PreOrderMerchantInfoResponse infoResponse = new PreOrderMerchantInfoResponse();
            BeanUtils.copyProperties(merchantOrderVo, infoResponse);
            merchantOrderVo.getOrderInfoList().forEach(info -> {
                if (!proIdList.contains(info.getProductId())) {
                    Product product = productService.getById(info.getProductId());
                    productList.add(product);
                    proIdList.add(product.getId());
                }
            });
            orderDetailInfoList.addAll(merchantOrderVo.getOrderInfoList());
            // 查询适用的用户优惠券
            List<Integer> merProIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).distinct().collect(Collectors.toList());
            BigDecimal merPrice = merchantOrderVo.getProTotalFee();
            BigDecimal merRemainingAmount = merchantOrderVo.getProTotalFee().subtract(orderInfoVo.getPlatCouponFee());
            List<CouponUser> merCouponUserList = couponUserService.findManyByUidAndMerIdAndMoneyAndProList(userInfo.getId(), merchantOrderVo.getMerId(), merProIdList, merPrice);
            for (int i = 0; i < merCouponUserList.size(); ) {
                CouponUser couponUser = merCouponUserList.get(i);
                if (merchantOrderVo.getUserCouponId() > 0 && couponUser.getId().equals(merchantOrderVo.getUserCouponId())) {
                    couponUser.setIsChecked(true);
                    couponUser.setIsChoose(true);
                    i++;
                    continue;
                }
                if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    if (merRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                    i++;
                    continue;
                }
                Coupon coupon = couponService.getById(couponUser.getCouponId());
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proRemainingAmount = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getPlatCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proRemainingAmount.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0) {
                    if (proRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                }
                i++;
            }
            infoResponse.setMerCouponUserList(merCouponUserList);
            infoResponseList.add(infoResponse);
        }
        preOrderResponse.setMerchantInfoList(infoResponseList);

        // 获取平台可用优惠券列表
        BigDecimal proTotalFee = orderInfoVo.getProTotalFee();
        List<Integer> proCategoryIdList = productList.stream().map(Product::getCategoryId).collect(Collectors.toList());
        List<Integer> secondParentIdList = productCategoryService.findParentIdByChildIds(proCategoryIdList);
        List<Integer> firstParentIdList = productCategoryService.findParentIdByChildIds(secondParentIdList);
        proCategoryIdList.addAll(secondParentIdList);
        proCategoryIdList.addAll(firstParentIdList);
        List<Integer> brandIdList = productList.stream().map(Product::getBrandId).collect(Collectors.toList());
        List<CouponUser> platCouponUserList = couponUserService.findManyPlatByUidAndMerIdAndMoneyAndProList(userInfo.getId(), proIdList, proCategoryIdList, merIdList, brandIdList, proTotalFee);

        BigDecimal remainingAmount = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
        for (CouponUser couponUser : platCouponUserList) {
            if (orderInfoVo.getPlatUserCouponId() > 0 && couponUser.getId().equals(orderInfoVo.getPlatUserCouponId())) {
                couponUser.setIsChecked(true);
                couponUser.setIsChoose(true);
            }
            // 判断是否可用
            if (remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (orderInfoVo.getProTotalFee().compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            Coupon coupon = couponService.getById(couponUser.getCouponId());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
        }

        preOrderResponse.setPlatCouponUserList(platCouponUserList);
        return preOrderResponse;
    }

    /**
     * 计算订单价格
     *
     * @param request 计算订单价格请求对象
     * @return ComputedOrderPriceResponse
     */
    @Override
    public ComputedOrderPriceResponse computedOrderPrice(OrderComputedPriceRequest request) {
        // 通过缓存获取预下单对象
        String key = OrderConstants.PRE_ORDER_CACHE_PREFIX + request.getPreOrderNo();
        boolean exists = redisUtil.exists(key);
        if (!exists) {
            throw new CrmebException("预下单订单不存在");
        }
        List<OrderMerchantRequest> orderMerchantRequestList = request.getOrderMerchantRequestList();
        if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP))) {
            orderMerchantRequestList.forEach(m -> {
                if (m.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                    Merchant merchant = merchantService.getByIdException(m.getMerId());
                    if (!merchant.getIsTakeTheir()) {
                        throw new CrmebException("请先联系商户管理员开启门店自提");
                    }
                }
            });
        }

        String orderVoString = redisUtil.get(key).toString();
        PreOrderInfoVo orderInfoVo = JSONObject.parseObject(orderVoString, PreOrderInfoVo.class);
        User user = userService.getInfo();
        return computedPrice(request, orderInfoVo, user);
    }

    /**
     * <b> 体检虚拟订单退款 </b>
     * @author ZXF
     * @create 2025/08/27 0027 16:59
     * @version
     * @注意事项 </b>
     */
    @Override
    public boolean virtualRefund(CreateVirtualOrderRequest virtualOrderRequest) {
        User user = userService.getInfo();
        String[] virtualOrderNos = virtualOrderRequest.getVirtualOrderNos().split(",");
        Map<String, Long> countMap = Arrays.stream(virtualOrderNos)
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        return virtualOrderService.changeNumByVirtualOrderMap(countMap,user);
    }

    /**
     * 创建订单(根据虚单转实单)
     *
     * @param virtualOrderRequest 创建订单请求参数
     * @return OrderPayResultResponse
     */
    @Override
    public OrderPayResultResponse createHealthOrder(CreateVirtualOrderRequest virtualOrderRequest) {
        OrderPayResultResponse response = new OrderPayResultResponse();
        //验证业务是否已经执行防止重复提交业务
        List<OrderDetail> dlist = orderDetailService.getBySourceId(virtualOrderRequest.getSourceId());
        if (dlist.size() > 0) {
            response.setStatus(true);
            return response;
        }
        String[] virtualOrderNos = virtualOrderRequest.getVirtualOrderNos().split(",");
        Map<String, Long> countMap = Arrays.stream(virtualOrderNos)
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        List<VirtualOrderResponse> list = new ArrayList<>();
        //同规格同买方同卖方归为一类
        Map<String, VirtualOrderResponse> responseMap = new HashMap<>();

        countMap.forEach((key, value) -> {
            VirtualOrderResponse orderResponse = virtualOrderService.getVirtualOrderByVirtualOrderNo(key);
            if(orderResponse.getAvailableNum() < value){
                throw new CrmebException("企业（"+orderResponse.getUName()+"）送检超额，请联系平台管理员");
            }
            Integer num = Math.toIntExact(value);
            BigDecimal totalPrice = orderResponse.getPrice().multiply(new BigDecimal(value.toString()));

            String proKey = orderResponse.getUid()+":"+orderResponse.getMerId()+":"+orderResponse.getAttrValueId();
            VirtualOrderResponse proOrderResponse = responseMap.get(proKey);
            //收集归类的所有虚单
            JSONArray arr;
            if(proOrderResponse == null){
                arr = new JSONArray();
                orderResponse.setTotalPrice(totalPrice);
                orderResponse.setPayNum(num);
            }else{
                arr = JSONArray.parseArray(proOrderResponse.getVirtualNos());
                orderResponse.setTotalPrice(proOrderResponse.getTotalPrice().add(totalPrice));
                orderResponse.setPayNum(proOrderResponse.getPayNum()+num);
            }
            JSONObject json = new JSONObject();
            json.put("virtualOrderNo", orderResponse.getVirtualOrderNo());
            json.put("num", num);
            arr.add(json);
            orderResponse.setVirtualNos(arr.toJSONString());
            responseMap.put(proKey, orderResponse);
        });
        //同一类添加订单生成前置缓存
        responseMap.forEach((key, value) -> {
            VirtualOrderResponse orderResponse = value;
            PreOrderInfoVo preOrderInfoVo = JSONObject.parseObject(orderResponse.getPreOrderInfoVo(), PreOrderInfoVo.class);
            preOrderInfoVo.setOrderProNum(orderResponse.getPayNum());
            preOrderInfoVo.setProTotalFee(orderResponse.getTotalPrice());
            List<PreMerchantOrderVo> merchantOrderVoList = preOrderInfoVo.getMerchantOrderVoList();
            Integer productId;
            for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList){
                merchantOrderVo.setProTotalFee(orderResponse.getTotalPrice());
                merchantOrderVo.setProTotalNum(orderResponse.getPayNum());
                List<PreOrderInfoDetailVo> orderInfoList = merchantOrderVo.getOrderInfoList();
                //TODO 得确认下商品详情数量是多少，这里是一件一条还是就一条记录多件
                for (PreOrderInfoDetailVo orderInfoDetailVo : orderInfoList) {
                    orderInfoDetailVo.setPayPrice(orderResponse.getTotalPrice());
                    orderInfoDetailVo.setPayNum(orderResponse.getPayNum());
                    orderInfoDetailVo.setSourceId(virtualOrderRequest.getSourceId());
                    productId = orderInfoDetailVo.getProductId();
                }
                merchantOrderVo.setOrderInfoList(orderInfoList);
            }
            preOrderInfoVo.setMerchantOrderVoList(merchantOrderVoList);
            CreateOrderRequest orderRequest = JSONObject.parseObject(orderResponse.getCreateOrderRequest(), CreateOrderRequest.class);
            //根据大类生成订单号前缀
//            List<Integer> productIds = merchantOrderVoList.stream().flatMap(ov -> ov.getOrderInfoList().stream()).map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
//            Integer bizType = orderService.getBizTypeByProductsType(productIds);
            String orderNo = CrmebUtil.getOrderNo(BizEnum.beforeRule(orderResponse.getBizType()));
            orderRequest.setPreOrderNo(orderNo);
            JSONObject productCache = new JSONObject();
            productCache.put("productName", orderResponse.getProductName());
            productCache.put("bizType", orderResponse.getBizType());
            productCache.put("bizSubType", orderResponse.getBizSubType());
            orderResponse.setCachePreOrderInfoVo(JSONObject.toJSONString(preOrderInfoVo));
            orderResponse.setCacheUser(JSONObject.toJSONString(userService.getById(orderResponse.getUid())));
            orderResponse.setCacheProduct(productCache.toJSONString());
            orderResponse.setCreateOrderRequest(JSONObject.toJSONString(orderRequest));
            list.add(orderResponse);
        });

        //异步批量支付处理
        batchPayment(list);

        response.setStatus(true);
        return response;
    }

    /**
     * <b> 异步创建订单并且完成支付 </b>
     * @author ZXF
     * @create 2025/08/27 0027 15:19
     * @version
     * @注意事项 </b>
     */
    @Async
    public void batchPayment(List<VirtualOrderResponse> list){
        for (VirtualOrderResponse orderResponse : list){
            if(!virtualOrderCreateAndPay(orderResponse)){
                taskResidualService.saveTaskResidual("virtualOrderCreateAndPay","异步创建订单并且完成支付","com.zbkj.front.service.FrontOrderService",1,"com.zbkj.common.response.VirtualOrderResponse",JSONObject.toJSONString(orderResponse));
            }
        }
    }

    /**
     * <b> 异步创建订单并且完成支付 </b>
     * @author ZXF
     * @create 2025/08/27 0027 15:43
     * @version
     * @注意事项 </b>
     */
    @Override
    public boolean virtualOrderCreateAndPay(VirtualOrderResponse orderResponse) {
        OrderPayRequest orderPayRequest = new OrderPayRequest();
        Boolean execute = transactionTemplate.execute(e -> {
            try {
                CreateOrderRequest orderRequest = JSONObject.parseObject(orderResponse.getCreateOrderRequest(), CreateOrderRequest.class);
                String orderNo = orderRequest.getPreOrderNo();
                Order order = orderService.findByOrderNo(orderNo);
                if(order == null){
                    redisUtil.set(OrderConstants.PRE_ORDER_CACHE_PREFIX + orderNo, orderResponse.getCachePreOrderInfoVo(), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
                    redisUtil.set(OrderConstants.PRE_ORDER_USER_CACHE_PREFIX + orderNo, orderResponse.getCacheUser(), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
                    redisUtil.set(OrderConstants.PRE_ORDER_PRODUCT_CACHE_PREFIX + orderNo, orderResponse.getCacheProduct(), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
                    redisUtil.set(OrderConstants.PRE_ORDER_PLATFEE_CACHE_PREFIX + orderNo, orderResponse.getPlatFeeConfig(), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
                    //存放了标识+原虚单号
                    redisUtil.set(OrderConstants.PRE_ORDER_VIRTUAL_CACHE_PREFIX + orderNo, OrderConstants.ORDER_ATTR_VIRTUAL_SIGN+orderResponse.getVirtualOrderNo(), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
                    OrderNoResponse response = createOrder(orderRequest);
                    orderNo = response.getOrderNo();
                }
                // 以上完成订单创建，以下逻辑处理订单实际支付（考培支付流程）待开发
                orderPayRequest.setOrderNo(orderNo);
                orderPayRequest.setPayType(PayConstants.PAY_TYPE_WALLET);
                orderPayRequest.setPayChannel(PayConstants.PAY_TYPE_WALLET);
                orderPayRequest.setScene(orderResponse.getUid());//这里当做存用户id
                orderPayRequest.setFrom(orderResponse.getTotalPrice().toPlainString());//这里当做存金额
                return Boolean.TRUE;
            }catch (Exception ex){
                logger.error("体检类商品创建订单.virtualOrderCreateAndPay --> 虚单订单信息：{}，异常信息：{}", JSON.toJSONString(orderResponse), ex.getMessage());
                return Boolean.FALSE;
            }
        });
        if (!execute) {
            return Boolean.FALSE;
        }

        //以下业务不能跟上面创建订单业务放同个事物，原因：以下业务完成支付后需要异步对订单做拆单处理查数据库会找不到订单，因为同个事物业务未完成未提交入库
        if(!virtualPayment(orderPayRequest)){
            taskResidualService.saveTaskResidual("virtualPayment","体检业务实际交易业务","com.zbkj.front.service.FrontOrderService",1,"com.zbkj.common.request.OrderPayRequest",JSONObject.toJSONString(orderPayRequest));
        }
        if(!dealVirtualNum(orderResponse)){
            taskResidualService.saveTaskResidual("dealVirtualNum","体检业务虚拟订单设置成交数量","com.zbkj.front.service.FrontOrderService",1,"com.zbkj.common.response.VirtualOrderResponse",JSONObject.toJSONString(orderResponse));
        }
        return Boolean.TRUE;
    }

    /**
     * <b> 体检业务实际交易业务 </b>
     * @author ZXF
     * @create 2025/08/28 0028 14:14
     * @version
     * @注意事项 </b>
     */
    @Override
    public boolean virtualPayment(OrderPayRequest orderPayRequest) {
        Boolean execute = transactionTemplate.execute(e -> {
            try {
//                User user = userService.getById(orderPayRequest.getScene());
                payService.payment(orderPayRequest);
                return Boolean.TRUE;
            }catch (Exception ex){
                logger.error("体检类订单操作支付.virtualPayment --> 订单支付信息：{}，异常信息：{}", JSON.toJSONString(orderPayRequest), ex.getMessage());
                return Boolean.FALSE;
            }
        });
        if (!execute) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * <b> 体检业务实际交易业务完成后更新对应虚拟单使用数量 </b>
     * @author ZXF
     * @create 2025/08/28 0028 14:14
     * @version
     * @注意事项 </b>
     */
    @Override
    public boolean dealVirtualNum(VirtualOrderResponse orderResponse) {
        try {
            JSONArray virtualNoObjs =JSONArray.parseArray(orderResponse.getVirtualNos());
            Map<String, Long> countMap = Maps.newHashMap();
            for(Object obj:virtualNoObjs){
                JSONObject json = (JSONObject) obj;
                countMap.put(json.getString("virtualOrderNo"), json.getLong("num"));
            }
            User user = userService.getById(orderResponse.getUid());
            return virtualOrderService.dealNumByVirtualOrderMap(countMap,user);
        }catch (Exception ex){
            logger.error("体检类虚拟订单操作成交数量修改.changeVirtualNum --> 订单支付信息：{}，异常信息：{}", JSON.toJSONString(orderResponse), ex.getMessage());
            return Boolean.FALSE;
        }
    }

    /**
     * 创建订单
     *
     * @param orderRequest 创建订单请求参数
     * @return OrderNoResponse 订单编号
     */
    @Override
    public OrderNoResponse createOrder(CreateOrderRequest orderRequest) {
        // 通过缓存获取预下单对象
        String key = OrderConstants.PRE_ORDER_CACHE_PREFIX + orderRequest.getPreOrderNo();
        boolean exists = redisUtil.exists(key);
        if (!exists) {
            throw new CrmebException("预下单订单不存在");
        }
        String orderVoString = redisUtil.get(key).toString();
        PreOrderInfoVo orderInfoVo = JSONObject.parseObject(orderVoString, PreOrderInfoVo.class);
        User user = userService.getInfo();

        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            return seckillService.createOrder(orderRequest, orderInfoVo, user);
        }

        UserAddress userAddress = null;
        List<OrderMerchantRequest> orderMerchantRequestList = orderRequest.getOrderMerchantRequestList();
        if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_EXPRESS))) {
            if (ObjectUtil.isNull(orderRequest.getAddressId())) {
                throw new CrmebException("请选择收货地址");
            }
            userAddress = userAddressService.getById(orderRequest.getAddressId());
            if (ObjectUtil.isNull(userAddress) || userAddress.getIsDel()) {
                throw new CrmebException("收货地址有误");
            }
        }
        if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP))) {
            orderMerchantRequestList.forEach(m -> {
                if (m.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                    Merchant merchant = merchantService.getByIdException(m.getMerId());
                    if (!merchant.getIsTakeTheir()) {
                        throw new CrmebException("请先联系商户管理员开启门店自提");
                    }
                }
            });
        }
        String virtualkey = OrderConstants.PRE_ORDER_VIRTUAL_CACHE_PREFIX + orderRequest.getPreOrderNo();
        boolean vExists = redisUtil.exists(virtualkey);
        String barCode = "";
        if (vExists) {
            barCode = redisUtil.get(virtualkey).toString();
        }
        String orderNo = "";
        String virtualOrderNo = "";
        Integer bizType = 0;
        Integer bizSubType = 0;
        String productName = "";
        boolean isChangeRecord = true;//是否修改库存
        List<MyRecord> skuRecordList = null;
        if(StringUtils.isEmpty(barCode)){
            // 校验商品库存
            skuRecordList = validateProductStock(orderInfoVo);

            // 查询商品规格属性值信息
            ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(skuRecordList.get(0).getInt("attrValueId")
                    , orderInfoVo.getMerchantOrderVoList().get(0).getOrderInfoList().get(0).getProductId(), ProductConstants.PRODUCT_TYPE_NORMAL);
            if (ObjectUtil.isNull(attrValue)) {
                throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
            }
            barCode = attrValue.getBarCode();
            //商品规格标记虚拟标识、PreOrderNo不带虚单标识、不带特殊标识走创建虚单
            if (StringUtils.isNotEmpty(barCode) && barCode.startsWith(OrderConstants.ORDER_ATTR_VIRTUAL_SIGN)
                    &&!orderRequest.getPreOrderNo().startsWith(BizEnum.SIR.toString())
                    &&!orderRequest.getPreOrderNo().startsWith(BizEnum.SPA.toString())) {
                return virtualOrderService.createOrder(orderRequest, orderInfoVo,skuRecordList, user);
            }
        }else{
            //虚单生成实单需要用到的业务
            // 通过缓存获取预下单对象
            String uKey = OrderConstants.PRE_ORDER_USER_CACHE_PREFIX + orderRequest.getPreOrderNo();
            boolean uExists = redisUtil.exists(uKey);
            if (!uExists) {
                throw new CrmebException("预下单虚单用户不存在");
            }
            String userString = redisUtil.get(uKey).toString();
            user = JSONObject.parseObject(userString,User.class);
            String pKey = OrderConstants.PRE_ORDER_PRODUCT_CACHE_PREFIX + orderRequest.getPreOrderNo();
            boolean pExists = redisUtil.exists(pKey);
            if (!pExists) {
                throw new CrmebException("预下单虚单商品不存在");
            }
            String productString = redisUtil.get(pKey).toString();
            JSONObject pJson = JSONObject.parseObject(productString);
            if(pJson != null){
                bizType = pJson.getInteger("bizType");
                bizSubType = pJson.getInteger("bizSubType");
                productName = pJson.getString("productName");
            }
            orderNo = orderRequest.getPreOrderNo();
            virtualOrderNo = barCode.replace(OrderConstants.ORDER_ATTR_VIRTUAL_SIGN, "");
            isChangeRecord = false;
        }

        orderInfoVo.getMerchantOrderVoList().forEach(e -> {
            orderMerchantRequestList.forEach(o -> {
                if (o.getMerId().equals(e.getMerId())) {
                    e.setShippingType(o.getShippingType());
                    e.setUserCouponId(o.getUserCouponId());
                    e.setCouponFee(BigDecimal.ZERO);
                    e.setMerCouponFee(BigDecimal.ZERO);
                }
            });
        });
        // 计算订单各种价格
        getFreightFee(orderInfoVo, userAddress);
        orderInfoVo.setPlatUserCouponId(orderRequest.getPlatUserCouponId());
        orderInfoVo.setPlatCouponFee(BigDecimal.ZERO);
        orderInfoVo.setCouponFee(BigDecimal.ZERO);
        orderInfoVo.setMerCouponFee(BigDecimal.ZERO);
        getCouponFee_V1_3(orderInfoVo, user.getId());
        if (orderRequest.getIsUseIntegral() && user.getIntegral() > 0) {// 使用积分
            integralDeductionComputed(orderInfoVo, user.getIntegral());
        }
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        List<Integer> productIds = merchantOrderVoList.stream().flatMap(ov -> ov.getOrderInfoList().stream()).map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
        if (productIds.size() != 1) {
            throw new CrmebException("一个订单内只能有一种类型的商品");
        }
        // 平台订单
        Order order = new Order();
        //如果走暂存购买份额类的业务orderNo从业务头携带进来，如果未携带的需要根据规则生成订单号
        if(StringUtils.isEmpty(orderNo)){
            Product product = productService.getById(productIds.get(0));
            bizSubType = product.getBizSubType();
            productName = product.getName();
            bizType = orderService.getBizTypeByProductsType(productIds);// 0-实物 1-虚拟 2-特殊(考培) 3-保险, 4-风评
            //没前缀加前缀
            orderNo = BizEnum.buildNoPrefix(orderRequest.getPreOrderNo(),bizType);
        }
//            orderNo = CrmebUtil.getOrderNo(OrderConstants.ORDER_PREFIX_PLATFORM);
        order.setRegionCode(user.getRegionCode());
        order.setOrderNo(orderNo);
        order.setMerId(orderInfoVo.getMerId());
        order.setUid(user.getId());
        order.setTotalNum(orderInfoVo.getOrderProNum());
        order.setProTotalPrice(orderInfoVo.getProTotalFee());
        order.setTotalPostage(orderInfoVo.getFreightFee());
        order.setTotalPrice(order.getProTotalPrice().add(order.getTotalPostage()));
        order.setCouponPrice(orderInfoVo.getCouponFee());
        order.setUseIntegral(merchantOrderVoList.stream().mapToInt(PreMerchantOrderVo::getUseIntegral).sum());
        order.setIntegralPrice(merchantOrderVoList.stream().map(PreMerchantOrderVo::getIntegralPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        order.setPayPrice(order.getProTotalPrice().add(order.getTotalPostage()).subtract(order.getCouponPrice()).subtract(order.getIntegralPrice()));
        order.setPayPostage(order.getTotalPostage());
        order.setPaid(false);
        order.setCancelStatus(OrderConstants.ORDER_CANCEL_STATUS_NORMAL);
        order.setLevel(OrderConstants.ORDER_LEVEL_PLATFORM);
        order.setType(orderInfoVo.getType());// 默认普通订单
        order.setMerCouponPrice(orderInfoVo.getMerCouponFee());
        order.setPlatCouponPrice(orderInfoVo.getPlatCouponFee());
        order.setPlatCouponId(orderInfoVo.getPlatUserCouponId());
        order.setBizType(bizType);
        order.setBizSubType(bizSubType);
        order.setUserName(user.getNickname());
        order.setMemberName(bcxDepartmentMemberService.getMemerNamesByUid(user.getId()));
        order.setProductName(productName);
        if (!StrUtil.isBlank(merchantOrderVoList.get(0).getOrderInfoList().get(0).getRegionCode())) {
            //风评订单从前端设置区域编码
            order.setRegionCode(merchantOrderVoList.get(0).getOrderInfoList().get(0).getRegionCode());
        }
        if (BIZ_TYPE_RISK.equals(order.getBizType())) {
            order.setType(OrderConstants.ORDER_TYPE_RISK);
        }
        order.setVirtualOrderNo(virtualOrderNo);

        //关联订单
        BcxOrderRelation relation;
        if (ProductConstants.BIZ_TYPE_RISK.equals(order.getBizType())) {
            relation = BcxOrderRelation.builder().orderNo(order.getOrderNo()).masterOrderNo(StrUtil.isNotBlank(orderInfoVo.getRelateOrderNo()) ? orderInfoVo.getRelateOrderNo() : order.getOrderNo()).build();
        } else {
            relation = null;
        }
        // 商户订单
        List<Integer> couponIdList = CollUtil.newArrayList();
        if (orderRequest.getPlatUserCouponId() > 0) {
            couponIdList.add(orderRequest.getPlatUserCouponId());
        }
        List<MerchantOrder> merchantOrderList = new ArrayList<>();
        List<OrderDetail> orderDetailList = new ArrayList<>();
        BcxPlatformFee bcxPlatformFee = new BcxPlatformFee();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            MerchantOrder merchantOrder = new MerchantOrder();
            merchantOrder.setOrderNo(order.getOrderNo());
            merchantOrder.setMerId(merchantOrderVo.getMerId());
            merchantOrder.setUid(user.getId());
            for (OrderMerchantRequest om : orderMerchantRequestList) {
                if (om.getMerId().equals(merchantOrderVo.getMerId())) {
                    if (StrUtil.isNotBlank(om.getRemark())) {
                        merchantOrder.setUserRemark(om.getRemark());
                    }
                    merchantOrder.setShippingType(om.getShippingType());
                    break;
                }
            }
            if (merchantOrder.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                merchantOrder.setUserAddress(merchantOrderVo.getMerName());
                merchantOrder.setVerifyCode(String.valueOf(CrmebUtil.randomCount(1111111111, 999999999)));
            } else if (merchantOrder.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_EXPRESS)) {
                if (userAddress == null) {
                    throw new CrmebException("请选择收货地址");
                }
                merchantOrder.setRealName(userAddress.getRealName());
                merchantOrder.setUserPhone(userAddress.getPhone());
                String userAddressStr = userAddress.getProvince() + userAddress.getCity() + userAddress.getDistrict() + userAddress.getStreet() + userAddress.getDetail();
                merchantOrder.setUserAddress(userAddressStr);
            } else {// shippingType == 0  无需配送商品，不应该有地址
                merchantOrder.setRealName("");
                merchantOrder.setUserPhone("");
                merchantOrder.setUserAddress("");
            }
            merchantOrder.setTotalNum(merchantOrderVo.getProTotalNum());
            merchantOrder.setProTotalPrice(merchantOrderVo.getProTotalFee());
            merchantOrder.setTotalPostage(merchantOrderVo.getFreightFee());
            merchantOrder.setTotalPrice(merchantOrder.getProTotalPrice().add(merchantOrder.getTotalPostage()));
            merchantOrder.setPayPostage(merchantOrder.getTotalPostage());
            merchantOrder.setUseIntegral(merchantOrderVo.getUseIntegral());
            merchantOrder.setIntegralPrice(merchantOrderVo.getIntegralPrice());
            merchantOrder.setCouponId(merchantOrderVo.getUserCouponId());
            if (merchantOrder.getCouponId() > 0) {
                couponIdList.add(merchantOrder.getCouponId());
            }
            List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList();
            BigDecimal merCouponPrice = detailVoList.stream().map(PreOrderInfoDetailVo::getMerCouponPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrder.setMerCouponPrice(merCouponPrice);
            BigDecimal platCouponPrice = detailVoList.stream().map(PreOrderInfoDetailVo::getPlatCouponPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrder.setPlatCouponPrice(platCouponPrice);
            merchantOrder.setCouponPrice(merchantOrder.getMerCouponPrice().add(merchantOrder.getPlatCouponPrice()));
            merchantOrder.setPayPrice(merchantOrder.getTotalPrice().subtract(merchantOrder.getCouponPrice()).subtract(merchantOrder.getIntegralPrice()));
            merchantOrder.setGainIntegral(0);
            merchantOrder.setType(order.getType());
            merchantOrderList.add(merchantOrder);

            BigDecimal totalPlatFee = BigDecimal.ZERO;
            BcxPlatformFeeConfig platFeeConfig = bcxPlatformFeeConfigService.queryByAnyId(order.getMerId(), 0, 0);
            for (PreOrderInfoDetailVo detailVo : detailVoList) {
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setOrderNo(order.getOrderNo());
                orderDetail.setMerId(merchantOrder.getMerId());
                orderDetail.setUid(user.getId());
                orderDetail.setProductId(detailVo.getProductId());
                orderDetail.setProductName(detailVo.getProductName());
                orderDetail.setImage(detailVo.getImage());
                orderDetail.setAttrValueId(detailVo.getAttrValueId());
                orderDetail.setSku(detailVo.getSku());
                orderDetail.setPrice(detailVo.getPrice());
                orderDetail.setPayNum(detailVo.getPayNum());
                orderDetail.setWeight(detailVo.getWeight());
                orderDetail.setVolume(detailVo.getVolume());
                orderDetail.setProductType(detailVo.getProductType());
                orderDetail.setSubBrokerageType(detailVo.getSubBrokerageType());
                orderDetail.setBrokerage(detailVo.getBrokerage());
                orderDetail.setBrokerageTwo(detailVo.getBrokerageTwo());
                orderDetail.setFreightFee(detailVo.getFreightFee());

                orderDetail.setUseIntegral(detailVo.getUseIntegral());
                orderDetail.setIntegralPrice(detailVo.getIntegralPrice());
                orderDetail.setSourceId(detailVo.getSourceId());
                //虚单转实单业务来源的走取缓存的方式获取分账配置，否则走原来流程
                String platFeekey = OrderConstants.PRE_ORDER_PLATFEE_CACHE_PREFIX + orderRequest.getPreOrderNo();
                boolean pfExists = redisUtil.exists(platFeekey);
                if (!pfExists) {
                    platFeeConfig = bcxPlatformFeeConfigService.queryByAnyId(order.getMerId(), orderDetail.getProductId(), 0);
                }else{
                    String feeJson = redisUtil.get(platFeekey).toString();
                    if(StringUtils.isNotEmpty(feeJson)){
                        platFeeConfig = JSONObject.parseObject(feeJson, BcxPlatformFeeConfig.class);
                    }
                }

                if (platFeeConfig == null) {
                    throw new CrmebException("平台服务费配置不存在，请联系客服");
                }
                BigDecimal platformFee = bcxPlatformFeeConfigService.calcPlatformFee(platFeeConfig, ProductConstants.SPEC_TYPE_SELL, order.getPayPrice(), orderDetail.getPayNum(), ProductConstants.SPEC_TYPE_SELL);
                orderDetail.setPlatFee(platformFee);
                totalPlatFee = totalPlatFee.add(platformFee);

                orderDetail.setPayPrice(BigDecimal.ZERO);

                orderDetail.setMerCouponPrice(detailVo.getMerCouponPrice());
                orderDetail.setPlatCouponPrice(detailVo.getPlatCouponPrice());
                orderDetail.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                BigDecimal detailPayPrice = orderDetail.getPrice().multiply(new BigDecimal(orderDetail.getPayNum().toString())).add(orderDetail.getFreightFee()).subtract(orderDetail.getCouponPrice()).subtract(orderDetail.getIntegralPrice());
                if (detailPayPrice.compareTo(BigDecimal.ZERO) >= 0) {
                    orderDetail.setPayPrice(detailPayPrice);
                }
                orderDetailList.add(orderDetail);
            }
            order.setPlatFee(totalPlatFee);

            bcxPlatformFee.setOrderType(0);
            bcxPlatformFee.setOrderNo(order.getOrderNo());
            bcxPlatformFee.setPlatFee(totalPlatFee);
            bcxPlatformFee.setTransPriority(bcxPlatformFeeConfigService.calcPlatformFeePriority(platFeeConfig, ProductConstants.SPEC_TYPE_SELL));
            bcxPlatformFee.setConfig(JSONObject.toJSONString(platFeeConfig));
        }

        logger.info("订单生成：商户订单列表： " + JSON.toJSONString(merchantOrderList));
        if(isChangeRecord){
            User finalUser = user;
            List<MyRecord> finalSkuRecordList = skuRecordList;
            Boolean execute = transactionTemplate.execute(e -> {
                Boolean result = true;
                logger.info("开始扣件商品库存:order:{}", JSON.toJSONString(order));
                if (order.getType().equals(OrderConstants.ORDER_TYPE_NORMAL) || order.getType().equals(OrderConstants.ORDER_TYPE_RISK)) { // 普通商品和风评商品
                    logger.info("开始扣件商品库存 --> 普通商品:{}", JSON.toJSONString(finalSkuRecordList));
                    // 扣减库存
                    for (MyRecord skuRecord : finalSkuRecordList) {
                        // 普通商品口库存
                        result = productService.operationStock(skuRecord.getInt("productId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT);
                        if (!result) {
                            e.setRollbackOnly();
                            logger.error("生成订单扣减商品库存失败,预下单号：{},商品ID：{}", orderRequest.getPreOrderNo(), skuRecord.getInt("productId"));
                            return result;
                        }
                        // 普通商品规格扣库存
                        result = productAttrValueService.operationStock(skuRecord.getInt("attrValueId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT, ProductConstants.PRODUCT_TYPE_NORMAL, skuRecord.getInt("attrValueVersion"));
                        if (!result) {
                            e.setRollbackOnly();
                            logger.error("生成订单扣减商品sku库存失败,预下单号：{},商品skuID：{}", orderRequest.getPreOrderNo(), skuRecord.getInt("attrValueId"));
                            return result;
                        }
                    }
                }
                if (order.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {// 视频号订单
                    logger.info("开始扣件商品库存 --> 视频号商品:{}", JSON.toJSONString(finalSkuRecordList));
                    MyRecord skuRecord = finalSkuRecordList.get(0);
                    // 商品规格表扣库存
                    productAttrValueService.operationStock(skuRecord.getInt("attrValueId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT, ProductConstants.PRODUCT_TYPE_COMPONENT, skuRecord.getInt("attrValueVersion"));
                    // 视频商品规格表扣库存
                    payComponentProductSkuService.operationStock(skuRecord.getInt("skuId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT, skuRecord.getInt("skuVersion"));
                    // 视频号商品扣库存
                    payComponentProductService.operationStock(skuRecord.getInt("productId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT);
                }
                if (order.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
                    logger.info("开始扣件商品库存 --> 秒杀商品:{}", JSON.toJSONString(finalSkuRecordList));
                    MyRecord skuRecord = finalSkuRecordList.get(0);
                    seckillService.subStock(skuRecord);
                }
                orderService.save(order);
                bcxPlatformFeeService.save(bcxPlatformFee);
                if (relation != null) {
                    bcxOrderRelationService.save(relation);
                }
                merchantOrderService.saveBatch(merchantOrderList);
                orderDetailService.saveBatch(orderDetailList);
                // 扣除用户积分
                if (order.getUseIntegral() > 0) {
                    result = userService.updateIntegral(finalUser.getId(), order.getUseIntegral(), Constants.OPERATION_TYPE_SUBTRACT);
                    if (!result) {
                        e.setRollbackOnly();
                        logger.error("生成订单扣除用户积分失败,预下单号：{}", orderRequest.getPreOrderNo());
                        return result;
                    }
                    UserIntegralRecord userIntegralRecord = initOrderUseIntegral(finalUser.getId(), order.getUseIntegral(), finalUser.getIntegral(), order.getOrderNo());
                    userIntegralRecordService.save(userIntegralRecord);
                }

                if (CollUtil.isNotEmpty(couponIdList)) {
                    couponUserService.useBatch(couponIdList);
                }
                // 生成订单日志
                orderStatusService.createLog(order.getOrderNo(), OrderStatusConstants.ORDER_STATUS_CREATE, OrderStatusConstants.ORDER_LOG_MESSAGE_CREATE);
                // 清除购物车数据
                if (CollUtil.isNotEmpty(orderInfoVo.getCartIdList())) {
                    cartService.deleteCartByIds(orderInfoVo.getCartIdList());
                }
                return Boolean.TRUE;
            });
            if (!execute) {
                throw new CrmebException("订单生成失败");
            }
        }else{
            Boolean execute = transactionTemplate.execute(e -> {
                Boolean result = true;
                logger.info("开始扣件商品库存:order:{}", JSON.toJSONString(order));

                orderService.save(order);
                bcxPlatformFeeService.save(bcxPlatformFee);
                if (relation != null) {
                    bcxOrderRelationService.save(relation);
                }
                merchantOrderService.saveBatch(merchantOrderList);
                orderDetailService.saveBatch(orderDetailList);

                if (CollUtil.isNotEmpty(couponIdList)) {
                    couponUserService.useBatch(couponIdList);
                }
                // 生成订单日志
                orderStatusService.createLog(order.getOrderNo(), OrderStatusConstants.ORDER_STATUS_CREATE, OrderStatusConstants.ORDER_LOG_MESSAGE_CREATE);
                return Boolean.TRUE;
            });
            if (!execute) {
                throw new CrmebException("订单生成失败");
            }
        }
        // 删除缓存订单
        if (redisUtil.exists(key)) {
            redisUtil.delete(key);
        }

        // 加入自动未支付自动取消队列
        redisUtil.lPush(TaskConstants.ORDER_TASK_REDIS_KEY_AUTO_CANCEL_KEY, order.getOrderNo());

        OrderNoResponse response = new OrderNoResponse();
        response.setOrderNo(order.getOrderNo());
        response.setPayPrice(order.getPayPrice());
        return response;
    }

    /**
     * 用户积分记录——订单抵扣
     *
     * @param uid         用户ID
     * @param useIntegral 使用的积分
     * @param integral    用户当前积分
     * @param orderNo     订单号
     * @return 用户积分记录
     */
    private UserIntegralRecord initOrderUseIntegral(Integer uid, Integer useIntegral, Integer integral, String orderNo) {
        UserIntegralRecord integralRecord = new UserIntegralRecord();
        integralRecord.setUid(uid);
        integralRecord.setLinkId(orderNo);
        integralRecord.setLinkType(IntegralRecordConstants.INTEGRAL_RECORD_LINK_TYPE_ORDER);
        integralRecord.setType(IntegralRecordConstants.INTEGRAL_RECORD_TYPE_SUB);
        integralRecord.setTitle(StrUtil.format("订单使用{}积分进行金额抵扣", useIntegral));
        integralRecord.setIntegral(useIntegral);
        integralRecord.setBalance(integral - useIntegral);
        integralRecord.setStatus(IntegralRecordConstants.INTEGRAL_RECORD_STATUS_COMPLETE);
        return integralRecord;
    }

    /**
     * 积分抵扣计算
     *
     * @param orderInfoVo  订单vo
     * @param userIntegral 用户积分
     */
    @Override
    public void integralDeductionComputed(PreOrderInfoVo orderInfoVo, Integer userIntegral) {
        BigDecimal payPrice = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getCouponFee());
        String integralDeductionSwitch = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        if (integralDeductionSwitch.equals("false")) {
            throw new CrmebException("积分抵扣未开启，请重新下单");
        }
        String integralDeductionStartMoney = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
        if (Integer.parseInt(integralDeductionStartMoney) <= 0 || payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) < 0) {
            throw new CrmebException("支付金额不满足积分抵扣起始金额，请重新下单");
        }
        // 查询积分使用比例
        String integralRatio = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_RATIO);
        // 可抵扣金额
        BigDecimal canDeductionPrice = payPrice.multiply(new BigDecimal(integralRatio)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
        // 积分转换金额
        String integralDeductionMoney = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_MONEY);
        BigDecimal deductionPrice = new BigDecimal(userIntegral.toString()).multiply(new BigDecimal(integralDeductionMoney));
        // 积分兑换金额小于实际支付可抵扣金额
        int useIntegral;
        BigDecimal integralDeductionPrice;
        if (deductionPrice.compareTo(canDeductionPrice) <= 0) {
            useIntegral = userIntegral;
            integralDeductionPrice = deductionPrice;
        } else {
            useIntegral = canDeductionPrice.divide(new BigDecimal(integralDeductionMoney), 0, BigDecimal.ROUND_UP).intValue();
            integralDeductionPrice = canDeductionPrice;
        }
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        Integer tempUseIntegral = useIntegral;
        BigDecimal tempIntegralDeductionPrice = integralDeductionPrice;
        List<PreMerchantOrderVo> voList = merchantOrderVoList.stream().sorted(Comparator.comparing(o -> (o.getProTotalFee().subtract(o.getCouponFee())))).collect(Collectors.toList());
        BigDecimal paySubPrice = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
        for (int i = 0; i < voList.size(); i++) {
            PreMerchantOrderVo merchantOrderVo = voList.get(i);
            BigDecimal merPayPrice = merchantOrderVo.getProTotalFee().subtract(merchantOrderVo.getMerCouponFee());
            if (merchantOrderVoList.size() == (i + 1)) {
                merchantOrderVo.setUseIntegral(tempUseIntegral);
                merchantOrderVo.setIntegralPrice(tempIntegralDeductionPrice);
                Integer merUseIntegral = tempUseIntegral;
                BigDecimal merIntegralDeductionPrice = tempIntegralDeductionPrice;
                List<PreOrderInfoDetailVo> orderInfoList = merchantOrderVo.getOrderInfoList();
                if (orderInfoList.size() == 1) {
                    orderInfoList.get(0).setUseIntegral(merUseIntegral);
                    orderInfoList.get(0).setIntegralPrice(merIntegralDeductionPrice);
                } else {
                    for (int j = 0; j < orderInfoList.size(); j++) {
                        PreOrderInfoDetailVo detailVo = orderInfoList.get(j);
                        if (orderInfoList.size() == (j + 1)) {
                            detailVo.setUseIntegral(merUseIntegral);
                            detailVo.setIntegralPrice(merIntegralDeductionPrice);
                            break;
                        }
                        BigDecimal detailPayPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum())).subtract(detailVo.getMerCouponPrice());
                        BigDecimal ratio = detailPayPrice.divide(merPayPrice, 10, BigDecimal.ROUND_HALF_UP);
                        int detailUseIntegral = new BigDecimal(merUseIntegral.toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_DOWN).intValue();
                        BigDecimal detailIntegralPrice = new BigDecimal(detailUseIntegral).multiply(new BigDecimal(integralDeductionMoney));
                        merUseIntegral = merUseIntegral - detailUseIntegral;
                        merIntegralDeductionPrice = merIntegralDeductionPrice.subtract(detailIntegralPrice);
                        detailVo.setUseIntegral(detailUseIntegral);
                        detailVo.setIntegralPrice(detailIntegralPrice);
                    }
                }
                break;
            }
            BigDecimal payRatio = merPayPrice.divide(paySubPrice, 10, BigDecimal.ROUND_HALF_UP);
            Integer merUseIntegral = new BigDecimal(tempUseIntegral.toString()).multiply(payRatio).setScale(0, BigDecimal.ROUND_DOWN).intValue();
            if (merUseIntegral.equals(0)) {
                continue;
            }
            BigDecimal merIntegralDeductionPrice = new BigDecimal(merUseIntegral).multiply(new BigDecimal(integralDeductionMoney));
            merchantOrderVo.setUseIntegral(merUseIntegral > tempUseIntegral ? tempUseIntegral : merUseIntegral);
            merchantOrderVo.setIntegralPrice(merIntegralDeductionPrice.compareTo(tempIntegralDeductionPrice) > 0 ? tempIntegralDeductionPrice : merIntegralDeductionPrice);
            if (merIntegralDeductionPrice.compareTo(merPayPrice) > 0) {
                merchantOrderVo.setIntegralPrice(merPayPrice);
            }
            List<PreOrderInfoDetailVo> orderInfoList = merchantOrderVo.getOrderInfoList();
            if (orderInfoList.size() == 1) {
                orderInfoList.get(0).setUseIntegral(merUseIntegral);
                orderInfoList.get(0).setIntegralPrice(merIntegralDeductionPrice);
            } else {
                for (int j = 0; j < orderInfoList.size(); j++) {
                    PreOrderInfoDetailVo detailVo = orderInfoList.get(j);
                    if (orderInfoList.size() == (j + 1)) {
                        detailVo.setUseIntegral(merUseIntegral);
                        detailVo.setIntegralPrice(merIntegralDeductionPrice);
                        break;
                    }
                    BigDecimal detailPayPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum())).subtract(detailVo.getMerCouponPrice());
                    BigDecimal ratio = detailPayPrice.divide(merPayPrice, 10, BigDecimal.ROUND_HALF_UP);
                    int detailUseIntegral = new BigDecimal(merUseIntegral.toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_DOWN).intValue();
                    BigDecimal detailIntegralPrice = new BigDecimal(detailUseIntegral).multiply(new BigDecimal(integralDeductionMoney));
                    merUseIntegral = merUseIntegral - detailUseIntegral;
                    merIntegralDeductionPrice = merIntegralDeductionPrice.subtract(detailIntegralPrice);
                    detailVo.setUseIntegral(detailUseIntegral);
                    detailVo.setIntegralPrice(detailIntegralPrice);
                }
            }
            tempUseIntegral = tempUseIntegral - merchantOrderVo.getUseIntegral();
            tempIntegralDeductionPrice = tempIntegralDeductionPrice.subtract(merchantOrderVo.getIntegralPrice());
        }
        orderInfoVo.setMerchantOrderVoList(voList);
    }

    @Override
    public String importInsuranceOrder(BatchInsuranceOrderRequest request) {
        // 校验jwt
        String sourceId = request.getSourceId();
        String verify = request.getVerify();
        String regionCode = request.getRegionCode();
        String sourceIdVerify = TokenUtils.getSourceIdFromVerify(verify);
        if (!sourceId.equals(sourceIdVerify)) {
            throw new CrmebException("校验失败");
        }
        String orderNo = getInsuranceOrderNo(sourceId);
        if (!importSingleInsuranceOrder(sourceId, regionCode, request)) {
            throw new CrmebException("导入失败");
        }
        return orderNo;
    }

    @Override
    public String bindUserByInsuranceOrder(OrderSaveUserRequest request) {
        // 校验jwt
        String sourceId = request.getSourceId();
        String verify = request.getVerify();
        String sourceIdVerify = TokenUtils.getSourceIdFromVerify(verify);
        if (!sourceId.equals(sourceIdVerify)) {
            throw new CrmebException("校验失败");
        }
        String orderNo = getInsuranceOrderNo(sourceId);
        Order order = orderService.getByOrderNo(orderNo);
        if (order == null) {
            throw new CrmebException("订单不存在");
        }
        if (order.getUid() > 0) {
            //如果已绑定用户就返回，不继续处理
            return orderNo;
        }
        User user = userService.getById(Integer.parseInt(request.getUserId()));
        if (user == null) {
            throw new CrmebException("用户不存在");
        }
        order.setUid(user.getId());
        Boolean execute = transactionTemplate.execute(e -> {
            orderService.updateById(order);
            return true;
        });
        if (!execute) {
            throw new CrmebException("订单绑定用户失败");
        }
        return orderNo;
    }

    @Override
    public boolean saveOrderUrl(OrderSaveUrlRequest request) {
        String url = request.getUrl();
        String orderNo = request.getOrderNo();
        Order order = orderService.getByOrderNo(orderNo);
        if (!order.getBizType().equals(BIZ_TYPE_RISK)) {
            throw new CrmebException("不允许设置外部地址");
        }
        List<Order> merOrders = orderService.getByPlatOrderNo(orderNo);
        Boolean success = transactionTemplate.execute(e -> {
            order.setUrl(url);
            order.setRegionCode(request.getRegionCode());
            if (order.getStatus().equals(OrderConstants.ORDER_STATUS_WAIT_SHIPPING)) {
                order.setStatus(OrderConstants.ORDER_STATUS_WAIT_RECEIPT);
            }
            orderService.updateById(order);
            if (CollUtil.isNotEmpty(merOrders)) {
                merOrders.forEach(o -> {
                    o.setUrl(url);
                    o.setRegionCode(request.getRegionCode());
                    if (o.getStatus().equals(OrderConstants.ORDER_STATUS_WAIT_SHIPPING)) {
                        o.setStatus(OrderConstants.ORDER_STATUS_WAIT_RECEIPT);
                    }
                });
                orderService.updateBatchById(merOrders);
            }
            return true;
        });
        if (Boolean.TRUE.equals(success)) {
            List<OrderDetail> orderDetails = orderDetailService.getByOrderNo(order.getOrderNo());
            OrderDetail orderDetail = orderDetails.get(0);
            try {
                User user = userService.getById(order.getUid());
                if (StrUtil.isNotBlank(user.getPhone())) {
                    String content;
                    if (StringUtils.isNotEmpty(url)) {
                        content = "您的【" + orderDetail.getProductName() + "】已发货【" + url + "】，也可以登陆申报网站下载。";
                    } else {
                        content = "您的【" + orderDetail.getProductName() + "】商品订单（" + order.getOrderNo() + "）已完成服务。";
                    }
                    smsService.sendMessage(user.getPhone(), content);
                } else {
                    logger.error("找不到用户手机号，取消发送短信");
                }
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }
        return Boolean.TRUE.equals(success);
    }

    private boolean importSingleInsuranceOrder(String sourceId, String regionCode, BatchInsuranceOrderRequest request) {
        String productCode = request.getProductCode();
        Integer payNum = request.getPayNum();
        BigDecimal payPrice = request.getPayPrice();
        String orderNo = getInsuranceOrderNo(sourceId);
        boolean redisRepetition = redisUtil.getRedisRepetition(orderNo);
        if (!redisRepetition) {
            throw new CrmebException("订单已提交，请勿重复操作");
        }
        Order exists = orderService.findByOrderNo(orderNo);
        if (exists != null) {
            throw new CrmebException("订单已存在，导入失败：" + orderNo);
        }
        List<ProductAttrValue> byBarCodes = productAttrValueService.getByBarCodes(CollUtil.newArrayList(productCode));
        if (byBarCodes.size() != 1) {
            throw new CrmebException("找不到商品条码或商品条码不唯一，无法对应到商品，请梳理保险商品规格中的商品条码字段");
        }
        ProductAttrValue productAttrValue = byBarCodes.get(0);
        Integer productId = productAttrValue.getProductId();
        Product product = productService.getById(productId);
//        User user = userService.getByIdCardNo(request.getIdentityCardNo());
        User user = null;
        Order order = new Order();
        order.setRegionCode(regionCode);
        order.setOrderNo(orderNo);
        order.setMerId(product.getMerId());
        order.setLevel(OrderConstants.ORDER_LEVEL_MERCHANT);
        //order.setPlatOrderNo(order.getOrderNo());
        order.setUid(user != null ? user.getId() : 0);
        order.setTotalNum(payNum);
        order.setProTotalPrice(payPrice);
        order.setTotalPrice(payPrice);
        order.setPayPrice(payPrice);
        order.setPaid(true);
        order.setStatus(OrderConstants.ORDER_STATUS_COMPLETE);
        order.setCancelStatus(OrderConstants.ORDER_CANCEL_STATUS_NORMAL);
        order.setType(OrderConstants.ORDER_TYPE_NORMAL);
        order.setBizType(ProductConstants.BIZ_TYPE_INSURANCE);
        order.setInsuranceType(request.getInsuranceType());
        order.setUserName(user != null ? user.getNickname() : request.getIdentityCardNo());
        order.setMemberName(user != null ? bcxDepartmentMemberService.getMemerNamesByUid(user.getId()) : "");
        order.setProductName(product.getName());
        order.setPayType("bailianbao");//百联保
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderNo(order.getOrderNo());
        orderDetail.setMerId(order.getMerId());
        orderDetail.setUid(0);
        orderDetail.setProductId(productId);
        orderDetail.setProductName(product.getName());
        orderDetail.setImage(product.getImage());
        orderDetail.setAttrValueId(productAttrValue.getId());
        orderDetail.setSku(productAttrValue.getSku());
        orderDetail.setPrice(product.getPrice());
        orderDetail.setPayNum(payNum);
        orderDetail.setWeight(productAttrValue.getWeight());
        orderDetail.setVolume(productAttrValue.getVolume());
        orderDetail.setProductType(ProductConstants.PRODUCT_TYPE_NORMAL);
        orderDetail.setSubBrokerageType(product.getIsSub() ? 1 : 2);
        orderDetail.setBrokerage(productAttrValue.getBrokerage());
        orderDetail.setBrokerageTwo(productAttrValue.getBrokerageTwo());
        orderDetail.setSourceId(sourceId);
        orderDetail.setPayPrice(payPrice);

        MerchantOrder merchantOrder = new MerchantOrder();
        BeanUtils.copyProperties(order, merchantOrder);
        merchantOrder.setId(null);
        merchantOrder.setOrderNo(order.getOrderNo());

        transactionTemplate.execute(e -> {
            orderService.save(order);
            merchantOrderService.save(merchantOrder);
            orderDetailService.save(orderDetail);
            return true;
        });
        // bcxPerformanceReportService.generateReport(order.getOrderNo());
        return true;
    }

    private static String getInsuranceOrderNo(String sourceId) {
        return OrderConstants.ORDER_PREFIX_MERCHANT + "|" + sourceId;
    }

    /**
     * 订单列表
     *
     * @param status      订单状态|-1：全部，0：待支付，1：待发货,2：部分发货， 3：待核销，4：待收货,5：已收货,6：已完成，9：已取消
     * @param pageRequest 分页参数
     * @return PageInfo
     */
    @Override
    public PageInfo<OrderFrontDataResponse> list(Integer status, Integer categoryId, Integer bizType, Integer fapiaoStatus, PageParamRequest pageRequest) {
        Integer userId = userService.getUserIdException();

        PageInfo<Order> pageInfo = orderService.getUserOrderList(userId, status, bizType, categoryId, fapiaoStatus, pageRequest);
        List<Order> orderList = pageInfo.getList();
        if (CollUtil.isEmpty(orderList)) {
            return CommonPage.copyPageInfo(pageInfo, CollUtil.newArrayList());
        }
        List<Integer> merIdList = orderList.stream().map(Order::getMerId).filter(i -> i > 0).distinct().collect(Collectors.toList());
        Map<Integer, Merchant> merchantMap = null;
        if (CollUtil.isNotEmpty(merIdList)) {
            merchantMap = merchantService.getMerIdMapByIdList(merIdList);
        }
        List<OrderFrontDataResponse> responseList = CollUtil.newArrayList();
        for (Order order : orderList) {
            OrderFrontDataResponse infoResponse = new OrderFrontDataResponse();
            BeanUtils.copyProperties(order, infoResponse);
            // 订单详情对象列表
            List<OrderDetail> orderDetailList = orderDetailService.getByOrderNo(order.getOrderNo());
            List<OrderInfoFrontDataResponse> infoResponseList = CollUtil.newArrayList();
            orderDetailList.forEach(e -> {
                OrderInfoFrontDataResponse orderInfoResponse = new OrderInfoFrontDataResponse();
                BeanUtils.copyProperties(e, orderInfoResponse);
                Product product = productService.query().select("category_id").eq("id", e.getProductId()).one();
                if (product != null) {
                    productCategoryService.getCache().parallelStream().filter(c1 -> Objects.equals(product.getCategoryId(), c1.getId())).findAny().ifPresent(c -> {
                        orderInfoResponse.setCategoryName(c.getName());
                        infoResponse.setCategoryName(c.getName());
                    });
                }
                infoResponseList.add(orderInfoResponse);
            });
            infoResponse.setOrderInfoList(infoResponseList);
            if (order.getMerId() > 0) {
                infoResponse.setMerName(merchantMap.get(order.getMerId()).getName());
            }
            responseList.add(infoResponse);
        }
        return CommonPage.copyPageInfo(pageInfo, responseList);
    }

    /**
     * <b> 将虚拟订单伪装成实际订单返回前台展示 </b>
     * @author ZXF
     * @create 2025/08/28 0028 15:44
     * @version
     * @注意事项 </b>
     */
    private OrderFrontDetailResponse frontVirtualDetail(String orderNo) {
        User user = userService.getInfo();
        VirtualOrder virtualOrder = virtualOrderService.getByVirtualOrderNo(orderNo);
        PreOrderInfoVo orderInfoVo = JSONObject.parseObject(virtualOrder.getPreOrderInfoVo(), PreOrderInfoVo.class);
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        List<Integer> productIds = merchantOrderVoList.stream().flatMap(ov -> ov.getOrderInfoList().stream()).map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
        if (productIds.size() != 1) {
            throw new CrmebException("一个订单内只能有一种类型的商品");
        }
        Product product = productService.getById(productIds.get(0));
        Integer bizType = orderService.getBizTypeByProductsType(productIds);// 0-实物 1-虚拟 2-特殊(考培) 3-保险, 4-风评
        // 平台订单
        Order order = new Order();
        order.setCreateTime(new Date());
        order.setRegionCode(user.getRegionCode());
        order.setOrderNo(orderNo);
        order.setMerId(orderInfoVo.getMerId());
        order.setUid(user.getId());
        order.setTotalNum(orderInfoVo.getOrderProNum());
        order.setProTotalPrice(orderInfoVo.getProTotalFee());
        order.setTotalPostage(orderInfoVo.getFreightFee());
        order.setTotalPrice(order.getProTotalPrice().add(order.getTotalPostage()));
        order.setCouponPrice(orderInfoVo.getCouponFee());
        order.setUseIntegral(merchantOrderVoList.stream().mapToInt(PreMerchantOrderVo::getUseIntegral).sum());
        order.setIntegralPrice(merchantOrderVoList.stream().map(PreMerchantOrderVo::getIntegralPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        order.setPayPrice(order.getProTotalPrice().add(order.getTotalPostage()).subtract(order.getCouponPrice()).subtract(order.getIntegralPrice()));
        order.setPayPostage(order.getTotalPostage());
        order.setPaid(false);
        order.setCancelStatus(OrderConstants.ORDER_CANCEL_STATUS_NORMAL);
        order.setLevel(OrderConstants.ORDER_LEVEL_PLATFORM);
        order.setType(orderInfoVo.getType());// 默认普通订单
        order.setMerCouponPrice(orderInfoVo.getMerCouponFee());
        order.setPlatCouponPrice(orderInfoVo.getPlatCouponFee());
        order.setPlatCouponId(orderInfoVo.getPlatUserCouponId());
        order.setBizType(bizType);
        order.setBizSubType(product.getBizSubType());
        order.setUserName(user.getNickname());
        order.setMemberName(bcxDepartmentMemberService.getMemerNamesByUid(user.getId()));
        order.setProductName(product.getName());
        if (!StrUtil.isBlank(merchantOrderVoList.get(0).getOrderInfoList().get(0).getRegionCode())) {
            //风评订单从前端设置区域编码
            order.setRegionCode(merchantOrderVoList.get(0).getOrderInfoList().get(0).getRegionCode());
        }
        if (BIZ_TYPE_RISK.equals(order.getBizType())) {
            order.setType(OrderConstants.ORDER_TYPE_RISK);
        }
        List<MerchantOrder> merchantOrderList = new ArrayList<>();
        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            MerchantOrder merchantOrder = new MerchantOrder();
            merchantOrder.setOrderNo(order.getOrderNo());
            merchantOrder.setMerId(merchantOrderVo.getMerId());
            merchantOrder.setUid(user.getId());
            merchantOrder.setTotalNum(merchantOrderVo.getProTotalNum());
            merchantOrder.setProTotalPrice(merchantOrderVo.getProTotalFee());
            merchantOrder.setTotalPostage(merchantOrderVo.getFreightFee());
            merchantOrder.setTotalPrice(merchantOrder.getProTotalPrice().add(merchantOrder.getTotalPostage()));
            merchantOrder.setPayPostage(merchantOrder.getTotalPostage());
            merchantOrder.setUseIntegral(merchantOrderVo.getUseIntegral());
            merchantOrder.setIntegralPrice(merchantOrderVo.getIntegralPrice());
            merchantOrder.setCouponId(merchantOrderVo.getUserCouponId());
            List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList();
            BigDecimal merCouponPrice = detailVoList.stream().map(PreOrderInfoDetailVo::getMerCouponPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrder.setMerCouponPrice(merCouponPrice);
            BigDecimal platCouponPrice = detailVoList.stream().map(PreOrderInfoDetailVo::getPlatCouponPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrder.setPlatCouponPrice(platCouponPrice);
            merchantOrder.setCouponPrice(merchantOrder.getMerCouponPrice().add(merchantOrder.getPlatCouponPrice()));
            merchantOrder.setPayPrice(merchantOrder.getTotalPrice().subtract(merchantOrder.getCouponPrice()).subtract(merchantOrder.getIntegralPrice()));
            merchantOrder.setGainIntegral(0);
            merchantOrder.setType(order.getType());
            merchantOrderList.add(merchantOrder);

            BigDecimal totalPlatFee = BigDecimal.ZERO;
            BcxPlatformFeeConfig platFeeConfig = bcxPlatformFeeConfigService.queryByAnyId(order.getMerId(), 0, 0);
            for (PreOrderInfoDetailVo detailVo : detailVoList) {
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setOrderNo(order.getOrderNo());
                orderDetail.setMerId(merchantOrder.getMerId());
                orderDetail.setUid(user.getId());
                orderDetail.setProductId(detailVo.getProductId());
                orderDetail.setProductName(detailVo.getProductName());
                orderDetail.setImage(detailVo.getImage());
                orderDetail.setAttrValueId(detailVo.getAttrValueId());
                orderDetail.setSku(detailVo.getSku());
                orderDetail.setPrice(detailVo.getPrice());
                orderDetail.setPayNum(detailVo.getPayNum());
                orderDetail.setWeight(detailVo.getWeight());
                orderDetail.setVolume(detailVo.getVolume());
                orderDetail.setProductType(detailVo.getProductType());
                orderDetail.setSubBrokerageType(detailVo.getSubBrokerageType());
                orderDetail.setBrokerage(detailVo.getBrokerage());
                orderDetail.setBrokerageTwo(detailVo.getBrokerageTwo());
                orderDetail.setFreightFee(detailVo.getFreightFee());

                orderDetail.setUseIntegral(detailVo.getUseIntegral());
                orderDetail.setIntegralPrice(detailVo.getIntegralPrice());
                orderDetail.setSourceId(detailVo.getSourceId());

                platFeeConfig = bcxPlatformFeeConfigService.queryByAnyId(order.getMerId(), orderDetail.getProductId(), 0);
                if (platFeeConfig == null) {
                    throw new CrmebException("平台服务费配置不存在，请联系客服");
                }
                BigDecimal platformFee = bcxPlatformFeeConfigService.calcPlatformFee(platFeeConfig, ProductConstants.SPEC_TYPE_SELL, order.getPayPrice(), orderDetail.getPayNum(), ProductConstants.SPEC_TYPE_SELL);
                orderDetail.setPlatFee(platformFee);
                totalPlatFee = totalPlatFee.add(platformFee);

                orderDetail.setPayPrice(BigDecimal.ZERO);

                orderDetail.setMerCouponPrice(detailVo.getMerCouponPrice());
                orderDetail.setPlatCouponPrice(detailVo.getPlatCouponPrice());
                orderDetail.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                BigDecimal detailPayPrice = orderDetail.getPrice().multiply(new BigDecimal(orderDetail.getPayNum().toString())).add(orderDetail.getFreightFee()).subtract(orderDetail.getCouponPrice()).subtract(orderDetail.getIntegralPrice());
                if (detailPayPrice.compareTo(BigDecimal.ZERO) >= 0) {
                    orderDetail.setPayPrice(detailPayPrice);
                }
                orderDetailList.add(orderDetail);
            }
            order.setPlatFee(totalPlatFee);
        }

        String cancelStr = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_ORDER_CANCEL_TIME);
        if (StrUtil.isBlank(cancelStr) || !NumberUtil.isInteger(cancelStr)) {
            cancelStr = ORDER_EXPIRED_MINUTES;
        }
        OrderFrontDetailResponse response = new OrderFrontDetailResponse();
        response.setCurrentTime(new Date());
        response.setExpireMinute(NumberUtil.parseInt(cancelStr));
        BeanUtils.copyProperties(order, response);
        Map<Integer, List<OrderDetail>> orderDetailMap = orderDetailList.stream().collect(Collectors.groupingBy(OrderDetail::getMerId));
        List<MerchantOrderFrontDetailResponse> merDetailResponseList = CollUtil.newArrayList();
        for (MerchantOrder merchantOrder : merchantOrderList) {
            MerchantOrderFrontDetailResponse merDetailResponse = new MerchantOrderFrontDetailResponse();
            BeanUtils.copyProperties(merchantOrder, merDetailResponse);
            Merchant merchant = merchantService.getById(merchantOrder.getMerId());
            merDetailResponse.setMerName(merchant.getName());
            merDetailResponse.setLastDeliveryTime(merchantOrder.getLastDeliveryTime());
            List<OrderDetail> detailList = orderDetailMap.get(merchantOrder.getMerId());
            List<OrderInfoFrontDataResponse> dataResponseList = detailList.stream().map(d -> {
                OrderInfoFrontDataResponse dataResponse = new OrderInfoFrontDataResponse();
                BeanUtils.copyProperties(d, dataResponse);
                if (product != null) {
                    productCategoryService.getCache().parallelStream().filter(c1 -> Objects.equals(product.getCategoryId(), c1.getId())).findAny().ifPresent(c -> {
                        dataResponse.setCategoryName(c.getName());
                        response.setCategoryName(c.getName());
                    });
                }
                return dataResponse;
            }).collect(Collectors.toList());
            merDetailResponse.setOrderInfoList(dataResponseList);
            merDetailResponseList.add(merDetailResponse);
        }
        response.setMerchantOrderList(merDetailResponseList);
        return response;
    }

    /**
     * 移动端订单详情
     *
     * @param orderNo 订单编号
     * @return OrderFrontDetailResponse
     */
    @Override
    public OrderFrontDetailResponse frontDetail(String orderNo) {
        User currentUser = userService.getInfo();
//        Order order = orderService.getByOrderNo(orderNo);
        Order order;
        try {
            order = orderService.getByOrderNo(orderNo);
        }catch (Exception e) {
            return frontVirtualDetail(orderNo);
        }
        if (order.getIsUserDel() || order.getIsMerchantDel() || !order.getUid().equals(currentUser.getId())) {
            throw new CrmebException("订单不存在");
        }
        String cancelStr = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_ORDER_CANCEL_TIME);
        if (StrUtil.isBlank(cancelStr) || !NumberUtil.isInteger(cancelStr)) {
            cancelStr = ORDER_EXPIRED_MINUTES;
        }
        OrderFrontDetailResponse response = new OrderFrontDetailResponse();
        response.setCurrentTime(new Date());
        response.setExpireMinute(NumberUtil.parseInt(cancelStr));
        BeanUtils.copyProperties(order, response);
        List<MerchantOrder> merchantOrderList = merchantOrderService.getByOrderNo(orderNo);
        List<OrderDetail> orderDetailList = orderDetailService.getByOrderNo(orderNo);
        Map<Integer, List<OrderDetail>> orderDetailMap = orderDetailList.stream().collect(Collectors.groupingBy(OrderDetail::getMerId));
        List<MerchantOrderFrontDetailResponse> merDetailResponseList = CollUtil.newArrayList();
        for (MerchantOrder merchantOrder : merchantOrderList) {
            MerchantOrderFrontDetailResponse merDetailResponse = new MerchantOrderFrontDetailResponse();
            BeanUtils.copyProperties(merchantOrder, merDetailResponse);
            Merchant merchant = merchantService.getById(merchantOrder.getMerId());
            merDetailResponse.setMerName(merchant.getName());
            merDetailResponse.setLastDeliveryTime(merchantOrder.getLastDeliveryTime());
            if (merchantOrder.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                merDetailResponse.setMerPhone(merchant.getPhone());
                merDetailResponse.setMerProvince(merchant.getProvince());
                merDetailResponse.setMerCity(merchant.getCity());
                merDetailResponse.setMerDistrict(merchant.getDistrict());
                merDetailResponse.setMerAddressDetail(merchant.getAddressDetail());
                merDetailResponse.setMerLatitude(merchant.getLatitude());
                merDetailResponse.setMerLongitude(merchant.getLongitude());
            }
            List<OrderDetail> detailList = orderDetailMap.get(merchantOrder.getMerId());
            List<OrderInfoFrontDataResponse> dataResponseList = detailList.stream().map(d -> {
                OrderInfoFrontDataResponse dataResponse = new OrderInfoFrontDataResponse();
                BeanUtils.copyProperties(d, dataResponse);
                Product product = productService.query().select("category_id").eq("id", d.getProductId()).one();
                if (product != null) {
                    productCategoryService.getCache().parallelStream().filter(c1 -> Objects.equals(product.getCategoryId(), c1.getId())).findAny().ifPresent(c -> {
                        dataResponse.setCategoryName(c.getName());
                        response.setCategoryName(c.getName());
                    });
                }
                return dataResponse;
            }).collect(Collectors.toList());
            merDetailResponse.setOrderInfoList(dataResponseList);
            merDetailResponseList.add(merDetailResponse);
        }
        response.setMerchantOrderList(merDetailResponseList);

        BcxOrderFapiao fapiao = bcxOrderFapiaoService.getFapiaoByOrderNo(orderNo);
        if (fapiao != null) {
            response.setFapiaoNo(fapiao.getFapiaoNo());
            response.setApplyNo(fapiao.getApplyNo());
        }
        return response;
    }

    /**
     * 订单商品评论列表
     *
     * @param pageRequest 分页参数
     * @return PageInfo
     */
    @Override
    public PageInfo<InfoReplyResponse> replyList(PageParamRequest pageRequest) {
        Integer userId = userService.getUserIdException();
        PageInfo<OrderDetail> pageInfo = orderDetailService.getReplyList(userId, false, pageRequest);
        List<OrderDetail> orderDetailList = pageInfo.getList();
        if (CollUtil.isEmpty(orderDetailList)) {
            return CommonPage.copyPageInfo(pageInfo, CollUtil.newArrayList());
        }
        List<Integer> merIdList = orderDetailList.stream().map(OrderDetail::getMerId).distinct().collect(Collectors.toList());
        Map<Integer, Merchant> merchantMap = merchantService.getMerIdMapByIdList(merIdList);
        List<InfoReplyResponse> responseList = orderDetailList.stream().map(info -> {
            InfoReplyResponse replyResponse = new InfoReplyResponse();
            BeanUtils.copyProperties(info, replyResponse);
            replyResponse.setMerName(merchantMap.get(info.getMerId()).getName());
            return replyResponse;
        }).collect(Collectors.toList());
        return CommonPage.copyPageInfo(pageInfo, responseList);
    }

    /**
     * 评价订单商品
     *
     * @param request 评价参数
     */
    @Override
    public Boolean replyProduct(OrderProductReplyRequest request) {
        Order order = orderService.getByOrderNo(request.getOrderNo());
        if (!(order.getStatus().equals(OrderConstants.ORDER_STATUS_COMPLETE) || order.getStatus().equals(OrderConstants.ORDER_STATUS_TAKE_DELIVERY))) {
            throw new CrmebException("订单状态异常，无法评价");
        }
        OrderDetail orderDetail = orderDetailService.getById(request.getOrderDetailId());
        if (ObjectUtil.isNull(orderDetail) || !order.getOrderNo().equals(orderDetail.getOrderNo())) {
            throw new CrmebException("订单详情不存在");
        }
        if (!orderDetail.getIsReceipt()) {
            throw new CrmebException("请收货后再评论");
        }
        if (orderDetail.getIsReply()) {
            throw new CrmebException("已评价!");
        }
        orderDetail.setIsReply(true);
        User user = userService.getInfo();
        ProductReply productReply = new ProductReply();
        BeanUtils.copyProperties(request, productReply);
        productReply.setMerId(orderDetail.getMerId());
        productReply.setProductId(orderDetail.getProductId());
        if (order.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            SeckillProduct seckillProduct = seckillProductService.getById(orderDetail.getProductId());
            productReply.setProductId(seckillProduct.getProductId());
        }
        productReply.setAttrValueId(orderDetail.getAttrValueId());
        productReply.setSku(orderDetail.getSku());
        productReply.setUid(user.getId());
        productReply.setNickname(user.getNickname());
        String cdnUrl = systemAttachmentService.getCdnUrl();
        productReply.setAvatar(systemAttachmentService.clearPrefix(user.getAvatar(), cdnUrl));
        if (CollUtil.isNotEmpty(request.getPics())) {
            List<String> pics = request.getPics().stream().map(e -> systemAttachmentService.clearPrefix(e, cdnUrl)).collect(Collectors.toList());
            productReply.setPics(String.join(",", pics));
        }
        Boolean execute = transactionTemplate.execute(e -> {
            orderDetailService.updateById(orderDetail);
            productReplyService.save(productReply);
            return Boolean.TRUE;
        });
        if (!execute) {
            throw new CrmebException("评价订单商品失败");
        }
        return execute;
    }

    /**
     * 取消订单
     *
     * @param orderNo 订单编号
     */
    @Override
    public Boolean cancel(String orderNo) {
        Integer uid = userService.getUserIdException();
        Order order = orderService.getByOrderNo(orderNo);
        if (!order.getUid().equals(uid)) {
            throw new CrmebException("订单不存在");
        }
        if (order.getPaid()) {
            throw new CrmebException("订单已支付，无法取消");
        }
        Boolean cancel = orderService.cancel(orderNo, true);
        if (cancel) {
            redisUtil.lPush(TaskConstants.ORDER_TASK_REDIS_KEY_AFTER_CANCEL_BY_USER, order.getOrderNo());
        }
        return cancel;
    }

    /**
     * 考培批量退款订单
     *
     * @param orders 订单编号
     */
    @Override
    public List<OrdersBatchOpsResponse> refundOrders(OrdersBatchOpsRequest orders) {
        List<OrdersBatchOpsResponse> result = CollUtil.newArrayList();
        for (String orderNo : orders.getOrderNos()) {
            OrdersBatchOpsResponse resp = new OrdersBatchOpsResponse();
            resp.setOrderNo(orderNo);
            try {
                Order order = orderService.getByOrderNo(orderNo);
                List<OrderDetail> orderDetails = orderDetailService.getByOrderNo(orderNo);
                if (ObjectUtil.isNull(order) || CollUtil.isEmpty(orderDetails)) {
                    throw new CrmebException("没有该订单");
                }
                Integer bizType = order.getBizType();
                if (!bizType.equals(ProductConstants.BIZ_TYPE_SPACIAL)) {
                    throw new CrmebException("非特殊商品(考培)，无法批量退款");
                }
                if (orderDetails.size() > 1 || orderDetails.get(0).getPayNum() != 1) {
                    throw new CrmebException("订单数量校验失败，无法批量退款");
                }
                OrderRefundApplyRequest refundRequest = new OrderRefundApplyRequest();
                refundRequest.setOrderNo(orderNo);
                refundRequest.setOrderDetailId(orderDetails.get(0).getId());
                refundRequest.setNum(orderDetails.get(0).getPayNum());
                refundRequest.setExplain("考培订单批量退款");
                Boolean execute = transactionTemplate.execute(e -> {
                    try {
                        RefundOrder refundOrder = refundApplyBatch(refundRequest);
                        if (ObjectUtil.isNotNull(refundOrder) && refundOrderService.dealRefund(refundOrder)) {
                            resp.setSuccess(true);
                            resp.setMessage("申请退款成功");
                        } else {
                            e.setRollbackOnly();
                            resp.setSuccess(false);
                            resp.setMessage("申请退款失败");
                        }
                    } catch (Exception re) {
                        logger.error(re.getMessage());
                        e.setRollbackOnly();
                        resp.setSuccess(false);
                        resp.setMessage(re.getMessage());
                        return false;
                    }
                    return true;
                });
            } catch (Exception e) {
                resp.setSuccess(false);
                resp.setMessage(e.getMessage());
            }
            result.add(resp);
        }
        return result;
    }


    /**
     * 考培批量退款订单(临时用)
     *
     * @param orders 订单编号
     */
    @Override
    public List<OrdersBatchOpsResponse> refundOrdersTest(OrdersBatchOpsRequest orders) {
        List<OrdersBatchOpsResponse> result = CollUtil.newArrayList();
        for (String orderNo : orders.getOrderNos()) {
            OrdersBatchOpsResponse resp = new OrdersBatchOpsResponse();
            resp.setOrderNo(orderNo);
            try {
                Order order = orderService.getByOrderNo(orderNo);
                List<OrderDetail> orderDetails = orderDetailService.getByOrderNo(orderNo);
                if (ObjectUtil.isNull(order) || CollUtil.isEmpty(orderDetails)) {
                    throw new CrmebException("没有该订单");
                }
                Integer bizType = order.getBizType();
                if (!bizType.equals(ProductConstants.BIZ_TYPE_SPACIAL)) {
                    throw new CrmebException("非特殊商品(考培)，无法批量退款");
                }
                if (orderDetails.size() > 1 || orderDetails.get(0).getPayNum() != 1) {
                    throw new CrmebException("订单数量校验失败，无法批量退款");
                }
                OrderRefundApplyRequest refundRequest = new OrderRefundApplyRequest();
                refundRequest.setOrderNo(orderNo);
                refundRequest.setOrderDetailId(orderDetails.get(0).getId());
                refundRequest.setNum(orderDetails.get(0).getPayNum());
                refundRequest.setExplain("考培订单批量退款");
                Boolean execute = transactionTemplate.execute(e -> {
                    try {
                        RefundOrder refundOrder = refundApplyBatchTest(refundRequest);
                        if (ObjectUtil.isNotNull(refundOrder) && refundOrderService.dealRefund(refundOrder)) {
                            resp.setSuccess(true);
                            resp.setMessage("申请退款成功");
                        } else {
                            e.setRollbackOnly();
                            resp.setSuccess(false);
                            resp.setMessage("申请退款失败");
                        }
                    } catch (Exception re) {
                        logger.error(re.getMessage());
                        e.setRollbackOnly();
                        resp.setSuccess(false);
                        resp.setMessage(re.getMessage());
                        return false;
                    }
                    return true;
                });
            } catch (Exception e) {
                resp.setSuccess(false);
                resp.setMessage(e.getMessage());
            }
            result.add(resp);
        }
        return result;
    }

    private RefundOrder refundApplyBatchTest(OrderRefundApplyRequest request) {
        Order order = orderService.getByOrderNo(request.getOrderNo());
        Integer uid = order.getUid();
        return dealRefundApplyTest(request, uid, order);
    }

    private RefundOrder dealRefundApplyTest(OrderRefundApplyRequest request, Integer uid, Order order) {
        OrderDetail orderDetail = verifyIfCanRefund(request, uid, order);
        MerchantOrder merchantOrder = merchantOrderService.getOneByOrderNo(order.getOrderNo());

        RefundOrder refundOrder = createRefundOrder(request, order, merchantOrder);

        RefundOrderInfo refundOrderInfo = createRefundOrderInfo(request, orderDetail, refundOrder);
        // 临时性计算退款金额、积分
        calculateRefundPriceAndIntegral(request, order, orderDetail, refundOrder, refundOrderInfo);

        Boolean execute = transactionTemplate.execute(e -> {
            try {
                orderService.updateById(order);
                orderDetailService.updateById(orderDetail);
                refundOrderService.save(refundOrder);
                refundOrderInfoService.save(refundOrderInfo);
            } catch (Exception re) {
                logger.error("创建退货单异常", re);
                e.setRollbackOnly();
                return false;
            }
            return true;
        });
        if (!execute) throw new CrmebException("申请退款失败");
        return refundOrder;
    }


    /**
     * 订单收货
     *
     * @param orderNo 订单号
     * @return Boolean
     */
    @Override
    public Boolean takeDelivery(String orderNo) {
        User user = userService.getInfo();
        Order order = orderService.getByOrderNo(orderNo);
        Integer merId = order.getMerId();
        Merchant merchant = merchantService.getByIdException(merId);
        if (!order.getUid().equals(user.getId())) {
            throw new CrmebException("订单不存在");
        }
        if (!order.getCancelStatus().equals(OrderConstants.ORDER_CANCEL_STATUS_NORMAL)) {
            throw new CrmebException("订单已取消");
        }
        if (order.getRefundStatus().equals(OrderConstants.ORDER_REFUND_STATUS_ALL)) {
            throw new CrmebException("已退款订单无法收货");
        }
        //考培订单如果出现已完成记录直接返回true不继续往下走
        Integer bizType = order.getBizType();
        if(ProductConstants.BIZ_TYPE_SPACIAL.equals(bizType) && order.getStatus().equals(OrderConstants.ORDER_STATUS_COMPLETE)){
            return Boolean.TRUE;
        }
        if (!order.getStatus().equals(OrderConstants.ORDER_STATUS_WAIT_RECEIPT)) {
            throw new CrmebException("订单状态异常");
        }
        if(PAY_TYPE_OFFLINE.equalsIgnoreCase(order.getPayType())) {
            //线下支付，需要判断是否到款
            BcxPayOffline offline = bcxPayOfflineService.queryByOrderNo(order.getOrderNo());
            if (offline.getStatus() != 3) {
                throw new CrmebException("订单是线下支付，且未到款，不能确认收货");
            }
        }
        Boolean execute = transactionTemplate.execute(e -> {
            orderService.takeDelivery(orderNo);
            orderDetailService.takeDelivery(orderNo);
            return Boolean.TRUE;
        });
        if (Boolean.TRUE.equals(execute)) {
            //后续操作放入redis
            redisUtil.lPush(TaskConstants.ORDER_TASK_REDIS_KEY_AFTER_TAKE_BY_USER, orderNo);
        }
        return Boolean.TRUE.equals(execute);
    }

    /**
     * 考培批量订单收货
     *
     * @param orders 订单号
     */
    @Override
    public List<OrdersBatchOpsResponse> takeSpecialOrders(OrdersBatchOpsRequest orders) {
        List<OrdersBatchOpsResponse> result = CollUtil.newArrayList();
        for (String orderNo : orders.getOrderNos()) {
            OrdersBatchOpsResponse response = transactionTemplate.execute(e -> {
                OrdersBatchOpsResponse resp = new OrdersBatchOpsResponse();
                resp.setOrderNo(orderNo);
                try {
                    if (takeDelivery(orderNo)) {
                        payService.specialOrderUnfreezeAndPay(orderNo);
                        resp.setSuccess(true);
                        resp.setMessage("订单收货成功");
                    } else {
                        e.setRollbackOnly();
                        resp.setSuccess(false);
                        resp.setMessage("订单收货失败");
                    }
                } catch (Exception err) {
                    e.setRollbackOnly();
                    resp.setSuccess(false);
                    resp.setMessage(err.getMessage());
                }
                return resp;
            });
            result.add(response);
        }

        return result;
    }

    /**
     * 删除订单
     *
     * @param orderNo 订单号
     * @return Boolean
     */
    @Override
    public Boolean delete(String orderNo) {
        Integer userId = userService.getUserIdException();
        Order order = orderService.getByOrderNo(orderNo);
        if (!order.getUid().equals(userId)) {
            throw new CrmebException("订单不存在");
        }
        if (!(order.getStatus().equals(OrderConstants.ORDER_STATUS_COMPLETE) || order.getStatus().equals(OrderConstants.ORDER_STATUS_CANCEL))) {
            throw new CrmebException("未完成订单无法删除");
        }
        if (order.getIsUserDel()) {
            throw new CrmebException("订单已删除，请不要进行重复操作");
        }
        order.setIsUserDel(true);
        return transactionTemplate.execute(e -> {
            orderService.updateById(order);
            orderStatusService.createLog(orderNo, OrderStatusConstants.ORDER_STATUS_USER_DELETE, OrderStatusConstants.ORDER_LOG_USER_DELETE);
            return Boolean.TRUE;
        });
    }

    /**
     * 售后申请列表(可申请售后列表)
     *
     * @param pageParamRequest 分页参数
     */
    @Override
    public PageInfo<OrderDetail> getAfterSaleApplyList(String orderNo, PageParamRequest pageParamRequest) {
        Integer uid = userService.getUserIdException();
        PageInfo<OrderDetail> pageInfo = orderDetailService.findAfterSaleApplyList(uid, orderNo, pageParamRequest);
        List<OrderDetail> orderDetailList = pageInfo.getList();
        if (CollUtil.isEmpty(orderDetailList)) {
            return pageInfo;
        }
        List<Integer> merIdList = orderDetailList.stream().map(OrderDetail::getMerId).distinct().collect(Collectors.toList());
        Map<Integer, Merchant> merchantMap = merchantService.getMapByIdList(merIdList);
        orderDetailList.forEach(o -> {
            o.setMerName(merchantMap.get(o.getMerId()).getName());
        });
        return pageInfo;
    }

    /**
     * 查询退款理由
     *
     * @return 退款理由集合
     */
    @Override
    public List<String> getRefundReason() {
        String reasonString = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_STOR_REASON);
        reasonString = CrmebUtil.UnicodeToCN(reasonString);
        reasonString = reasonString.replace("rn", "n");
        return Arrays.asList(reasonString.split("\\n"));
    }

    /**
     * 订单退款申请
     *
     * @param request 申请参数
     * @return Boolean
     */
    @Override
    public RefundOrder refundApply(OrderRefundApplyRequest request) {
        Integer uid = userService.getUserIdException();
        Order order = orderService.getByOrderNo(request.getOrderNo());
        if (order.getBizType().equals(ProductConstants.BIZ_TYPE_SPACIAL) || order.getBizType().equals(ProductConstants.BIZ_TYPE_VIRTUAL)) {
            throw new CrmebException("虚拟商品和特殊商品不可通过该方法退款");
        }
        if (order.getBizType().equals(BIZ_TYPE_RISK)) {
            final RefundOrder[] refundOrder = new RefundOrder[1];
            Boolean execute = transactionTemplate.execute(e -> {
                try {
                    refundOrder[0] = dealRefundApply(request, uid, order);
                    logger.info("=============请求风评系统，取消风评订单：orderNo：" + request.getOrderNo());
                    Map<String, Object> body1 = new HashMap<>();
                    body1.put("activityNo", order.getPlatOrderNo());
                    JSONObject result = restTemplateUtil.post(systemConfigService.getRiskSystemUrl() + "/wechat/cancel", new JSONObject(body1), ERiskUtil.getHeader());
                    logger.info(result.toJSONString());
                    Boolean success = result.getBoolean("success");
                    if (ObjectUtil.isNotNull(refundOrder[0]) && success && refundOrderService.dealRefund(refundOrder[0])) {
                        return true;
                    } else {
                        e.setRollbackOnly();
                        logger.error("风评订单取消失败，风评系统返回取消结果：" + success);
                    }
                } catch (Exception re) {
                    logger.error(re.getMessage());
                    e.setRollbackOnly();
                }
                return false;
            });
            if (!execute) {
                throw new CrmebException("风评报告已出不能退款，如有问题请联系客服");
            }
            return refundOrder[0];
        }
        return dealRefundApply(request, uid, order);
    }

    private OrderDetail verifyIfCanRefund(OrderRefundApplyRequest request, Integer uid, Order order) {
        if ((!order.getUid().equals(uid) && !order.getBizType().equals(ProductConstants.BIZ_TYPE_INSURANCE)) || order.getIsUserDel()) {//保险订单去掉uid校验
            throw new CrmebException("订单不存在");
        }
        if (order.getIsUserDel()) {
            throw new CrmebException("订单不存在");
        }
        if (order.getCancelStatus() > OrderConstants.ORDER_CANCEL_STATUS_NORMAL) {
            throw new CrmebException("订单已取消");
        }
        if (!order.getPaid()) {
            throw new CrmebException("未支付订单无法申请退款");
        }
        if (order.getRefundStatus().equals(OrderConstants.ORDER_REFUND_STATUS_ALL)) {
            throw new CrmebException("订单已全部退款");
        }
        //保险订单在完成状态下也能申请，但是没有实际退款，只退分销佣金
        if (order.getStatus().equals(OrderConstants.ORDER_STATUS_COMPLETE) && !order.getBizType().equals(ProductConstants.BIZ_TYPE_INSURANCE)) {
            throw new CrmebException("已完成订单无法申请退款");
        }
        OrderDetail orderDetail = orderDetailService.getById(request.getOrderDetailId());
        if (ObjectUtil.isNull(orderDetail) || !orderDetail.getOrderNo().equals(order.getOrderNo())) {
            throw new CrmebException("订单详情不存在");
        }
        int canApplyNum = orderDetail.getPayNum() - orderDetail.getApplyRefundNum() - orderDetail.getRefundNum();
        if (canApplyNum < request.getNum()) {
            throw new CrmebException(StrUtil.format("剩余可退款数量为{}", canApplyNum));
        }
        return orderDetail;
    }


    private RefundOrder createRefundOrder(OrderRefundApplyRequest request, Order order, MerchantOrder merchantOrder) {
        RefundOrder refundOrder = new RefundOrder();
        refundOrder.setRefundOrderNo(CrmebUtil.getOrderNo(OrderConstants.ORDER_PREFIX_REFUND));
        refundOrder.setOrderNo(order.getOrderNo());
        refundOrder.setMerId(order.getMerId());
        refundOrder.setUid(order.getUid());
        refundOrder.setRealName(merchantOrder.getRealName());
        refundOrder.setUserPhone(merchantOrder.getUserPhone());
        refundOrder.setUserAddress(merchantOrder.getUserAddress());
        refundOrder.setRefundPrice(order.getPayPrice());
        refundOrder.setTotalNum(order.getTotalNum());
        refundOrder.setRefundReasonWap(request.getText());
        refundOrder.setRefundReasonWapImg(systemAttachmentService.clearPrefix(request.getReasonImage()));
        refundOrder.setRefundReasonWapExplain(request.getExplain());
        refundOrder.setRefundStatus(OrderConstants.MERCHANT_REFUND_ORDER_STATUS_APPLY);
        refundOrder.setRefundPlatCouponPrice(merchantOrder.getPlatCouponPrice());
        return refundOrder;
    }

    private static RefundOrderInfo createRefundOrderInfo(OrderRefundApplyRequest request, OrderDetail
            orderDetail, RefundOrder refundOrder) {
        RefundOrderInfo refundOrderInfo = new RefundOrderInfo();
        refundOrderInfo.setRefundOrderNo(refundOrder.getRefundOrderNo());
        refundOrderInfo.setMerId(orderDetail.getMerId());
        refundOrderInfo.setOrderDetailId(orderDetail.getId());
        refundOrderInfo.setProductId(orderDetail.getProductId());
        refundOrderInfo.setProductName(orderDetail.getProductName());
        refundOrderInfo.setImage(orderDetail.getImage());
        refundOrderInfo.setAttrValueId(orderDetail.getAttrValueId());
        refundOrderInfo.setSku(orderDetail.getSku());
        refundOrderInfo.setPrice(orderDetail.getPrice());
        refundOrderInfo.setPayNum(orderDetail.getPayNum());
        refundOrderInfo.setProductType(orderDetail.getProductType());
        refundOrderInfo.setPayPrice(orderDetail.getPayPrice());
        refundOrderInfo.setApplyRefundNum(request.getNum());
        return refundOrderInfo;
    }

    private static void calculateRefundPriceAndIntegral(OrderRefundApplyRequest request, Order order, OrderDetail
            orderDetail, RefundOrder refundOrder, RefundOrderInfo refundOrderInfo) {
        if (request.getNum().equals(orderDetail.getPayNum())) {
            refundOrderInfo.setRefundPrice(orderDetail.getPayPrice());
            refundOrderInfo.setRefundUseIntegral(orderDetail.getUseIntegral());
            refundOrderInfo.setRefundGainIntegral(orderDetail.getGainIntegral());
            refundOrderInfo.setRefundFreightFee(orderDetail.getFreightFee());
        } else {
            refundOrderInfo.setRefundUseIntegral(0);
            refundOrderInfo.setRefundGainIntegral(0);
            BigDecimal ratio = new BigDecimal(request.getNum().toString()).divide(new BigDecimal(orderDetail.getPayNum().toString()), 10, BigDecimal.ROUND_HALF_UP);
            refundOrderInfo.setRefundPrice(orderDetail.getPayPrice().multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP));
            if (orderDetail.getUseIntegral() > 0) {
                refundOrderInfo.setRefundUseIntegral(new BigDecimal(orderDetail.getUseIntegral().toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
            }
            if (orderDetail.getGainIntegral() > 0) {
                refundOrderInfo.setRefundGainIntegral(new BigDecimal(orderDetail.getGainIntegral().toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
            }
            if (orderDetail.getPlatCouponPrice().compareTo(BigDecimal.ZERO) > 0) {
                refundOrderInfo.setRefundPlatCouponPrice(orderDetail.getPlatCouponPrice().multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP));
            }
            refundOrderInfo.setRefundFreightFee(orderDetail.getFreightFee().multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP));
        }
        if (order.getBizType().equals(ProductConstants.BIZ_TYPE_INSURANCE)) {
            refundOrderInfo.setRefundPrice(request.getRefundPrice());
        }
        refundOrder.setRefundPrice(refundOrderInfo.getRefundPrice());
        refundOrder.setRefundUseIntegral(refundOrderInfo.getRefundUseIntegral());
        refundOrder.setRefundGainIntegral(refundOrderInfo.getRefundGainIntegral());
        refundOrder.setRefundPlatCouponPrice(refundOrderInfo.getRefundPlatCouponPrice());
        refundOrder.setRefundFreightFee(refundOrderInfo.getRefundFreightFee());

        order.setRefundStatus(OrderConstants.ORDER_REFUND_STATUS_APPLY);
        orderDetail.setApplyRefundNum(orderDetail.getApplyRefundNum() + request.getNum());
    }

    private RefundOrder dealRefundApply(OrderRefundApplyRequest request, Integer uid, Order order) {
        OrderDetail orderDetail = verifyIfCanRefund(request, uid, order);
        MerchantOrder merchantOrder = merchantOrderService.getOneByOrderNo(order.getOrderNo());

        RefundOrder refundOrder = createRefundOrder(request, order, merchantOrder);

        RefundOrderInfo refundOrderInfo = createRefundOrderInfo(request, orderDetail, refundOrder);
        // 临时性计算退款金额、积分
        calculateRefundPriceAndIntegral(request, order, orderDetail, refundOrder, refundOrderInfo);

        Boolean execute = transactionTemplate.execute(e -> {
            try {
                orderService.updateById(order);
                orderDetailService.updateById(orderDetail);
                refundOrderService.save(refundOrder);
                refundOrderInfoService.save(refundOrderInfo);
            } catch (Exception re) {
                logger.error("创建退货单异常", re);
                e.setRollbackOnly();
                return false;
            }
            return true;
        });
        if (!execute) throw new CrmebException("申请退款失败");
        return refundOrder;
    }

    private RefundOrder refundApplyBatch(OrderRefundApplyRequest request) {
        Order order = orderService.getByOrderNo(request.getOrderNo());
        Integer uid = order.getBizType().equals(ProductConstants.BIZ_TYPE_INSURANCE) ? 0 : userService.getUserIdException();
        return dealRefundApply(request, uid, order);
    }

    /**
     * 退款订单列表
     *
     * @param type        列表类型：0-处理中，9-申请记录
     * @param pageRequest 分页参数
     * @return PageInfo
     */
    @Override
    public PageInfo<RefundOrderResponse> getRefundOrderList(Integer type, PageParamRequest pageRequest) {
        PageInfo<RefundOrder> pageInfo = refundOrderService.getH5List(type, pageRequest);
        List<RefundOrder> refundOrderList = pageInfo.getList();
        if (CollUtil.isEmpty(refundOrderList)) {
            return CommonPage.copyPageInfo(pageInfo, CollUtil.newArrayList());
        }
        List<RefundOrderResponse> responseList = refundOrderList.stream().map(refundOrder -> {
            RefundOrderResponse response = new RefundOrderResponse();
            BeanUtils.copyProperties(refundOrder, response);
            RefundOrderInfo orderInfo = refundOrderInfoService.getByRefundOrderNo(refundOrder.getRefundOrderNo());
            response.setProductName(orderInfo.getProductName());
            response.setImage(orderInfo.getImage());
            response.setSku(orderInfo.getSku());
            response.setApplyRefundNum(orderInfo.getApplyRefundNum());
            return response;
        }).collect(Collectors.toList());
        return CommonPage.copyPageInfo(pageInfo, responseList);
    }

    /**
     * 退款订单详情
     *
     * @param refundOrderNo 退款订单号
     * @return RefundOrderInfoResponse
     */
    @Override
    public RefundOrderInfoResponse refundOrderDetail(String refundOrderNo) {
        return refundOrderService.getRefundOrderDetailByRefundOrderNo(refundOrderNo);
    }

    /**
     * 订单物流详情
     */
    @Override
    public LogisticsResultVo getLogisticsInfo(Integer invoiceId) {
        return orderService.getLogisticsInfo(invoiceId);
    }

    /**
     * 获取发货单列表
     *
     * @param orderNo 订单号
     * @return 发货单列表
     */
    @Override
    public OrderInvoiceFrontResponse getInvoiceList(String orderNo) {
        Order order = orderService.getByOrderNo(orderNo);
        List<OrderInvoiceResponse> invoiceList = orderService.getInvoiceList(orderNo);
        OrderInvoiceFrontResponse response = new OrderInvoiceFrontResponse();
        response.setInvoiceList(invoiceList);
        if (CollUtil.isEmpty(invoiceList)) {
            response.setNum(1);
            response.setDeliveryNum(0);
            return response;
        }
        response.setNum(order.getStatus().equals(OrderConstants.ORDER_STATUS_PART_SHIPPING) ? invoiceList.size() + 1 : invoiceList.size());
        response.setDeliveryNum(invoiceList.size());
        return response;
    }

    /**
     * 获取个人中心订单数量
     */
    @Override
    public OrderCenterNumResponse userCenterNum() {
        Integer userId = userService.getUserIdException();
        OrderCenterNumResponse response = new OrderCenterNumResponse();
        response.setAllCount(orderService.getCountByStatusAndUid(-1, userId));
        response.setAwaitPayCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_WAIT_PAY, userId));
        response.setAwaitShippedCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_WAIT_SHIPPING, userId));
        response.setReceiptCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_WAIT_RECEIPT, userId));
        response.setVerificationCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_AWAIT_VERIFICATION, userId));
        response.setCompleteCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_COMPLETE, userId));
        response.setCancelCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_CANCEL, userId));
        response.setAwaitReplyCount(orderDetailService.getAwaitReplyCount(userId));
        response.setRefundCount(refundOrderService.getRefundingCount(userId));
        return response;
    }

    /**
     * 获取订单状态图
     */
    @Override
    public List<HashMap<String, Object>> getOrderStatusImage() {
        List<HashMap<String, Object>> mapList = systemGroupDataService.getListMapByGid(GroupDataConstants.GROUP_DATA_ID_ORDER_STATUS_IMAGE);
        return mapList;
    }

    private List<MyRecord> validateProductStock(PreOrderInfoVo orderInfoVo) {
        List<MyRecord> recordList = CollUtil.newArrayList();
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {// 秒杀订单
            recordList.add(seckillService.validateCreateOrderProductStock(orderInfoVo));
            return recordList;
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {// 视频号订单
            // 查询商品信息 视频号都是单品下单
            List<PreOrderInfoDetailVo> detailVos = orderInfoVo.getMerchantOrderVoList().get(0).getOrderInfoList();
            PayComponentProduct product = payComponentProductService.getById(detailVos.get(0).getProductId());
            if (ObjectUtil.isNull(product)) {
                throw new CrmebException("商品信息不存在，请刷新后重新选择");
            }
            if (product.getIsDel()) {
                throw new CrmebException("商品已删除，请刷新后重新选择");
            }
            if (!product.getStatus().equals(5)) {
                throw new CrmebException("商品已下架，请刷新后重新选择");
            }
            if (product.getStock().equals(0) || product.getStock() < detailVos.get(0).getPayNum()) {
                throw new CrmebException("商品库存不足，请刷新后重新选择");
            }
            // 查询商品规格属性值信息
            ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(detailVos.get(0).getAttrValueId(), detailVos.get(0).getProductId(), ProductConstants.PRODUCT_TYPE_COMPONENT);
            if (ObjectUtil.isNull(attrValue)) {
                throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
            }
            if (attrValue.getStock().equals(0) || attrValue.getStock() < detailVos.get(0).getPayNum()) {
                throw new CrmebException("商品规格库存不足，请刷新后重新选择");
            }
            // 查询视频商品规格属性值
            PayComponentProductSku productSku = payComponentProductSkuService.getByProIdAndAttrValueId(product.getId(), attrValue.getId());
            if (ObjectUtil.isNull(productSku)) {
                throw new CrmebException("商品sku信息不存在，请刷新后重新选择");
            }
            if (productSku.getStockNum().equals(0) || productSku.getStockNum() < detailVos.get(0).getPayNum()) {
                throw new CrmebException("商品sku库存不足，请刷新后重新选择");
            }

            MyRecord record = new MyRecord();
            record.set("productId", product.getId());
            record.set("attrValueId", detailVos.get(0).getAttrValueId());
            record.set("attrValueVersion", attrValue.getVersion());
            record.set("num", detailVos.get(0).getPayNum());
            record.set("skuId", productSku.getId());
            record.set("skuVersion", productSku.getVersion());
            recordList.add(record);
            return recordList;
        }
        // 普通商品
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        merchantOrderVoList.forEach(merchantOrderVo -> {
            Merchant merchant = merchantService.getByIdException(merchantOrderVo.getMerId());
            if (!merchant.getIsSwitch()) {
                throw new CrmebException("商户已关闭，请重新下单");
            }
            merchantOrderVo.getOrderInfoList().forEach(info -> {
                // 查询商品信息
                Product product = productService.getById(info.getProductId());
                if (ObjectUtil.isNull(product) || product.getIsDel()) {
                    throw new CrmebException("购买的商品信息不存在");
                }
                if (!product.getIsShow()) {
                    throw new CrmebException("购买的商品已下架");
                }
                if (product.getStock().equals(0) || info.getPayNum() > product.getStock()) {
                    throw new CrmebException("购买的商品库存不足");
                }
                // 查询商品规格属性值信息
                ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(info.getAttrValueId(), info.getProductId(), ProductConstants.PRODUCT_TYPE_NORMAL);
                if (ObjectUtil.isNull(attrValue)) {
                    throw new CrmebException("购买的商品规格信息不存在");
                }
                if (attrValue.getStock() < info.getPayNum()) {
                    throw new CrmebException("购买的商品库存不足");
                }
                MyRecord record = new MyRecord();
                record.set("productId", info.getProductId());
                record.set("num", info.getPayNum());
                record.set("attrValueId", info.getAttrValueId());
                record.set("attrValueVersion", attrValue.getVersion());
                recordList.add(record);
            });
        });
        return recordList;
    }

    /**
     * 计算订单运费
     */
    @Override
    public void getFreightFee(PreOrderInfoVo orderInfoVo, UserAddress userAddress) {
        BigDecimal freightFee = BigDecimal.ZERO;

        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            BigDecimal storePostage = BigDecimal.ZERO;
            if (merchantOrderVo.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                merchantOrderVo.setFreightFee(storePostage);
                continue;
            }
            if (ObjectUtil.isNull(userAddress) || userAddress.getCityId() <= 0) {
                merchantOrderVo.setFreightFee(storePostage);
                continue;
            }
            // 运费根据商品计算
            Map<Integer, MyRecord> proMap = CollUtil.newHashMap();
            merchantOrderVo.getOrderInfoList().forEach(e -> {
                Integer proId = e.getProductId();
                if (proMap.containsKey(proId)) {
                    MyRecord record = proMap.get(proId);
                    record.set("totalPrice", record.getBigDecimal("totalPrice").add(e.getPrice().multiply(BigDecimal.valueOf(e.getPayNum()))));
                    record.set("totalNum", record.getInt("totalNum") + e.getPayNum());
                    BigDecimal weight = e.getWeight().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("weight", record.getBigDecimal("weight").add(weight));
                    BigDecimal volume = e.getVolume().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("volume", record.getBigDecimal("volume").add(volume));
                } else {
                    MyRecord record = new MyRecord();
                    record.set("totalPrice", e.getPrice().multiply(BigDecimal.valueOf(e.getPayNum())));
                    record.set("totalNum", e.getPayNum());
                    record.set("tempId", e.getTempId());
                    record.set("proId", proId);
                    BigDecimal weight = e.getWeight().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("weight", weight);
                    BigDecimal volume = e.getVolume().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("volume", volume);
                    proMap.put(proId, record);
                }
            });

            // 指定包邮（单品运费模板）> 指定区域配送（单品运费模板）
            int districtId = userAddress.getDistrictId();
            for (Map.Entry<Integer, MyRecord> m : proMap.entrySet()) {
                MyRecord record = m.getValue();
                Integer tempId = record.getInt("tempId");
                ShippingTemplates shippingTemplate = shippingTemplatesService.getById(tempId);
                if (ObjectUtil.isNull(shippingTemplate) || shippingTemplate.getAppoint().equals(ShippingTemplatesConstants.APPOINT_TYPE_ALL)) {
                    continue;
                }
                if (shippingTemplate.getAppoint().equals(ShippingTemplatesConstants.APPOINT_TYPE_DEFINED)) {
                    ShippingTemplatesFree shippingTemplatesFree = shippingTemplatesFreeService.getByTempIdAndCityId(tempId, districtId);
                    if (ObjectUtil.isNotNull(shippingTemplatesFree)) {
                        BigDecimal totalPrice = record.getBigDecimal("totalPrice");
                        if (totalPrice.compareTo(shippingTemplatesFree.getPrice()) >= 0) {
                            continue;
                        }
                        if (shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_NUMBER)) {
                            if (BigDecimal.valueOf(record.getInt("totalNum")).compareTo(shippingTemplatesFree.getNumber()) >= 0) {
                                continue;
                            }
                        }
                        BigDecimal surplus = shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT)
                                ? record.getBigDecimal("weight") : record.getBigDecimal("volume");
                        if (surplus.compareTo(shippingTemplatesFree.getNumber()) >= 0) {
                            continue;
                        }
                    }
                }
                ShippingTemplatesRegion shippingTemplatesRegion = shippingTemplatesRegionService.getByTempIdAndCityId(tempId, districtId);
                if (ObjectUtil.isNull(shippingTemplatesRegion)) {
                    shippingTemplatesRegion = shippingTemplatesRegionService.getByTempIdAndCityId(tempId, 0);
                }
                if (shippingTemplate.getAppoint().equals(ShippingTemplatesConstants.APPOINT_TYPE_PART)) {
                    if (ObjectUtil.isNull(shippingTemplatesRegion)) {
                        continue;
                    }
                }
                BigDecimal postageFee = BigDecimal.ZERO;
                // 判断计费方式：件数、重量、体积
                switch (shippingTemplate.getType()) {
                    case ShippingTemplatesConstants.CHARGE_MODE_TYPE_NUMBER: // 件数
                        // 判断件数是否超过首件
                        Integer num = record.getInt("totalNum");
                        if (num <= shippingTemplatesRegion.getFirst().intValue()) {
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice());
                            postageFee = shippingTemplatesRegion.getFirstPrice();
                        } else {// 超过首件的需要计算续件
                            int renewalNum = num - shippingTemplatesRegion.getFirst().intValue();
                            // 剩余件数/续件 = 需要计算的续件费用的次数
                            BigDecimal divide = BigDecimal.valueOf(renewalNum).divide(shippingTemplatesRegion.getRenewal(), 0, BigDecimal.ROUND_UP);
                            BigDecimal renewalPrice = shippingTemplatesRegion.getRenewalPrice().multiply(divide);
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice()).add(renewalPrice);
                            postageFee = shippingTemplatesRegion.getFirstPrice().add(renewalPrice);
                        }
                        List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(e -> e.getProductId().equals(record.getInt("proId"))).collect(Collectors.toList());
                        if (detailVoList.size() == 1) {
                            detailVoList.get(0).setFreightFee(postageFee);
                        } else {
                            for (int i = 0; i < detailVoList.size(); i++) {
                                PreOrderInfoDetailVo detail = detailVoList.get(i);
                                if (detailVoList.size() == (i + 1)) {
                                    detail.setFreightFee(postageFee);
                                    break;
                                }
                                BigDecimal ratio = new BigDecimal(detail.getPayNum().toString()).divide(new BigDecimal(num.toString()), 10, BigDecimal.ROUND_HALF_UP);
                                BigDecimal multiply = postageFee.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                                detail.setFreightFee(multiply);
                                postageFee = postageFee.subtract(multiply);
                            }
                        }
                        break;
                    case ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT: // 重量
                    case ShippingTemplatesConstants.CHARGE_MODE_TYPE_VOLIME: // 体积
                        BigDecimal surplus = shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT) ? record.getBigDecimal("weight") : record.getBigDecimal("volume");
                        if (surplus.compareTo(shippingTemplatesRegion.getFirst()) <= 0) {
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice());
                            postageFee = shippingTemplatesRegion.getFirstPrice();
                        } else {// 超过首件的需要计算续件
                            BigDecimal renewalNum = surplus.subtract(shippingTemplatesRegion.getFirst());
                            // 剩余件数/续件 = 需要计算的续件费用的次数
                            BigDecimal divide = renewalNum.divide(shippingTemplatesRegion.getRenewal(), 0, BigDecimal.ROUND_UP);
                            BigDecimal renewalPrice = shippingTemplatesRegion.getRenewalPrice().multiply(divide);
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice()).add(renewalPrice);
                            postageFee = shippingTemplatesRegion.getFirstPrice().add(renewalPrice);
                        }
                        List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList().stream().filter(e -> e.getProductId().equals(record.getInt("proId"))).collect(Collectors.toList());
                        if (infoDetailVoList.size() == 1) {
                            infoDetailVoList.get(0).setFreightFee(postageFee);
                        } else {
                            for (int i = 0; i < infoDetailVoList.size(); i++) {
                                PreOrderInfoDetailVo detail = infoDetailVoList.get(i);
                                if (infoDetailVoList.size() == (i + 1)) {
                                    detail.setFreightFee(postageFee);
                                    break;
                                }
                                BigDecimal wv = shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT) ? detail.getWeight() : detail.getVolume();
                                BigDecimal ratio = wv.multiply(new BigDecimal(detail.getPayNum().toString())).divide(surplus, 10, BigDecimal.ROUND_HALF_UP);
                                BigDecimal multiply = postageFee.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                                detail.setFreightFee(multiply);
                                postageFee = postageFee.subtract(multiply);
                            }
                        }
                        break;
                }
            }
            merchantOrderVo.setFreightFee(storePostage);
            freightFee = freightFee.add(storePostage);
        }
        orderInfoVo.setFreightFee(freightFee);
    }

    /**
     * 校验预下单商品信息
     *
     * @param request 预下单请求参数
     * @return OrderInfoVo
     */
    private PreOrderInfoVo validatePreOrderRequest(PreOrderRequest request, User user) {
        logger.info("预下单检查调用 -》validatePreOrderRequest->:request:{}|user:{}", JSON.toJSONString(request), JSON.toJSONString(user));
        PreOrderInfoVo preOrderInfoVo = new PreOrderInfoVo();
        List<PreMerchantOrderVo> merchantOrderVoList = CollUtil.newArrayList();
        if (request.getPreOrderType().equals("shoppingCart")) {// 购物车购买
            logger.info("预下单检查调用:购物车购买");
            if (request.getOrderDetails().size() > 1) {
                throw new CrmebException("暂不支持多类商品一起下单");
            }
            merchantOrderVoList = validatePreOrderShopping(request, user);
            List<Integer> cartIdList = request.getOrderDetails().stream().map(PreOrderDetailRequest::getShoppingCartId).distinct().collect(Collectors.toList());
            preOrderInfoVo.setCartIdList(cartIdList);
        }
        if (request.getPreOrderType().equals("buyNow")) {// 立即购买
            logger.info("预下单检查调用:立即购买");
            // 立即购买只会有一条详情
            if (request.getOrderDetails().size() > 1) {
                throw new CrmebException("下单类型出错，商品明细多于一条");
            }
            PreOrderDetailRequest detailRequest = request.getOrderDetails().get(0);
            merchantOrderVoList.add(validatePreOrderNormal(detailRequest));
            preOrderInfoVo.setRelateOrderNo(detailRequest.getRelateOrderNo());
        }
        if (request.getPreOrderType().equals("video")) {
            logger.info("预下单检查调用:视频号下单");
            // 视频号暂时只能购买一个商品
            if (request.getOrderDetails().size() > 1) {
                throw new CrmebException("下单类型出错，商品明细多于一条");
            }
            PreOrderDetailRequest detailRequest = request.getOrderDetails().get(0);
            merchantOrderVoList.add(validatePreOrderVideo(detailRequest));
            preOrderInfoVo.setType(OrderConstants.ORDER_TYPE_VIDEO);
        }
        if (request.getPreOrderType().equals("seckill")) {
            logger.info("预下单检查调用:秒杀下单");
            // 秒杀只支持单商品支付
            if (request.getOrderDetails().size() > 1) {
                throw new CrmebException("下单类型出错，商品明细多于一条");
            }
            PreOrderDetailRequest detailRequest = request.getOrderDetails().get(0);
            merchantOrderVoList.add(seckillService.validatePreOrderSeckill(detailRequest));
            preOrderInfoVo.setType(OrderConstants.ORDER_TYPE_SECKILL);
        }
        preOrderInfoVo.setMerchantOrderVoList(merchantOrderVoList);
        preOrderInfoVo.setMerId(merchantOrderVoList.get(0).getMerId());
        logger.info("预下单检查调用:结果preOrderInfoVo:{}", JSON.toJSONString(preOrderInfoVo));
        return preOrderInfoVo;
    }

    /**
     * 普通商品下单校验
     *
     * @param detailRequest 商品参数
     */
    private PreMerchantOrderVo validatePreOrderNormal(PreOrderDetailRequest detailRequest) {
        Integer uid = userService.getUserId();
        // 普通商品
        if (ObjectUtil.isNull(detailRequest.getProductId())) {
            throw new CrmebException("商品编号不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getAttrValueId())) {
            throw new CrmebException("商品规格属性值不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getProductNum()) || detailRequest.getProductNum() <= 0) {
            throw new CrmebException("购买数量必须大于0");
        }
        // 查询商品信息
        Product product = productService.getById(detailRequest.getProductId());
        if (ObjectUtil.isNull(product) || product.getIsDel()) {
            throw new CrmebException("商品信息不存在，请刷新后重新选择");
        }
        if (!product.getIsShow()) {
            throw new CrmebException("商品已下架，请刷新后重新选择");
        }
        if (product.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品库存不足，请刷新后重新选择");
        }
        // 限购
        BcxProductQuota quota = bcxProductQuotaService.queryByProductId(detailRequest.getProductId());
        if (quota != null) {
            if (quota.getOneQuota() > 0 && quota.getOneQuota() < detailRequest.getProductNum()) {
                throw new CrmebException(StrUtil.format("此商品单次最多可购买{}件", quota.getOneQuota()));
            }
            if (quota.getAllQuota() > 0) {
                Integer productNumCount = orderService.getProductNumCount(uid, detailRequest.getProductId(), ProductConstants.PRODUCT_TYPE_NORMAL);
                if ((quota.getAllQuota() - productNumCount) < detailRequest.getProductNum()) {
                    throw new CrmebException(StrUtil.format("此商品您还可买{}件", quota.getAllQuota() - productNumCount));
                }
            }
        }
        // 查询商品规格属性值信息
        ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(detailRequest.getAttrValueId(), detailRequest.getProductId(), ProductConstants.PRODUCT_TYPE_NORMAL);
        if (ObjectUtil.isNull(attrValue)) {
            throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
        }
        if (attrValue.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品规格库存不足，请刷新后重新选择");
        }
        Merchant merchant = merchantService.getByIdException(product.getMerId());
        if (!merchant.getIsSwitch()) {
            throw new CrmebException("商户已关闭，请重新选择商品");
        }
        //考培类 特殊商品 不可更改 商品属性和数量
        if (product.getBizType().equals(ProductConstants.BIZ_TYPE_SPACIAL)) {
            if (StrUtil.isBlank(detailRequest.getSourceId()) || StrUtil.isBlank(detailRequest.getVerify())) {
                throw new CrmebException("特殊商品必须有sourceId和verify");
            }
            String s = detailRequest.getProductNum().toString() + "BCX_ORDER_VERIFY" + detailRequest.getAttrValueId().toString();
            if (!detailRequest.getVerify().equals(MD5Util.string2MD5(s))) {
                throw new CrmebException("源头单据校验失败：商品数量和规格不可修改");
            }
        }
        //风评类订单
        if (product.getBizType().equals(ProductConstants.BIZ_TYPE_RISK)) {
            if (StrUtil.isBlank(detailRequest.getRegionCode())) {
                throw new CrmebException("活动区域信息必填/出错，请联系管理员");
            }
            if (attrValue.getBarCode().toUpperCase().startsWith(PRODUCT_E_RISK_REPORT)) {
                // 风评商品，获取风评系统报价价格
                if (StrUtil.isBlank(detailRequest.getSourceId())) {
                    throw new CrmebException("风评商品必须从风评报告入口购买");
//                    throw new CrmebException("风评商品必须从风评报告入口购买（sourceId：即报价ID）");
                }
                if (StrUtil.isBlank(detailRequest.getRegionCode())) {
                    throw new CrmebException("风评报告订单必须有区域信息");
                }
                logger.info("=============请求风评系统，获取报价价格 costId：" + detailRequest.getSourceId());
                JSONObject result = restTemplateUtil.post(systemConfigService.getRiskSystemUrl() + "/wechat/cost/" + detailRequest.getSourceId(), ERiskUtil.getHeader());
                logger.info(result.toJSONString());
                if (result.getJSONObject("data") == null || result.getJSONObject("data").getBigDecimal("amount") == null) {
                    throw new CrmebException("找不到原始报价信息");
                }
                Integer skuId = result.getJSONObject("data").getInteger("skuId");
                if (!attrValue.getId().equals(skuId)) {
                    logger.error("=============原始报价信息有误，请联系管理员：风评skuId：" + skuId + " 商城skuId" + attrValue.getId());
                    throw new CrmebException("原始报价信息有误，请联系管理员");
                }
                BigDecimal amount = result.getJSONObject("data").getBigDecimal("amount");
                attrValue.setPrice(amount);
            }
            if (attrValue.getBarCode().toUpperCase().startsWith(PRODUCT_E_RISK_EXPERT) || attrValue.getBarCode().toUpperCase().startsWith(PRODUCT_E_RISK_EXPLORATION)) {
                // 专家和勘查商品，必须关联风评订单
                String relateOrderNo = detailRequest.getRelateOrderNo();
                if (StrUtil.isBlank(relateOrderNo)) {
                    throw new CrmebException("专家和勘查商品，必须关联风评订单");
                }
                List<OrderDetail> orderDetails = orderDetailService.getByOrderNo(relateOrderNo);
                if (orderDetails == null || orderDetails.size() < 1 || orderDetails.get(0) == null) {
                    throw new CrmebException("关联的风评订单无效");
                }
                if (!orderDetails.get(0).getUid().equals(uid) || !orderDetails.get(0).getMerId().equals(product.getMerId())) {
                    throw new CrmebException("关联的风评订单无效(必须同商户同用户)");
                }
                ProductAttrValue masterSku = productAttrValueService.getById(orderDetails.get(0).getAttrValueId());
                if (!masterSku.getBarCode().toUpperCase().startsWith(PRODUCT_E_RISK_REPORT)) {
                    throw new CrmebException("必须关联的风评商品订单");
                }
            }
        }

        PreMerchantOrderVo merchantOrderVo = new PreMerchantOrderVo();
        merchantOrderVo.setMerId(merchant.getId());
        merchantOrderVo.setMerName(merchant.getName());
        merchantOrderVo.setFreightFee(BigDecimal.ZERO);
        merchantOrderVo.setCouponFee(BigDecimal.ZERO);
        merchantOrderVo.setUserCouponId(0);
        merchantOrderVo.setTakeTheirSwitch(merchant.getIsTakeTheir());
        PreOrderInfoDetailVo detailVo = new PreOrderInfoDetailVo();
        detailVo.setProductId(product.getId());
        detailVo.setProductName(product.getName());
        detailVo.setAttrValueId(attrValue.getId());
        detailVo.setSku(attrValue.getSku());
        detailVo.setPrice(attrValue.getPrice());
        detailVo.setPayPrice(attrValue.getPrice());
        detailVo.setPayNum(detailRequest.getProductNum());
        detailVo.setImage(StrUtil.isNotBlank(attrValue.getImage()) ? attrValue.getImage() : product.getImage());
        detailVo.setVolume(attrValue.getVolume());
        detailVo.setWeight(attrValue.getWeight());
        detailVo.setTempId(product.getTempId());
        detailVo.setSubBrokerageType(product.getIsSub() ? 1 : 2);
        detailVo.setBrokerage(attrValue.getBrokerage());
        detailVo.setBrokerageTwo(attrValue.getBrokerageTwo());
        detailVo.setRegionCode(detailRequest.getRegionCode());
        if (detailVo.getSubBrokerageType() == 2) {
            String firstRatio = systemConfigService.getValueByKey(SysConfigConstants.RETAIL_STORE_BROKERAGE_FIRST_RATIO);
            String secondRatio = systemConfigService.getValueByKey(SysConfigConstants.RETAIL_STORE_BROKERAGE_SECOND_RATIO);
            detailVo.setBrokerage(StrUtil.isNotBlank(firstRatio) ? Integer.parseInt(firstRatio) : 0);
            detailVo.setBrokerageTwo(StrUtil.isNotBlank(secondRatio) ? Integer.parseInt(secondRatio) : 0);
        }
        detailVo.setProductType(ProductConstants.PRODUCT_TYPE_NORMAL);
        detailVo.setSourceId(detailRequest.getSourceId());
        detailVo.setVerify(detailRequest.getVerify());
        List<PreOrderInfoDetailVo> infoList = CollUtil.newArrayList();
        infoList.add(detailVo);
        merchantOrderVo.setOrderInfoList(infoList);
        return merchantOrderVo;
    }

    /**
     * 购物车预下单校验
     *
     * @param request 请求参数
     * @param user    用户
     * @return List<PreMerchantOrderVo>
     */
    private List<PreMerchantOrderVo> validatePreOrderShopping(PreOrderRequest request, User user) {
        List<PreMerchantOrderVo> merchantOrderVoList = CollUtil.newArrayList();
        request.getOrderDetails().forEach(e -> {
            if (ObjectUtil.isNull(e.getShoppingCartId())) {
                throw new CrmebException("购物车编号不能为空");
            }
            Cart cart = cartService.getByIdAndUid(e.getShoppingCartId(), user.getId());
            if (ObjectUtil.isNull(cart)) {
                throw new CrmebException("未找到对应的购物车信息");
            }
            e.setProductId(cart.getProductId());
            e.setAttrValueId(cart.getProductAttrUnique());
            e.setProductNum(cart.getCartNum());
            PreMerchantOrderVo merchantOrderVo = validatePreOrderNormal(e);
            if (merchantOrderVoList.stream().anyMatch(m -> m.getMerId().equals(merchantOrderVo.getMerId()))) {
                for (PreMerchantOrderVo orderVo : merchantOrderVoList) {
                    if (orderVo.getMerId().equals(merchantOrderVo.getMerId())) {
                        orderVo.getOrderInfoList().addAll(merchantOrderVo.getOrderInfoList());
                        break;
                    }
                }
            } else {
                merchantOrderVoList.add(merchantOrderVo);
            }
        });
        List<Integer> productIds = request.getOrderDetails().stream().map(PreOrderDetailRequest::getProductId).collect(Collectors.toList());
        orderService.getBizTypeByProductsType(productIds);
        return merchantOrderVoList;
    }

    private ComputedOrderPriceResponse computedPrice_V1_3(OrderComputedPriceRequest request, PreOrderInfoVo
            orderInfoVo, User user) {
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        if (request.getIsUseIntegral()) {
            if (integralDeductionSwitch.equals("false")) {
                throw new CrmebException("积分抵扣未开启，请重新下单");
            }
        }

        // 计算各种价格
        ComputedOrderPriceResponse priceResponse = new ComputedOrderPriceResponse();
        List<OrderMerchantRequest> orderMerchantRequestList = request.getOrderMerchantRequestList();
        // 计算运费
        UserAddress userAddress = userAddressService.getById(request.getAddressId());
        orderInfoVo.getMerchantOrderVoList().forEach(e -> {
            orderMerchantRequestList.forEach(o -> {
                if (o.getMerId().equals(e.getMerId())) {
                    e.setShippingType(o.getShippingType());
                    e.setUserCouponId(o.getUserCouponId());
                }
            });
        });
        getFreightFee(orderInfoVo, userAddress);
        priceResponse.setFreightFee(orderInfoVo.getFreightFee());
        // 优惠券计算
        orderInfoVo.setPlatUserCouponId(request.getPlatUserCouponId());
        getCouponFee(orderInfoVo, orderMerchantRequestList, user.getId());
        priceResponse.setCouponFee(orderInfoVo.getCouponFee());
        List<ComputedMerchantOrderResponse> merOrderResponseList = orderInfoVo.getMerchantOrderVoList().stream().map(vo -> {
            ComputedMerchantOrderResponse merOrderResponse = new ComputedMerchantOrderResponse();
            merOrderResponse.setMerId(vo.getMerId());
            merOrderResponse.setUserCouponId(vo.getUserCouponId());
            merOrderResponse.setCouponFee(vo.getCouponFee());
            merOrderResponse.setFreightFee(vo.getFreightFee());
            return merOrderResponse;
        }).collect(Collectors.toList());
        priceResponse.setMerOrderResponseList(merOrderResponseList);
        // 积分部分
        BigDecimal payPrice = orderInfoVo.getProTotalFee().subtract(priceResponse.getCouponFee());
        priceResponse.setIsUseIntegral(request.getIsUseIntegral());
        priceResponse.setProTotalFee(orderInfoVo.getProTotalFee());
        if (!request.getIsUseIntegral() || user.getIntegral() <= 0) {// 不使用积分
            priceResponse.setDeductionPrice(BigDecimal.ZERO);
            priceResponse.setSurplusIntegral(user.getIntegral());
            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
            priceResponse.setUsedIntegral(0);
            priceResponse.setIntegralDeductionSwitch(false);
            priceResponse.setIsUseIntegral(false);
            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
            if ("ture".equals(integralDeductionSwitch) && payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
                priceResponse.setIntegralDeductionSwitch(true);
            }
            return priceResponse;
        }
        // 使用积分
        if (request.getIsUseIntegral() && user.getIntegral() > 0) {
            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
            if (Integer.parseInt(integralDeductionStartMoney) <= 0 || payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) < 0) {
                priceResponse.setDeductionPrice(BigDecimal.ZERO);
                priceResponse.setSurplusIntegral(user.getIntegral());
                priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
                priceResponse.setUsedIntegral(0);
                priceResponse.setIntegralDeductionSwitch(false);
                priceResponse.setIsUseIntegral(false);
                return priceResponse;
            }

            // 查询积分使用比例
            String integralRatio = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_RATIO);
            // 可抵扣金额
            BigDecimal canDeductionPrice = payPrice.multiply(new BigDecimal(integralRatio)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
            // 积分转换金额
            String integralDeductionMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_MONEY);
            BigDecimal deductionPrice = new BigDecimal(user.getIntegral()).multiply(new BigDecimal(integralDeductionMoney));
            // 积分兑换金额小于实际支付可抵扣金额
            if (deductionPrice.compareTo(canDeductionPrice) <= 0) {
//                payPrice = payPrice.subtract(deductionPrice);
                priceResponse.setSurplusIntegral(0);
                priceResponse.setUsedIntegral(user.getIntegral());
            } else {
                deductionPrice = canDeductionPrice;
                if (canDeductionPrice.compareTo(BigDecimal.ZERO) > 0) {
                    int usedIntegral = canDeductionPrice.divide(new BigDecimal(integralDeductionMoney), 0, BigDecimal.ROUND_UP).intValue();
                    priceResponse.setSurplusIntegral(user.getIntegral() - usedIntegral);
                    priceResponse.setUsedIntegral(usedIntegral);
                }
            }
            payPrice = payPrice.subtract(deductionPrice);
            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
            priceResponse.setDeductionPrice(deductionPrice);
            priceResponse.setIsUseIntegral(true);
            priceResponse.setIntegralDeductionSwitch(true);
        }
        return priceResponse;
    }

    private ComputedOrderPriceResponse computedPrice(OrderComputedPriceRequest request, PreOrderInfoVo
            orderInfoVo, User user) {
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        if (request.getIsUseIntegral()) {
            if (integralDeductionSwitch.equals("false")) {
                throw new CrmebException("积分抵扣未开启，请重新下单");
            }
        }

        // 计算各种价格
        ComputedOrderPriceResponse priceResponse = new ComputedOrderPriceResponse();
        List<OrderMerchantRequest> orderMerchantRequestList = request.getOrderMerchantRequestList();
        // 计算运费
        UserAddress userAddress = userAddressService.getById(request.getAddressId());
        orderInfoVo.getMerchantOrderVoList().forEach(e -> {
            orderMerchantRequestList.forEach(o -> {
                if (o.getMerId().equals(e.getMerId())) {
                    e.setShippingType(o.getShippingType());
                    e.setUserCouponId(o.getUserCouponId());
                    e.setCouponFee(BigDecimal.ZERO);
                    e.setMerCouponFee(BigDecimal.ZERO);
                }
            });
        });
        getFreightFee(orderInfoVo, userAddress);
        priceResponse.setFreightFee(orderInfoVo.getFreightFee());
        // 优惠券计算
        orderInfoVo.setCouponFee(BigDecimal.ZERO);
        orderInfoVo.setPlatUserCouponId(request.getPlatUserCouponId());
        orderInfoVo.setPlatCouponFee(BigDecimal.ZERO);
        orderInfoVo.setMerCouponFee(BigDecimal.ZERO);
        priceResponse.setCouponFee(BigDecimal.ZERO);
        if (!orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL) && !orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {
            getCouponFee_V1_3(orderInfoVo, user.getId());
            priceResponse.setCouponFee(orderInfoVo.getCouponFee());
            priceResponse.setPlatCouponFee(orderInfoVo.getPlatCouponFee());
            priceResponse.setMerCouponFee(orderInfoVo.getMerCouponFee());
            priceResponse.setMerOrderResponseList(computedPriceGetMerOrderList(orderInfoVo));
            priceResponse.setPlatCouponUserList(computedPriceGetPlatOrderList(orderInfoVo));
        }
        // 积分部分
        BigDecimal payPrice = orderInfoVo.getProTotalFee().subtract(priceResponse.getCouponFee());
        priceResponse.setIsUseIntegral(request.getIsUseIntegral());
        priceResponse.setProTotalFee(orderInfoVo.getProTotalFee());
        if (!request.getIsUseIntegral() || user.getIntegral() <= 0) {// 不使用积分
            priceResponse.setDeductionPrice(BigDecimal.ZERO);
            priceResponse.setSurplusIntegral(user.getIntegral());
            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
            priceResponse.setUsedIntegral(0);
            priceResponse.setIntegralDeductionSwitch(false);
            priceResponse.setIsUseIntegral(false);
            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
            if ("ture".equals(integralDeductionSwitch) && payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
                priceResponse.setIntegralDeductionSwitch(true);
            }
            return priceResponse;
        }
        // 使用积分
        if (request.getIsUseIntegral() && user.getIntegral() > 0) {
            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
            if (Integer.parseInt(integralDeductionStartMoney) <= 0 || payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) < 0) {
                priceResponse.setDeductionPrice(BigDecimal.ZERO);
                priceResponse.setSurplusIntegral(user.getIntegral());
                priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
                priceResponse.setUsedIntegral(0);
                priceResponse.setIntegralDeductionSwitch(false);
                priceResponse.setIsUseIntegral(false);
                return priceResponse;
            }

            // 查询积分使用比例
            String integralRatio = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_RATIO);
            // 可抵扣金额
            BigDecimal canDeductionPrice = payPrice.multiply(new BigDecimal(integralRatio)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
            // 积分转换金额
            String integralDeductionMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_MONEY);
            BigDecimal deductionPrice = new BigDecimal(user.getIntegral()).multiply(new BigDecimal(integralDeductionMoney));
            // 积分兑换金额小于实际支付可抵扣金额
            if (deductionPrice.compareTo(canDeductionPrice) <= 0) {
//                payPrice = payPrice.subtract(deductionPrice);
                priceResponse.setSurplusIntegral(0);
                priceResponse.setUsedIntegral(user.getIntegral());
            } else {
                deductionPrice = canDeductionPrice;
                if (canDeductionPrice.compareTo(BigDecimal.ZERO) > 0) {
                    int usedIntegral = canDeductionPrice.divide(new BigDecimal(integralDeductionMoney), 0, BigDecimal.ROUND_UP).intValue();
                    priceResponse.setSurplusIntegral(user.getIntegral() - usedIntegral);
                    priceResponse.setUsedIntegral(usedIntegral);
                }
            }
            payPrice = payPrice.subtract(deductionPrice);
            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
            priceResponse.setDeductionPrice(deductionPrice);
            priceResponse.setIsUseIntegral(true);
            priceResponse.setIntegralDeductionSwitch(true);
        }
        return priceResponse;
    }

    private List<CouponUser> computedPriceGetPlatOrderList(PreOrderInfoVo orderInfoVo) {
        Integer userId = userService.getUserId();
        // 获取平台可用优惠券列表
        List<Integer> merIdList = new ArrayList<>();
        List<Integer> proIdList = new ArrayList<>();
        List<Product> productList = new ArrayList<>();
        List<PreOrderInfoDetailVo> orderDetailInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : orderInfoVo.getMerchantOrderVoList()) {
            merIdList.add(merchantOrderVo.getMerId());
            merchantOrderVo.getOrderInfoList().forEach(info -> {
                if (!proIdList.contains(info.getProductId())) {
                    Product product = productService.getById(info.getProductId());
                    productList.add(product);
                    proIdList.add(product.getId());
                }
            });
            orderDetailInfoList.addAll(merchantOrderVo.getOrderInfoList());
        }
        BigDecimal proTotalFee = orderInfoVo.getProTotalFee();
        List<Integer> proCategoryIdList = productList.stream().map(Product::getCategoryId).collect(Collectors.toList());
        List<Integer> secondParentIdList = productCategoryService.findParentIdByChildIds(proCategoryIdList);
        List<Integer> firstParentIdList = productCategoryService.findParentIdByChildIds(secondParentIdList);
        proCategoryIdList.addAll(secondParentIdList);
        proCategoryIdList.addAll(firstParentIdList);
        List<Integer> brandIdList = productList.stream().map(Product::getBrandId).collect(Collectors.toList());
        List<CouponUser> platCouponUserList = couponUserService.findManyPlatByUidAndMerIdAndMoneyAndProList(userId, proIdList, proCategoryIdList, merIdList, brandIdList, proTotalFee);

        BigDecimal remainingAmount = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
        for (CouponUser couponUser : platCouponUserList) {
            if (orderInfoVo.getPlatUserCouponId() > 0 && couponUser.getId().equals(orderInfoVo.getPlatUserCouponId())) {
                couponUser.setIsChecked(true);
                couponUser.setIsChoose(true);
            }
            // 判断是否可用
            if (remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (orderInfoVo.getProTotalFee().compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            Coupon coupon = couponService.getById(couponUser.getCouponId());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId()))
                        .map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0
                        && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
        }
        return platCouponUserList;
    }

    private List<ComputedMerchantOrderResponse> computedPriceGetMerOrderList(PreOrderInfoVo orderInfoVo) {
        Integer userId = userService.getUserId();
        List<ComputedMerchantOrderResponse> merOrderResponseList = orderInfoVo.getMerchantOrderVoList().stream().map(vo -> {
            ComputedMerchantOrderResponse merOrderResponse = new ComputedMerchantOrderResponse();
            merOrderResponse.setMerId(vo.getMerId());
            merOrderResponse.setUserCouponId(vo.getUserCouponId());
            merOrderResponse.setCouponFee(vo.getMerCouponFee());
            merOrderResponse.setFreightFee(vo.getFreightFee());
            // 查询适用的用户优惠券
            List<Integer> merProIdList = vo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).distinct().collect(Collectors.toList());
            BigDecimal merPrice = vo.getProTotalFee();
            BigDecimal merRemainingAmount = vo.getProTotalFee().subtract(orderInfoVo.getPlatCouponFee());
            List<CouponUser> merCouponUserList = couponUserService.findManyByUidAndMerIdAndMoneyAndProList(userId, vo.getMerId(), merProIdList, merPrice);
            for (int i = 0; i < merCouponUserList.size(); ) {
                CouponUser couponUser = merCouponUserList.get(i);
                if (vo.getUserCouponId() > 0 && couponUser.getId().equals(vo.getUserCouponId())) {
                    couponUser.setIsChecked(true);
                    couponUser.setIsChoose(true);
                    i++;
                    continue;
                }
                if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    if (merRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                    i++;
                    continue;
                }
                Coupon coupon = couponService.getById(couponUser.getCouponId());
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = vo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proRemainingAmount = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getPlatCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0) {
                    if (proRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                }
                i++;
            }
            merOrderResponse.setMerCouponUserList(merCouponUserList);
            return merOrderResponse;
        }).collect(Collectors.toList());
        return merOrderResponseList;
    }

    /**
     * 视频号下单校验
     * 暂时只支持都买一种商品
     *
     * @param detailRequest 请求参数
     * @return 预下单结果
     */
    private PreMerchantOrderVo validatePreOrderVideo(PreOrderDetailRequest detailRequest) {
        logger.info("预下单下单校验 -》validatePreOrderVideo-》:{}", JSONObject.toJSONString(detailRequest));
        if (ObjectUtil.isNull(detailRequest.getProductId())) {
            throw new CrmebException("商品编号不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getAttrValueId())) {
            throw new CrmebException("商品规格属性值不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getProductNum()) || detailRequest.getProductNum() <= 0) {
            throw new CrmebException("购买数量必须大于0");
        }
        // 查询商品信息
        PayComponentProduct product = payComponentProductService.getById(detailRequest.getProductId());
        logger.info("预下单下单校验 -》product-》:{}", JSON.toJSONString(product));
        if (ObjectUtil.isNull(product)) {
            throw new CrmebException("商品信息不存在，请刷新后重新选择");
        }
        if (product.getIsDel()) {
            throw new CrmebException("商品已删除，请刷新后重新选择");
        }
        if (!product.getStatus().equals(5)) {
            throw new CrmebException("商品已下架，请刷新后重新选择");
        }
        if (product.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品库存不足，请刷新后重新选择");
        }
        // 查询商品规格属性值信息
        ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(detailRequest.getAttrValueId(), detailRequest.getProductId(), ProductConstants.PRODUCT_TYPE_COMPONENT);
        logger.info("预下单下单校验 -》attrValue:{}", JSON.toJSONString(attrValue));
        if (ObjectUtil.isNull(attrValue)) {
            throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
        }
        if (attrValue.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品规格库存不足，请刷新后重新选择");
        }
        Merchant merchant = merchantService.getByIdException(product.getMerId());
        if (!merchant.getIsSwitch()) {
            throw new CrmebException("商户已关闭，请重新选择商品");
        }
        PreMerchantOrderVo merchantOrderVo = new PreMerchantOrderVo();
        merchantOrderVo.setMerId(merchant.getId());
        merchantOrderVo.setMerName(merchant.getName());
        merchantOrderVo.setFreightFee(BigDecimal.ZERO);
        merchantOrderVo.setCouponFee(BigDecimal.ZERO);
        merchantOrderVo.setUserCouponId(0);
        merchantOrderVo.setTakeTheirSwitch(merchant.getIsTakeTheir());
        PreOrderInfoDetailVo detailVo = new PreOrderInfoDetailVo();
        detailVo.setProductId(product.getId());
        detailVo.setProductName(product.getTitle());
        detailVo.setAttrValueId(attrValue.getId());
        detailVo.setSku(attrValue.getSku());
        detailVo.setPrice(attrValue.getPrice());
        detailVo.setPayPrice(attrValue.getPrice());
        detailVo.setPayNum(detailRequest.getProductNum());
        detailVo.setImage(StrUtil.isNotBlank(attrValue.getImage()) ? attrValue.getImage() : product.getHeadImg());
        detailVo.setVolume(attrValue.getVolume());
        detailVo.setWeight(attrValue.getWeight());
        detailVo.setTempId(product.getTempId());
        detailVo.setSubBrokerageType(product.getIsSub() ? 1 : 2);
        detailVo.setBrokerage(attrValue.getBrokerage());
        detailVo.setBrokerageTwo(attrValue.getBrokerageTwo());
//        PreOrderInfoDetailVo detailVo = new PreOrderInfoDetailVo();
//        detailVo.setProductId(product.getId());
//        detailVo.setProductName(product.getTitle());
//        detailVo.setAttrValueId(attrValue.getId());
//        detailVo.setSku(attrValue.getSku());
//        detailVo.setPrice(attrValue.getPrice());
//        detailVo.setPayNum(detailRequest.getProductNum());
//        detailVo.setImage(attrValue.getImage());
//        detailVo.setVolume(attrValue.getVolume());
//        detailVo.setWeight(attrValue.getWeight());
//        detailVo.setTempId(product.getTempId());
////        detailVo.setIsSub(product.getIsSub());
//        detailVo.setProductType(ProductConstants.PRODUCT_TYPE_COMPONENT);
        List<PreOrderInfoDetailVo> infoList = CollUtil.newArrayList();
        infoList.add(detailVo);
        merchantOrderVo.setOrderInfoList(infoList);
        logger.info("预下单下单校验-》merchantOrderVo:{}", JSON.toJSONString(merchantOrderVo));
        return merchantOrderVo;
    }

    /**
     * 获取优惠金额
     */
    @Override
    public void getCouponFee(PreOrderInfoVo
                                     orderInfoVo, List<OrderMerchantRequest> orderMerchantRequestList, Integer uid) {
        long count = orderMerchantRequestList.stream().filter(e -> e.getUserCouponId() > 0).count();
        if (count <= 0) {
            orderInfoVo.setCouponFee(BigDecimal.ZERO);
            return;
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            throw new CrmebException("营销活动商品无法使用优惠券");
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {
            throw new CrmebException("视频号商品无法使用优惠券");
        }
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            for (OrderMerchantRequest orderMerchantRequest : orderMerchantRequestList) {
                if (merchantOrderVo.getMerId().equals(orderMerchantRequest.getMerId()) && orderMerchantRequest.getUserCouponId() > 0) {
                    merchantOrderVo.setUserCouponId(orderMerchantRequest.getUserCouponId());
                }
            }
        }
        BigDecimal couponFee = BigDecimal.ZERO;
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            if (merchantOrderVo.getUserCouponId() <= 0) {
                continue;
            }
            BigDecimal merchantCouponFee = BigDecimal.ZERO;
            // 判断优惠券是否可以使用
            CouponUser couponUser = couponUserService.getById(merchantOrderVo.getUserCouponId());
            if (ObjectUtil.isNull(couponUser) || !couponUser.getUid().equals(uid)) {
                throw new CrmebException("优惠券领取记录不存在！");
            }
            if (!couponUser.getMerId().equals(merchantOrderVo.getMerId())) {
                throw new CrmebException("商家无此优惠券");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_USED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已使用！");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_LAPSED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已失效！");
            }
            //判断是否在使用时间内
            Date date = CrmebDateUtil.nowDateTime();
            if (couponUser.getStartTime().compareTo(date) > 0) {
                throw new CrmebException("此优惠券还未到达使用时间范围之内！");
            }
            if (date.compareTo(couponUser.getEndTime()) > 0) {
                throw new CrmebException("此优惠券已经失效了");
            }
            if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(orderInfoVo.getProTotalFee()) > 0) {
                throw new CrmebException("总金额小于优惠券最小使用金额");
            }
            //检测优惠券信息
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                // 商品券
                List<Integer> productIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
                if (productIdList.size() < 1) {
                    throw new CrmebException("No item found product");
                }
                //设置优惠券所提供的集合
                List<CouponProduct> couponProductList = couponProductService.findByCid(couponUser.getCouponId());
                List<Integer> primaryKeyIdList = couponProductList.stream().map(CouponProduct::getPid).collect(Collectors.toList());
                //取两个集合的交集，如果是false则证明没有相同的值
                //oldList.retainAll(newList)返回值代表oldList是否保持原样，如果old和new完全相同，那old保持原样并返回false。
                //交集：listA.retainAll(listB) ——listA内容变为listA和listB都存在的对象；listB不变
                primaryKeyIdList.retainAll(productIdList);
                if (CollUtil.isEmpty(primaryKeyIdList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList().stream().filter(info -> primaryKeyIdList.contains(info.getProductId())).collect(Collectors.toList());
                if (CollUtil.isEmpty(infoDetailVoList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
                    throw new CrmebException("总金额小于优惠券最低使用金额");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                } else {
                    merchantCouponFee = merchantCouponFee.add(proTotalPrice);
                }
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setCouponPrice(detailCouponFee);
                    }
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                // 商家券
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList();
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
                    throw new CrmebException("总金额小于优惠券最低使用金额");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                } else {
                    merchantCouponFee = merchantCouponFee.add(proTotalPrice);
                }
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setCouponPrice(detailCouponFee);
                    }
                }
            }
            couponFee = couponFee.add(merchantCouponFee);
            merchantOrderVo.setCouponFee(merchantCouponFee);
        }
        orderInfoVo.setCouponFee(couponFee);
    }

    /**
     * 获取优惠金额
     */
    private void getCouponFee_V1_3(PreOrderInfoVo orderInfoVo, Integer uid) {
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            throw new CrmebException("营销活动商品无法使用优惠券");
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {
            throw new CrmebException("视频号商品无法使用优惠券");
        }
        orderInfoVo.getMerchantOrderVoList().forEach(merchantOrder -> {
            merchantOrder.setCouponFee(BigDecimal.ZERO);
            merchantOrder.setMerCouponFee(BigDecimal.ZERO);
            merchantOrder.getOrderInfoList().forEach(info -> {
                info.setMerCouponPrice(BigDecimal.ZERO);
                info.setPlatCouponPrice(BigDecimal.ZERO);
            });
        });
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        long count = merchantOrderVoList.stream().filter(e -> e.getUserCouponId() > 0).count();
        if (count <= 0 && orderInfoVo.getPlatUserCouponId() <= 0) {
            orderInfoVo.setCouponFee(BigDecimal.ZERO);
            return;
        }
        BigDecimal couponFee = BigDecimal.ZERO;
        Date date = CrmebDateUtil.nowDateTime();
        List<Integer> proIdList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            List<Integer> productIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
            proIdList.addAll(productIdList);
            if (merchantOrderVo.getUserCouponId() <= 0) {
                merchantOrderVo.setCouponFee(BigDecimal.ZERO);
                merchantOrderVo.setMerCouponFee(BigDecimal.ZERO);
                continue;
            }
            BigDecimal merchantCouponFee = BigDecimal.ZERO;
            // 判断优惠券是否可以使用
            CouponUser couponUser = couponUserService.getById(merchantOrderVo.getUserCouponId());
            if (ObjectUtil.isNull(couponUser) || !couponUser.getUid().equals(uid)) {
                throw new CrmebException("优惠券领取记录不存在！");
            }
            if (!couponUser.getMerId().equals(merchantOrderVo.getMerId())) {
                throw new CrmebException("商家无此优惠券");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_USED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已使用！");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_LAPSED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已失效！");
            }
            //判断是否在使用时间内
            if (couponUser.getStartTime().compareTo(date) > 0) {
                throw new CrmebException("此优惠券还未到达使用时间范围之内！");
            }
            if (date.compareTo(couponUser.getEndTime()) > 0) {
                throw new CrmebException("此优惠券已经失效了");
            }
            if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(merchantOrderVo.getProTotalFee()) > 0) {
//                throw new CrmebException("总金额小于优惠券最小使用金额");
                throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
            }

            //检测优惠券信息
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                // 商品券
                //设置优惠券所提供的集合
                List<CouponProduct> couponProductList = couponProductService.findByCid(couponUser.getCouponId());
                List<Integer> primaryKeyIdList = couponProductList.stream().map(CouponProduct::getPid).collect(Collectors.toList());
                //取两个集合的交集，如果是false则证明没有相同的值
                //oldList.retainAll(newList)返回值代表oldList是否保持原样，如果old和new完全相同，那old保持原样并返回false。
                //交集：listA.retainAll(listB) ——listA内容变为listA和listB都存在的对象；listB不变
                primaryKeyIdList.retainAll(productIdList);
                if (CollUtil.isEmpty(primaryKeyIdList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList().stream().filter(info -> primaryKeyIdList.contains(info.getProductId())).collect(Collectors.toList());
                if (CollUtil.isEmpty(infoDetailVoList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
//                    throw new CrmebException("总金额小于优惠券最低使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setMerCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setMerCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setMerCouponPrice(detailCouponFee);
                    }
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                // 商家券
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList();
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
//                    throw new CrmebException("总金额小于优惠券最低使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setMerCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setMerCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setMerCouponPrice(detailCouponFee);
                    }
                }
            }
            couponFee = couponFee.add(merchantCouponFee);
            merchantOrderVo.setCouponFee(merchantCouponFee);
            merchantOrderVo.setMerCouponFee(merchantCouponFee);
        }
        orderInfoVo.setMerCouponFee(couponFee);
        // 计算平台优惠券
        if (orderInfoVo.getPlatUserCouponId() > 0) {

            CouponUser couponUser = couponUserService.getById(orderInfoVo.getPlatUserCouponId());
            if (ObjectUtil.isNull(couponUser) || !couponUser.getUid().equals(uid)) {
                throw new CrmebException("优惠券领取记录不存在！");
            }
            if (!couponUser.getMerId().equals(0)) {
                throw new CrmebException("平台无此优惠券");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_USED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已使用！");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_LAPSED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已失效！");
            }
            //判断是否在使用时间内
            if (couponUser.getStartTime().compareTo(date) > 0) {
                throw new CrmebException("此优惠券还未到达使用时间范围之内！");
            }
            if (date.compareTo(couponUser.getEndTime()) > 0) {
                throw new CrmebException("此优惠券已经失效了");
            }

            Coupon coupon = couponService.getById(couponUser.getCouponId());
            List<PreOrderInfoDetailVo> orderInfoList = new ArrayList<>();
            merchantOrderVoList.forEach(m -> {
                orderInfoList.addAll(m.getOrderInfoList());
            });
            BigDecimal platCouponPrice = new BigDecimal(couponUser.getMoney().toString());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                BigDecimal remainingAmount = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
                if (orderInfoVo.getProTotalFee().compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                BigDecimal proTotalPrice = orderInfoList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                for (int i = 0; i < orderInfoList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = orderInfoList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                // 平台优惠券计算金额 = （商品金额 - 商户优惠金额） * 购买数量
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            proIdList = proIdList.stream().distinct().collect(Collectors.toList());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Product> productList = productService.findByIds(proIdList);
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Product> productList = productService.findByIds(proIdList);
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Product> productList = productService.findByIds(proIdList);
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            BigDecimal platCouponFee = new BigDecimal(couponUser.getMoney().toString());
            orderInfoVo.setPlatCouponFee(platCouponFee);
        }
        orderInfoVo.setCouponFee(orderInfoVo.getPlatCouponFee().add(orderInfoVo.getMerCouponFee()));
    }
}
