package com.slodon.b2b2c.controller.business.front;

import com.slodon.b2b2c.business.dto.OrderSubmitDTO;
import com.slodon.b2b2c.business.dto.OrderSubmitMqConsumerDTO;
import com.slodon.b2b2c.business.dto.OrderSubmitParamDTO;
import com.slodon.b2b2c.business.dto.PreOrderDTO;
import com.slodon.b2b2c.business.example.OrderExample;
import com.slodon.b2b2c.business.example.OrderReserveExample;
import com.slodon.b2b2c.business.pojo.Order;
import com.slodon.b2b2c.business.pojo.OrderProduct;
import com.slodon.b2b2c.business.pojo.OrderReserve;
import com.slodon.b2b2c.core.constant.*;
import com.slodon.b2b2c.core.response.JsonResult;
import com.slodon.b2b2c.core.response.SldResponse;
import com.slodon.b2b2c.core.uid.GoodsIdGenerator;
import com.slodon.b2b2c.core.util.AssertUtil;
import com.slodon.b2b2c.core.util.StringUtil;
import com.slodon.b2b2c.core.util.UserUtil;
import com.slodon.b2b2c.goods.dto.CalculateExpressDTO;
import com.slodon.b2b2c.goods.example.GoodsBindReserveNameExample;
import com.slodon.b2b2c.goods.pojo.Goods;
import com.slodon.b2b2c.goods.pojo.GoodsBindReserveName;
import com.slodon.b2b2c.goods.pojo.GoodsPromotion;
import com.slodon.b2b2c.goods.pojo.Product;
import com.slodon.b2b2c.member.pojo.Member;
import com.slodon.b2b2c.member.pojo.MemberAddress;
import com.slodon.b2b2c.model.business.CartModel;
import com.slodon.b2b2c.model.business.OrderModel;
import com.slodon.b2b2c.model.business.OrderReserveModel;
import com.slodon.b2b2c.model.goods.GoodsBindReserveNameModel;
import com.slodon.b2b2c.model.goods.GoodsFreightTemplateModel;
import com.slodon.b2b2c.model.goods.GoodsModel;
import com.slodon.b2b2c.model.goods.ProductModel;
import com.slodon.b2b2c.model.member.MemberAddressModel;
import com.slodon.b2b2c.model.member.MemberModel;
import com.slodon.b2b2c.model.promotion.*;
import com.slodon.b2b2c.model.system.ReasonModel;
import com.slodon.b2b2c.promotion.example.*;
import com.slodon.b2b2c.promotion.pojo.*;
import com.slodon.b2b2c.starter.redisson.SlodonLock;
import com.slodon.b2b2c.starter.redisson.constant.LockKeyConst;
import com.slodon.b2b2c.system.pojo.Reason;
import com.slodon.b2b2c.util.OrderSubmitUtil;
import com.slodon.b2b2c.vo.business.OrderSubmitCheckVO;
import com.slodon.b2b2c.vo.business.OrderSubmitPageVO;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.slodon.b2b2c.core.constant.StarterConfigConst.MQ_EXCHANGE_NAME;
import static com.slodon.b2b2c.core.constant.StarterConfigConst.MQ_QUEUE_NAME_ORDER_SUBMIT;

/**
 * @author Slodon
 */
@Api(tags = "front-订单操作")
@RestController
@RequestMapping("v3/business/front/orderOperate")
@Slf4j
public class FrontOrderOperateController {

    @Resource
    private CartModel cartModel;
    @Resource
    private OrderModel orderModel;
    @Resource
    private OrderReserveModel orderReserveModel;
    @Resource
    private MemberModel memberModel;
    @Resource
    private MemberAddressModel memberAddressModel;
    @Resource
    private GoodsFreightTemplateModel goodsFreightTemplateModel;
    @Resource
    private PromotionCommonModel promotionCommonModel;
    @Resource
    private ReasonModel reasonModel;
    @Resource
    private GoodsModel goodsModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private GoodsBindReserveNameModel goodsBindReserveNameModel;
    @Resource
    private PresellModel presellModel;
    @Resource
    private PresellGoodsModel presellGoodsModel;
    @Resource
    private PresellOrderExtendModel presellOrderExtendModel;
    @Resource
    private LadderGroupModel ladderGroupModel;
    @Resource
    private LadderGroupRuleModel ladderGroupRuleModel;
    @Resource
    private LadderGroupGoodsModel ladderGroupGoodsModel;
    @Resource
    private LadderGroupOrderExtendModel ladderGroupOrderExtendModel;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private OrderSubmitUtil orderSubmitUtil;
    @Resource
    private SlodonLock slodonLock;

    @PostMapping("check")
    @ApiOperation("检测购物车信息")
    public JsonResult<OrderSubmitCheckVO> check(HttpServletRequest request, @RequestBody OrderSubmitParamDTO dto) {
        Member member = UserUtil.getUser(request, Member.class);

        OrderSubmitCheckVO vo = cartModel.orderPreCheck(dto, member.getMemberId());
        if (vo.getState().equals(OrderSubmitCheckVO.STATE_0)) {
            //检测通过
            return SldResponse.success();
        } else {
            //检测不通过
            return SldResponse.failSpecial(vo);
        }
    }

    @PostMapping("confirm")
    @ApiOperation("确认订单接口（去结算），获取提交订单页数据(注：调用此接口之前需先调用[检测购物车信息]接口，state=200才可以调用此接口)")
    public JsonResult<OrderSubmitPageVO> confirm(HttpServletRequest request, @RequestBody OrderSubmitParamDTO dto) {
        Member member = UserUtil.getUser(request, Member.class);

        //构造返回数据
        OrderSubmitPageVO vo;

        //构造计算优惠dto
        OrderSubmitDTO orderSubmitDTO = orderSubmitUtil.getOrderSubmitDTO(dto, member.getMemberId(), true);

        //计算运费
        List<BigDecimal> expressFeeList = new ArrayList<>();
        MemberAddress memberAddress = null;
        if (!StringUtil.isNullOrZero(dto.getAddressId())) {
            memberAddress = memberAddressModel.getMemberAddressByAddressId(dto.getAddressId());
        }
        for (OrderSubmitDTO.OrderInfo orderInfo : orderSubmitDTO.getOrderInfoList()) {
            BigDecimal expressFee = new BigDecimal("0.00");
            //实物商品计算运费
            if (orderInfo.getIsVirtualGoods() == GoodsConst.IS_VIRTUAL_GOODS_1) {
                //传了地址信息，计算运费
                //构造计算运费的dto
                CalculateExpressDTO calculateExpressDTO = new CalculateExpressDTO();
                calculateExpressDTO.setStoreId(orderInfo.getStoreId());
                if (memberAddress == null) {
                    calculateExpressDTO.setCityCode(null);
                } else {
                    calculateExpressDTO.setCityCode(memberAddress.getCityCode());
                }
                orderInfo.getOrderProductInfoList().forEach(orderProductInfo -> {
                    calculateExpressDTO.getProductList()
                            .add(new CalculateExpressDTO.ProductInfo(
                                    orderProductInfo.getGoodsId(),
                                    orderProductInfo.getProductId(),
                                    orderProductInfo.getProductPrice(),
                                    orderProductInfo.getBuyNum()));
                });
                expressFee = goodsFreightTemplateModel.calculateExpressFee(calculateExpressDTO);
            }
            expressFeeList.add(expressFee);
        }

        GoodsPromotion singlePromotion = null;
        if (!dto.getIsCart()) {
            //立即购买，查询是否参加活动
            singlePromotion = cartModel.getSinglePromotion(dto.getProductId());
        }
        if (singlePromotion != null && singlePromotion.getPromotionType().equals(PromotionConst.PROMOTION_TYPE_103)) {
            //预售确认
            vo = preSaleConfirm(dto, orderSubmitDTO, singlePromotion, expressFeeList);
        } else if (singlePromotion != null && singlePromotion.getPromotionType().equals(PromotionConst.PROMOTION_TYPE_105)) {
            //阶梯团确认
            vo = ladderGroupConfirm(dto, orderSubmitDTO, singlePromotion, expressFeeList);
        } else {
            //调用活动模块计算优惠
            orderSubmitDTO = promotionCommonModel.orderSubmitCalculationDiscount(orderSubmitDTO, dto.getSource());

            vo = new OrderSubmitPageVO(orderSubmitDTO, expressFeeList);

            //是否可以开增值税发票
            boolean isVatInvoice = true;
            //保存已查询的商品，减少查库次数
            Map<Long, Goods> goodsMap = new HashMap<>();
            for (OrderSubmitDTO.OrderInfo orderInfo : orderSubmitDTO.getOrderInfoList()) {
                for (OrderSubmitDTO.OrderInfo.OrderProductInfo orderProductInfo : orderInfo.getOrderProductInfoList()) {
                    if (goodsMap.containsKey(orderProductInfo.getGoodsId())) {
                        continue;
                    }
                    Goods goods = goodsModel.getGoodsByGoodsId(orderProductInfo.getGoodsId());
                    if (goods.getIsVatInvoice() != null && goods.getIsVatInvoice() == GoodsConst.IS_VAT_INVOICE_NO) {
                        //商品不允许开增值税发票,跳出循环
                        isVatInvoice = false;
                        break;
                    }
                    goodsMap.put(goods.getGoodsId(), goods);
                }
                if (!isVatInvoice) {
                    //有一个商品不允许开增值税发票,跳出循环
                    break;
                }
            }
            vo.setIsVatInvoice(isVatInvoice);
        }
        //积分抵现开关是否开启
        String isEnable = stringRedisTemplate.opsForValue().get("integral_cash_out_is_enable");
        if (!StringUtil.isEmpty(isEnable) && "1".equals(isEnable)) {
            //查询数据库会员信息
            Member memberDb = memberModel.getMemberByMemberId(member.getMemberId());
            AssertUtil.notNull(memberDb, "会员不存在");
            vo.setMemberIntegral(memberDb.getMemberIntegral());
            //获取多少积分换算成1元
            String conversionRatio = stringRedisTemplate.opsForValue().get("integral_conversion_ratio");
            int integralScale = StringUtil.isEmpty(conversionRatio) ? 100 : Integer.parseInt(conversionRatio);
            vo.setIntegralScale(integralScale);
            //合计
            BigDecimal totalAmount = new BigDecimal(vo.getTotalAmount());
            //合计-运费
            for (BigDecimal fee : expressFeeList) {
                totalAmount = totalAmount.subtract(fee);
            }
            //积分抵扣金额
            BigDecimal integralCashAmount = new BigDecimal("0.00");
            if (!StringUtil.isNullOrZero(dto.getIntegral())) {
                //积分换算成金额的总值
                integralCashAmount = integralCashAmount.add(BigDecimal.valueOf(dto.getIntegral()).divide(new BigDecimal(integralScale), 2, RoundingMode.HALF_UP));
            }
            totalAmount = totalAmount.add(integralCashAmount);
            //订单金额达到积分最低使用金额后，才可以使用使用积分抵扣现金
            String useLowestAmount = stringRedisTemplate.opsForValue().get("integral_use_lowest_amount");
            BigDecimal lowestAmount = StringUtil.isEmpty(useLowestAmount) ? totalAmount : new BigDecimal(useLowestAmount);
            if (totalAmount.compareTo(lowestAmount) >= 0) {
                //最高抵扣金额,例如：商品金额为200元，最高抵扣上限为90%，则积分最多可抵扣180元
                String deductRate = stringRedisTemplate.opsForValue().get("integral_max_deduct_rate");
                int maxDeductRate = StringUtil.isEmpty(deductRate) || Integer.parseInt(deductRate) > 100 ? 100 : Integer.parseInt(deductRate);
                //积分金额
                Integer integralAmount = totalAmount.multiply(new BigDecimal(maxDeductRate)).multiply(new BigDecimal(integralScale))
                        .divide(new BigDecimal("100.00"), 2, RoundingMode.HALF_UP).intValue();
                vo.setIntegralList(this.getIntegralList(memberDb.getMemberIntegral(), integralAmount, integralScale));
            }
        }
        //立即购买，并且是虚拟商品，返回预留信息
        if (!dto.getIsCart() && orderSubmitDTO.getOrderInfoList().get(0).getIsVirtualGoods() == GoodsConst.IS_VIRTUAL_GOODS_2) {
            Product product = productModel.getProductByProductId(dto.getProductId());
            Goods goods = goodsModel.getGoodsByGoodsId(product.getGoodsId());
            vo.setIsVirtualGoods(goods.getIsVirtualGoods());
            GoodsBindReserveNameExample example = new GoodsBindReserveNameExample();
            example.setGoodsId(goods.getGoodsId());
            example.setOrderBy("reserve_name_id asc");
            List<GoodsBindReserveName> list = goodsBindReserveNameModel.getGoodsBindReserveNameList(example, null);
            vo.setReserveNameList(list);
        }
        return SldResponse.success(vo);
    }

    /**
     * 可用积分列表
     *
     * @param memberIntegral 会员积分
     * @param integralAmount 积分数量
     * @param integralScale  积分档位
     * @return 返回值
     */
    public List<Integer> getIntegralList(Integer memberIntegral, Integer integralAmount, Integer integralScale) {
        List<Integer> list = new ArrayList<>();
        //最多使用积分
        int availableIntegral = Integer.min(memberIntegral / integralScale * integralScale, integralAmount / integralScale * integralScale);

        //可以使用的积分档位数
        int times = availableIntegral / integralScale;
        //最高档位数
        int maxTime = 10;
        if (times <= maxTime) {
            //未达到最高档位
            for (int i = 1; i <= times; i++) {
                list.add(integralScale * i);
            }
        } else {
            //比最高档位多
            //档位间隔
            int space = times / maxTime * integralScale;
            for (int i = maxTime - 1; i >= 0; i--) {
                list.add(availableIntegral - space * i);
            }
        }
        return list;
    }

    /**
     * 预售确认
     *
     * @param dto             dto
     * @param orderSubmitDTO  dto
     * @param singlePromotion 商品活动绑定关系
     * @return 返回值
     */
    public OrderSubmitPageVO preSaleConfirm(OrderSubmitParamDTO dto, OrderSubmitDTO orderSubmitDTO, GoodsPromotion singlePromotion, List<BigDecimal> expressFeeList) {
        //查询预售活动
        Presell presell = presellModel.getPresellByPresellId(singlePromotion.getPromotionId());
        //全款预售
        if (presell.getType() == PreSellConst.PRE_SELL_TYPE_2) {
            //调用活动模块计算优惠
            orderSubmitDTO = promotionCommonModel.orderSubmitCalculationDiscount(orderSubmitDTO, dto.getSource());
        }
        //构造返回数据
        OrderSubmitPageVO vo = new OrderSubmitPageVO(orderSubmitDTO, expressFeeList);
        vo.setPromotionType(PromotionConst.PROMOTION_TYPE_103);
        //定金预售定金付款不计算运费
        if (presell.getType() == PreSellConst.PRE_SELL_TYPE_1) {
            vo.setTotalAmount(new BigDecimal(vo.getTotalAmount()).subtract(expressFeeList.get(0)).toString());
        }
        //查询预售商品
        PresellGoodsExample example = new PresellGoodsExample();
        example.setPresellId(singlePromotion.getPromotionId());
        example.setProductId(dto.getProductId());
        List<PresellGoods> preSellGoodsList = presellGoodsModel.getPresellGoodsList(example, null);
        AssertUtil.notEmpty(preSellGoodsList, "预售商品不存在");
        PresellGoods presellGoods = preSellGoodsList.get(0);

        OrderSubmitPageVO.PresellInfo preSellInfo = new OrderSubmitPageVO.PresellInfo();
        preSellInfo.setPresellId(presell.getPresellId());
        preSellInfo.setType(presell.getType());
        preSellInfo.setPresellState(OrderConst.ORDER_SUB_STATE_101);
        preSellInfo.setDeliverTime(presell.getDeliverTime());
        preSellInfo.setProductId(presellGoods.getProductId());
        preSellInfo.setPresellPrice(presellGoods.getPresellPrice().toString());
        preSellInfo.setFirstMoney(presellGoods.getFirstMoney().multiply(new BigDecimal(dto.getNumber())).toString());
        preSellInfo.setSecondMoney(presellGoods.getSecondMoney().multiply(new BigDecimal(dto.getNumber())).toString());
        if (!StringUtil.isNullOrZero(presellGoods.getFirstExpand())) {
            preSellInfo.setFirstExpand(presellGoods.getFirstExpand().multiply(new BigDecimal(dto.getNumber())).toString());
            preSellInfo.setFinalDiscount(presellGoods.getFirstExpand().multiply(new BigDecimal(dto.getNumber()))
                    .subtract(presellGoods.getFirstMoney().multiply(new BigDecimal(dto.getNumber()))).toString());
        }
        preSellInfo.setRemainStartTime(presell.getRemainStartTime());
        preSellInfo.setFirstPrice(presellGoods.getFirstMoney().toString());
        preSellInfo.setSecondPrice(presellGoods.getSecondMoney().toString());
        vo.setPresellInfo(preSellInfo);
        Goods goods = goodsModel.getGoodsByGoodsId(presellGoods.getGoodsId());
        vo.setIsVatInvoice(goods.getIsVatInvoice() == null || goods.getIsVatInvoice() == GoodsConst.IS_VAT_INVOICE_YES);
        vo.getStoreGroupList().forEach(storeGroupVO -> {
            if (presell.getType() == PreSellConst.PRE_SELL_TYPE_1) {
                storeGroupVO.setGoodsAmount(presellGoods.getFirstMoney().multiply(new BigDecimal(dto.getNumber())));
            }
            storeGroupVO.getProductList().forEach(productVO -> {
                productVO.setPrice(presellGoods.getPresellPrice());
            });
        });
        return vo;
    }

    /**
     * 阶梯团确认
     *
     * @param dto             dto
     * @param orderSubmitDTO  dto
     * @param singlePromotion 商品活动绑定关系
     * @return 返回值
     */
    public OrderSubmitPageVO ladderGroupConfirm(OrderSubmitParamDTO dto, OrderSubmitDTO orderSubmitDTO, GoodsPromotion singlePromotion, List<BigDecimal> expressFeeList) {
        //构造返回数据
        OrderSubmitPageVO vo = new OrderSubmitPageVO(orderSubmitDTO, expressFeeList);
        vo.setTotalAmount(new BigDecimal(vo.getTotalAmount()).subtract(expressFeeList.get(0)).toString());
        vo.setPromotionType(PromotionConst.PROMOTION_TYPE_105);
        //查询阶梯团活动
        LadderGroup ladderGroup = ladderGroupModel.getLadderGroupByGroupId(singlePromotion.getPromotionId());
        //查询阶梯团商品
        LadderGroupGoodsExample example = new LadderGroupGoodsExample();
        example.setGroupId(singlePromotion.getPromotionId());
        example.setProductId(dto.getProductId());
        List<LadderGroupGoods> groupGoodsList = ladderGroupGoodsModel.getLadderGroupGoodsList(example, null);
        AssertUtil.notEmpty(groupGoodsList, "阶梯团商品不存在");
        LadderGroupGoods groupGoods = groupGoodsList.get(0);

        OrderSubmitPageVO.LadderGroupInfo ladderGroupInfo = new OrderSubmitPageVO.LadderGroupInfo();
        ladderGroupInfo.setGroupId(ladderGroup.getGroupId());
        ladderGroupInfo.setLadderGroupState(OrderConst.ORDER_SUB_STATE_101);
        ladderGroupInfo.setProductId(groupGoods.getProductId());
        ladderGroupInfo.setProductPrice(groupGoods.getProductPrice().toString());
        ladderGroupInfo.setAdvanceDeposit(groupGoods.getAdvanceDeposit().multiply(new BigDecimal(dto.getNumber())).toString());
        ladderGroupInfo.setRemainAmount((groupGoods.getProductPrice().subtract(groupGoods.getAdvanceDeposit())).multiply(new BigDecimal(dto.getNumber())).toString());
        ladderGroupInfo.setRemainStartTime(ladderGroup.getEndTime());
        ladderGroupInfo.setFirstPrice(groupGoods.getAdvanceDeposit().toString());
        vo.setLadderGroupInfo(ladderGroupInfo);
        Goods goods = goodsModel.getGoodsByGoodsId(groupGoods.getGoodsId());
        vo.setIsVatInvoice(goods.getIsVatInvoice() == null || goods.getIsVatInvoice() == GoodsConst.IS_VAT_INVOICE_YES);
        vo.getStoreGroupList().forEach(storeGroupVO -> {
            storeGroupVO.setGoodsAmount(groupGoods.getAdvanceDeposit().multiply(new BigDecimal(dto.getNumber())));
            storeGroupVO.getProductList().forEach(productVO -> {
                productVO.setPrice(groupGoods.getProductPrice());
            });
        });
        return vo;
    }

    @PostMapping("submit")
    @ApiOperation("提交订单接口(注：调用此接口之前需先调用[检测购物车信息]接口，state=200才可以调用此接口)")
    @ApiResponses(
            @ApiResponse(code = 200, message = "data.paySn:支付单号;data.needPay:是否需要支付，true==是")
    )
    public JsonResult<Object> submit(HttpServletRequest request, @RequestBody OrderSubmitParamDTO dto) {
        Member member = UserUtil.getUser(request, Member.class);

        if (dto.getIsCart()) {
            AssertUtil.notNullOrZero(dto.getAddressId(), "请选择收货地址");
        } else {
            Product product = productModel.getProductByProductId(dto.getProductId());
            AssertUtil.notNull(product, "商品不存在");
            Goods goods = goodsModel.getGoodsByGoodsId(product.getGoodsId());
            AssertUtil.notNull(goods, "商品不存在");
            if (goods.getIsVirtualGoods() == GoodsConst.IS_VIRTUAL_GOODS_1) {
                AssertUtil.notNullOrZero(dto.getAddressId(), "请选择收货地址");
            }
        }

        PreOrderDTO preOrderDTO = new PreOrderDTO();
        if (!dto.getIsCart()) {
            AssertUtil.isTrue(StringUtil.isNullOrZero(dto.getNumber()) || dto.getNumber() < 0, "购买数量有误");

            //立即购买，查询订单类型、是否计算优惠
            GoodsPromotion singlePromotion = cartModel.getSinglePromotion(dto.getProductId());
            if (singlePromotion != null && !dto.getIsAloneBuy()) {
                preOrderDTO = promotionCommonModel.preOrderSubmit(singlePromotion.getPromotionType(),
                        singlePromotion.getPromotionId(), dto.getProductId(), dto.getNumber(), member.getMemberId());
            }
        }

        //构造入mq对象
        //统一生成支付单号
        String paySn = GoodsIdGenerator.getPaySn();
        //将表标识放入redis，key=标识前缀+paySn，mq处理成功或失败后删除标识
        stringRedisTemplate.opsForValue().set(StarterConfigConst.ORDER_SUBMIT_MQ_REDIS_PREFIX + paySn, "");
        //设置半个小时过期时间
        stringRedisTemplate.expire(StarterConfigConst.ORDER_SUBMIT_MQ_REDIS_PREFIX + paySn, 30 * 60, TimeUnit.SECONDS);
        OrderSubmitMqConsumerDTO consumerDTO = new OrderSubmitMqConsumerDTO();
        consumerDTO.setParamDTO(dto);
        consumerDTO.setMemberId(member.getMemberId());
        consumerDTO.setPaySn(paySn);
        consumerDTO.setPreOrderDTO(preOrderDTO);
        //入队列
        rabbitTemplate.convertAndSend(MQ_EXCHANGE_NAME, MQ_QUEUE_NAME_ORDER_SUBMIT, consumerDTO);

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("paySn", paySn);

        if (preOrderDTO.getOrderType().equals(PromotionConst.PROMOTION_TYPE_104)) {
            log.debug("秒杀订单---------------");
        } else {
            log.debug("普通订单--------------");
        }

        return SldResponse.success(dataMap);
    }

    @PostMapping("cancel")
    @ApiOperation("取消订单接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "parentSn", value = "父订单号", required = true, paramType = "query"),
            @ApiImplicitParam(name = "reasonId", value = "取消原因", required = true, paramType = "query")
    })
    public JsonResult<Object> cancelOrder(HttpServletRequest request, String parentSn, Integer reasonId) {
        Member member = UserUtil.getUser(request, Member.class);

        //订单信息
        OrderExample orderExample = new OrderExample();
        orderExample.setParentSn(parentSn);
        orderExample.setMemberId(member.getMemberId());
        orderExample.setOrderState(OrderConst.ORDER_STATE_10);
        List<Order> orderList = orderModel.getOrderList(orderExample, null);

        AssertUtil.notEmpty(orderList, "请选择要取消的订单");

        //查询取消原因
        Reason reason = reasonModel.getReasonByReasonId(reasonId);
        AssertUtil.notNull(reason, "取消原因不存在");

        //订单取消
        orderModel.cancelOrder(orderList, reason.getContent(), null, OrderConst.LOG_ROLE_MEMBER, Long.valueOf(member.getMemberId()), member.getMemberName(), "会员取消订单");

        return SldResponse.success("订单取消成功");
    }

    @PostMapping("receive")
    @ApiOperation("确认收货接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderSn", value = "订单号", required = true, paramType = "query")
    })
    public JsonResult<Object> receive(HttpServletRequest request, String orderSn) {
        Member member = UserUtil.getUser(request, Member.class);

        //订单信息
        Order order = orderModel.getOrderByOrderSn(orderSn);
        AssertUtil.isTrue(!order.getMemberId().equals(member.getMemberId()), "不能操作他人的订单");
        AssertUtil.isTrue(order.getLockState() > 0, "售后中的订单无法操作");


        //确认收货
        orderModel.receiveOrder(order, OrderConst.LOG_ROLE_MEMBER, Long.valueOf(member.getMemberId()), member.getMemberName(), "会员确认收货");

        return SldResponse.success("确认收货成功");
    }

    @ApiOperation("删除订单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderSn", value = "订单号", required = true, paramType = "query")
    })
    @PostMapping("delete")
    public JsonResult<Object> deleteOrder(HttpServletRequest request, String orderSn) {
        Member member = UserUtil.getUser(request, Member.class);

        //订单信息
        Order order = orderModel.getOrderByOrderSn(orderSn);
        AssertUtil.isTrue(!order.getMemberId().equals(member.getMemberId()), "您无权删除他人订单");
        AssertUtil.isTrue(!order.getOrderState().equals(OrderConst.ORDER_STATE_0)
                && !order.getOrderState().equals(OrderConst.ORDER_STATE_40)
                && !order.getOrderState().equals(OrderConst.ORDER_STATE_50), "您无权删除他人订单");
        //修改订单状态
        Order updateOrder = new Order();
        updateOrder.setDeleteState(OrderConst.DELETE_STATE_1);
        OrderExample example = new OrderExample();
        example.setOrderSn(orderSn);
        orderModel.updateOrderByExample(updateOrder, example);
        return SldResponse.success("删除成功");
    }

    @ApiOperation("修改地址")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderSn", value = "订单号", required = true, paramType = "query"),
            @ApiImplicitParam(name = "addressId", value = "地址id", required = true, paramType = "query")
    })
    @PostMapping("updateAddress")
    public JsonResult<Object> updateAddress(HttpServletRequest request, String orderSn, Integer addressId) {
        Member member = UserUtil.getUser(request, Member.class);

        Order order = orderModel.getOrdersWithOpByOrderSn(orderSn);
        AssertUtil.isTrue(!order.getMemberId().equals(member.getMemberId()), "您无权修改他人订单");
        AssertUtil.isTrue(!order.getOrderState().equals(OrderConst.ORDER_STATE_10)
                && !order.getOrderState().equals(OrderConst.ORDER_STATE_20), "该订单地址无法修改");
        AssertUtil.isTrue(order.getIsVirtualGoods() != GoodsConst.IS_VIRTUAL_GOODS_1, "非法操作");

        MemberAddress memberAddress = memberAddressModel.getMemberAddressByAddressId(addressId);
        AssertUtil.notNull(memberAddress, "地址不存在");

        //构造计算运费的dto
        CalculateExpressDTO calculateExpressDTO = new CalculateExpressDTO();
        calculateExpressDTO.setStoreId(order.getStoreId());
        calculateExpressDTO.setCityCode(memberAddress.getCityCode());
        for (OrderProduct orderProduct : order.getOrderProductList()) {
            //赠品不计算运费
            if (orderProduct.getIsGift() == OrderConst.IS_GIFT_YES) {
                continue;
            }
            calculateExpressDTO.getProductList()
                    .add(new CalculateExpressDTO.ProductInfo(
                            orderProduct.getGoodsId(),
                            orderProduct.getProductId(),
                            orderProduct.getProductShowPrice(),
                            orderProduct.getProductNum()));
        }
        //计算新运费
        BigDecimal totalFee = goodsFreightTemplateModel.calculateExpressFee(calculateExpressDTO);
        AssertUtil.isTrue(totalFee.compareTo(order.getExpressFee()) != 0, "地址修改后运费发生变化，暂不支持修改");

        orderModel.updateAddress(memberAddress, order.getParentSn());
        return SldResponse.success("修改成功");
    }

    @ApiOperation("定金预售和阶梯团订单尾款确认")
    @PostMapping("balanceConfirm")
    public JsonResult<OrderSubmitPageVO> balanceConfirm(HttpServletRequest request, @RequestBody OrderSubmitParamDTO dto) {
        Member member = UserUtil.getUser(request, Member.class);

        Order order = orderModel.getOrderByOrderSn(dto.getOrderSn());

        //构造计算优惠dto
        OrderSubmitDTO orderSubmitDTO = orderSubmitUtil.getOrderSubmitDTO(dto, member.getMemberId(), order);

        //调用活动模块计算优惠
        orderSubmitDTO = promotionCommonModel.orderSubmitCalculationDiscount(orderSubmitDTO, dto.getSource());

        //计算运费
        List<BigDecimal> expressFeeList = new ArrayList<>();
        BigDecimal expressFee = order.getExpressFee();
        expressFeeList.add(expressFee);

        //构造返回数据
        OrderSubmitPageVO vo = new OrderSubmitPageVO(orderSubmitDTO, expressFeeList);
        if (order.getOrderType() == PromotionConst.PROMOTION_TYPE_103) {
            vo.setPromotionType(PromotionConst.PROMOTION_TYPE_103);
            vo = preSaleBalanceConfirm(vo, order.getOrderSn());
        } else if (order.getOrderType() == PromotionConst.PROMOTION_TYPE_105) {
            vo.setPromotionType(PromotionConst.PROMOTION_TYPE_105);
            vo = ladderGroupBalanceConfirm(vo, order.getOrderSn());
            if (StringUtil.isEmpty(vo.getLadderGroupInfo().getRemainAmount())) {
                return SldResponse.failSpecial(vo);
            }
        }
        //积分抵现开关是否开启
        String isEnable = stringRedisTemplate.opsForValue().get("integral_cash_out_is_enable");
        if (!StringUtil.isEmpty(isEnable) && "1".equals(isEnable)) {
            //查询数据库会员信息
            Member memberDb = memberModel.getMemberByMemberId(member.getMemberId());
            AssertUtil.notNull(memberDb, "会员不存在");
            vo.setMemberIntegral(memberDb.getMemberIntegral());
            //获取多少积分换算成1元
            String conversionRatio = stringRedisTemplate.opsForValue().get("integral_conversion_ratio");
            int integralScale = StringUtil.isEmpty(conversionRatio) ? 100 : Integer.parseInt(conversionRatio);
            vo.setIntegralScale(integralScale);
            //合计
            BigDecimal totalAmount = new BigDecimal(vo.getTotalAmount());
            //合计-运费
            for (BigDecimal fee : expressFeeList) {
                totalAmount = totalAmount.subtract(fee);
            }
            //积分抵扣金额
            BigDecimal integralCashAmount = new BigDecimal("0.00");
            if (!StringUtil.isNullOrZero(dto.getIntegral())) {
                //积分换算成金额的总值
                integralCashAmount = integralCashAmount.add(BigDecimal.valueOf(dto.getIntegral()).divide(new BigDecimal(integralScale), 2, RoundingMode.HALF_UP));
            }
            totalAmount = totalAmount.add(integralCashAmount);
            //订单金额达到积分最低使用金额后，才可以使用使用积分抵扣现金
            String useLowestAmount = stringRedisTemplate.opsForValue().get("integral_use_lowest_amount");
            BigDecimal lowestAmount = StringUtil.isEmpty(useLowestAmount) ? totalAmount : new BigDecimal(useLowestAmount);
            if (totalAmount.compareTo(lowestAmount) >= 0) {
                //最高抵扣金额,例如：商品金额为200元，最高抵扣上限为90%，则积分最多可抵扣180元
                String deductRate = stringRedisTemplate.opsForValue().get("integral_max_deduct_rate");
                int maxDeductRate = StringUtil.isEmpty(deductRate) || Integer.parseInt(deductRate) > 100 ? 100 : Integer.parseInt(deductRate);
                //积分金额
                Integer integralAmount = totalAmount.multiply(new BigDecimal(maxDeductRate)).multiply(new BigDecimal(integralScale))
                        .divide(new BigDecimal("100.00"), 2, RoundingMode.HALF_UP).intValue();
                vo.setIntegralList(this.getIntegralList(memberDb.getMemberIntegral(), integralAmount, integralScale));
            }
        }
        //如果是虚拟商品，返回预留信息
        if (order.getIsVirtualGoods() == GoodsConst.IS_VIRTUAL_GOODS_2) {
            vo.setIsVirtualGoods(order.getIsVirtualGoods());
            OrderReserveExample example = new OrderReserveExample();
            example.setOrderSn(dto.getOrderSn());
            example.setOrderBy("reserve_id asc");
            List<OrderReserve> list = orderReserveModel.getOrderReserveList(example, null);
            vo.setOrderReserveList(list);
        }
        return SldResponse.success(vo);
    }

    /**
     * 预售尾款确认
     *
     * @param vo      提交订单页展示信息vo
     * @param orderSn orderSn
     * @return 返回值
     */
    public OrderSubmitPageVO preSaleBalanceConfirm(OrderSubmitPageVO vo, String orderSn) {
        //查询预售订单扩展信息
        PresellOrderExtendExample example = new PresellOrderExtendExample();
        example.setOrderSn(orderSn);
        List<PresellOrderExtend> orderExtendList = presellOrderExtendModel.getPresellOrderExtendList(example, null);
        AssertUtil.notEmpty(orderExtendList, "获取预售订单扩展信息为空");
        PresellOrderExtend orderExtend = orderExtendList.get(0);

        OrderSubmitPageVO.PresellInfo preSellInfo = new OrderSubmitPageVO.PresellInfo();
        preSellInfo.setPresellId(orderExtend.getPresellId());
        if (orderExtend.getIsAllPay() == OrderConst.IS_ALL_PAY_1) {
            preSellInfo.setType(PreSellConst.PRE_SELL_TYPE_2);
        } else {
            preSellInfo.setType(PreSellConst.PRE_SELL_TYPE_1);
        }
        preSellInfo.setPresellState(orderExtend.getOrderSubState());
        preSellInfo.setDeliverTime(orderExtend.getDeliverTime());
        preSellInfo.setProductId(orderExtend.getProductId());
        preSellInfo.setPresellPrice(StringUtil.isNullOrZero(orderExtend.getPresellPrice()) ? null : orderExtend.getPresellPrice().toString());
        preSellInfo.setFirstMoney(orderExtend.getDepositAmount().multiply(new BigDecimal(orderExtend.getProductNum())).toString());
        if (!StringUtil.isNullOrZero(orderExtend.getFirstExpand())) {
            preSellInfo.setFirstExpand(orderExtend.getFirstExpand().multiply(new BigDecimal(orderExtend.getProductNum())).toString());
            preSellInfo.setFinalDiscount((orderExtend.getFirstExpand().subtract(orderExtend.getDepositAmount())).multiply(new BigDecimal(orderExtend.getProductNum())).toString());
        }
        preSellInfo.setSecondMoney(StringUtil.isNullOrZero(orderExtend.getRemainAmount()) ? null : orderExtend.getRemainAmount().multiply(new BigDecimal(orderExtend.getProductNum())).toString());
        preSellInfo.setFirstPrice(orderExtend.getDepositAmount().toString());
        preSellInfo.setSecondPrice(StringUtil.isNullOrZero(orderExtend.getRemainAmount()) ? null : orderExtend.getRemainAmount().toString());
        vo.setPresellInfo(preSellInfo);
        Goods goods = goodsModel.getGoodsByGoodsId(orderExtend.getGoodsId());
        vo.setIsVatInvoice(goods.getIsVatInvoice() == null || goods.getIsVatInvoice() == GoodsConst.IS_VAT_INVOICE_YES);
        vo.getStoreGroupList().forEach(storeGroupVO -> {
            storeGroupVO.setGoodsAmount(orderExtend.getPresellPrice().multiply(new BigDecimal(orderExtend.getProductNum())));
            storeGroupVO.getProductList().forEach(productVO -> {
                productVO.setPrice(orderExtend.getPresellPrice());
            });
        });
        return vo;
    }

    /**
     * 阶梯团尾款确认
     *
     * @param vo      提交订单页展示信息vo
     * @param orderSn orderSn
     * @return 返回值
     */
    public OrderSubmitPageVO ladderGroupBalanceConfirm(OrderSubmitPageVO vo, String orderSn) {
        //查询阶梯团订单扩展信息
        LadderGroupOrderExtendExample example = new LadderGroupOrderExtendExample();
        example.setOrderSn(orderSn);
        List<LadderGroupOrderExtend> orderExtendList = ladderGroupOrderExtendModel.getLadderGroupOrderExtendList(example, null);
        AssertUtil.notEmpty(orderExtendList, "获取阶梯团订单扩展信息为空");
        LadderGroupOrderExtend orderExtend = orderExtendList.get(0);

        OrderSubmitPageVO.LadderGroupInfo ladderGroupInfo = new OrderSubmitPageVO.LadderGroupInfo();
        ladderGroupInfo.setGroupId(orderExtend.getGroupId());
        ladderGroupInfo.setLadderGroupState(orderExtend.getOrderSubState());
        ladderGroupInfo.setProductId(orderExtend.getProductId());
        ladderGroupInfo.setProductPrice(orderExtend.getProductPrice().toString());
        ladderGroupInfo.setAdvanceDeposit(orderExtend.getAdvanceDeposit().multiply(new BigDecimal(orderExtend.getProductNum())).toString());
        ladderGroupInfo.setRemainAmount(orderExtend.getRemainAmount().multiply(new BigDecimal(orderExtend.getProductNum())).toString());
        if (orderExtend.getOrderSubState() != LadderGroupConst.ORDER_SUB_STATE_1) {
            if (!StringUtils.isEmpty(vo.getTotalDiscount()) && !StringUtil.isNullOrZero(new BigDecimal(vo.getTotalDiscount()))) {
                ladderGroupInfo.setRealRemainAmount(new BigDecimal(ladderGroupInfo.getRemainAmount()).subtract(new BigDecimal(vo.getTotalDiscount())).toString());
            } else {
                ladderGroupInfo.setRealRemainAmount(ladderGroupInfo.getRemainAmount());
            }
        }
        ladderGroupInfo.setRemainStartTime(orderExtend.getRemainStartTime());
        ladderGroupInfo.setFirstPrice(orderExtend.getAdvanceDeposit().toString());
        ladderGroupInfo.setSecondPrice(orderExtend.getRemainAmount().toString());
        vo.setLadderGroupInfo(this.getLadderGroupInfo(ladderGroupInfo, orderExtend));
        Goods goods = goodsModel.getGoodsByGoodsId(orderExtend.getGoodsId());
        vo.setIsVatInvoice(goods.getIsVatInvoice() == null || goods.getIsVatInvoice() == GoodsConst.IS_VAT_INVOICE_YES);
        vo.getStoreGroupList().forEach(storeGroupVO -> {
            storeGroupVO.setGoodsAmount(orderExtend.getProductPrice().multiply(new BigDecimal(orderExtend.getProductNum())));
            storeGroupVO.getProductList().forEach(productVO -> {
                productVO.setPrice(orderExtend.getProductPrice());
            });
        });
        return vo;
    }

    /**
     * 获取阶梯团活动信息
     *
     * @param ladderGroupInfo 阶梯团活动信息
     * @return 阶梯团活动信息
     */
    public OrderSubmitPageVO.LadderGroupInfo getLadderGroupInfo(OrderSubmitPageVO.LadderGroupInfo ladderGroupInfo, LadderGroupOrderExtend orderExtend) {
        //查询阶梯规则
        LadderGroupRuleExample ruleExample = new LadderGroupRuleExample();
        ruleExample.setGroupId(ladderGroupInfo.getGroupId());
        List<LadderGroupRule> groupRuleList = ladderGroupRuleModel.getLadderGroupRuleList(ruleExample, null);
        if (CollectionUtils.isEmpty(groupRuleList)) {
            //没有规则说明数据有问题
            return ladderGroupInfo;
        }
        //查询生成尾款的阶梯团订单
        LadderGroupOrderExtendExample example = new LadderGroupOrderExtendExample();
        example.setGroupId(ladderGroupInfo.getGroupId());
        example.setExecuteState(LadderGroupConst.EXECUTE_STATE_1);
        List<LadderGroupOrderExtend> list = ladderGroupOrderExtendModel.getLadderGroupOrderExtendList(example, null);
        //满足阶梯等级,0代表一个规则也没有满足，不享受优惠
        Integer ladderLevel = 0;
        for (LadderGroupRule groupRule : groupRuleList) {
            if (list.size() >= groupRule.getJoinGroupNum()) {
                ladderLevel = groupRule.getLadderLevel();
                break;
            }
        }
        ladderGroupInfo.setLadderLevel(ladderLevel);
        //优惠金额=原价-定金-尾款
        BigDecimal discount = orderExtend.getProductPrice().subtract(orderExtend.getAdvanceDeposit()).subtract(orderExtend.getRemainAmount());
        ladderGroupInfo.setDiscount(discount.multiply(new BigDecimal(orderExtend.getProductNum())).toString());
        return ladderGroupInfo;
    }

    @ApiOperation("尾款提交订单接口")
    @ApiResponses(
            @ApiResponse(code = 200, message = "data.paySn:支付单号")
    )
    @PostMapping("balanceSubmit")
    public JsonResult<Object> balanceSubmit(HttpServletRequest request, @RequestBody OrderSubmitParamDTO dto) {
        AssertUtil.notEmpty(dto.getOrderSn(), "订单号不能为空");

        Member member = UserUtil.getUser(request, Member.class);

        //对用户加锁，防止同一用户在不同客户端重复使用个人资产
        String memberLockKey = LockKeyConst.MEMBER_ASSET_PREFIX + member.getMemberId();
        slodonLock.lock(memberLockKey);

        try {
            Order order = orderModel.getOrderByOrderSn(dto.getOrderSn());

            //构造计算优惠dto
            OrderSubmitDTO orderSubmitDTO = orderSubmitUtil.getOrderSubmitDTO(dto, member.getMemberId(), order);

            //调用活动模块计算优惠
            orderSubmitDTO = promotionCommonModel.orderSubmitCalculationDiscount(orderSubmitDTO, dto.getSource());

            orderModel.submitBalanceOrder(orderSubmitDTO, dto, member, order);

            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("paySn", order.getPaySn());
            return SldResponse.success(dataMap);
        } finally {
            slodonLock.unlock(memberLockKey);
        }
    }
}
