package cn.freemud.service.universal;

import cn.freemud.adapter.CreateOrderAdapter;
import cn.freemud.amqp.Header;
import cn.freemud.amqp.MQAction;
import cn.freemud.amqp.MQMessage;
import cn.freemud.amqp.MQService;
import cn.freemud.base.entity.BaseResponse;
import cn.freemud.base.util.DateUtil;
import cn.freemud.constant.RedisKeyConstant;
import cn.freemud.constant.ResponseCodeConstant;
import cn.freemud.entities.bo.CreateOrderBo;
import cn.freemud.entities.dto.SvcComPayRequestDto;
import cn.freemud.entities.dto.SvcComPayResponseDto;
import cn.freemud.entities.dto.order.CreateOrderOperateDto;
import cn.freemud.entities.dto.order.CreatePrepayRequestDto;
import cn.freemud.entities.dto.pay.CombPayResponse;
import cn.freemud.entities.dto.pay.OrderPayResponse;
import cn.freemud.entities.dto.pay.PaymentMqMessageDto;
import cn.freemud.entities.dto.shoppingCart.ActivityDiscountsDto;
import cn.freemud.entities.dto.shoppingCart.ShoppingCartGoodsDto;
import cn.freemud.entities.vo.*;
import cn.freemud.enums.*;
import cn.freemud.interceptor.ServiceException;
import cn.freemud.redis.RedisCache;
import cn.freemud.service.CheckOrderUniversal;
import cn.freemud.service.OrderAdapterService;
import cn.freemud.service.impl.PaymentQueueService;
import cn.freemud.service.thirdparty.ComPayClient;
import cn.freemud.service.thirdparty.SvcComPayClient;
import cn.freemud.service.universal.factory.CreateOrderServiceFactory;
import cn.freemud.utils.AppLogUtil;
import cn.freemud.utils.RedisUtil;
import cn.freemud.utils.ResponseUtil;
import cn.freemud.utils.ValidationCode;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.freemud.api.assortment.datamanager.entity.db.AssortmentOpenPlatformIappWxappStore;
import com.freemud.api.assortment.datamanager.entity.db.AssortmentOpenPlatformPartner;
import com.freemud.api.assortment.datamanager.entity.db.AssortmentOpenPlatformPartnerConfig;
import com.freemud.api.assortment.datamanager.entity.db.AssortmentOpenPlatformWxapp;
import com.freemud.api.assortment.datamanager.entity.vo.AssortmentCustomerInfoVo;
import com.freemud.api.assortment.datamanager.manager.AssortmentOpenPlatformIappWxappStoreManager;
import com.freemud.api.assortment.datamanager.manager.AssortmentOpenPlatformPartnerConfigManager;
import com.freemud.api.assortment.datamanager.manager.AssortmentOpenPlatformPartnerManager;
import com.freemud.api.assortment.datamanager.manager.AssortmentOpenPlatformWxappManager;
import com.freemud.application.sdk.api.constant.ResponseResultEnum;
import com.freemud.application.sdk.api.couponcenter.offline.request.CouponLockRequest;
import com.freemud.application.sdk.api.couponcenter.offline.response.CouponLockResponse;
import com.freemud.application.sdk.api.couponcenter.offline.service.OfflineCouponSdkService;
import com.freemud.application.sdk.api.log.LogThreadLocal;
import com.freemud.application.sdk.api.log.ThirdPartyLog;
import com.freemud.application.sdk.api.membercenter.request.UserScoreRequest;
import com.freemud.application.sdk.api.membercenter.service.MemberScoreService;
import com.freemud.application.sdk.api.ordercenter.entities.v1.OrderBeanV1;
import com.freemud.application.sdk.api.ordercenter.entities.v1.ProductBeanV1;
import com.freemud.application.sdk.api.ordercenter.enums.AfterSalesType;
import com.freemud.application.sdk.api.ordercenter.enums.OrderClientType;
import com.freemud.application.sdk.api.ordercenter.enums.PayChannelType;
import com.freemud.application.sdk.api.ordercenter.request.OrderCancelReq;
import com.freemud.application.sdk.api.ordercenter.request.OrderExtInfoDto;
import com.freemud.application.sdk.api.ordercenter.request.create.CreateOrderRequest;
import com.freemud.application.sdk.api.ordercenter.request.create.OrderTaskReq;
import com.freemud.application.sdk.api.ordercenter.response.OrderBaseResp;
import com.freemud.application.sdk.api.ordercenter.response.orderInfo.OrderPayItemResp;
import com.freemud.application.sdk.api.ordercenter.response.orderInfo.OrderInfoReqs;
import com.freemud.application.sdk.api.ordercenter.service.OrderSdkService;
import com.freemud.application.sdk.api.paymentcenter.client.request.CodePayRequest;
import com.freemud.application.sdk.api.paymentcenter.client.request.SVCCardAmountRequest;
import com.freemud.application.sdk.api.paymentcenter.client.response.CodePayResponse;
import com.freemud.application.sdk.api.paymentcenter.client.response.SVCCardAmountResponse;
import com.freemud.application.sdk.api.paymentcenter.client.service.PaymentNewService;
import com.freemud.application.sdk.api.promotioncenter.dto.promotion.GoodsStockDTO;
import com.freemud.application.sdk.api.promotioncenter.request.promotion.ActivityUpdateStockRequest;
import com.freemud.application.sdk.api.promotioncenter.service.PromotionSdkService;
import com.freemud.application.sdk.api.stockapi.request.UpdateStocksRequest;
import com.freemud.application.sdk.api.stockapi.service.StockSdkService;
import com.freemud.application.sdk.api.storecenter.request.StoreInfoRequest;
import com.freemud.application.sdk.api.storecenter.response.StoreResponse;
import com.freemud.application.sdk.api.storecenter.service.StoreCenterService;
import com.freemud.sdk.api.assortment.order.adapter.OrderSdkAdapter;
import com.freemud.sdk.api.assortment.order.enums.AutoOrderConfigTime;
import com.freemud.sdk.api.assortment.order.enums.StockChangeType;
import com.freemud.sdk.api.assortment.order.request.order.*;
import com.freemud.sdk.api.assortment.order.request.payment.CombPayRequest;
import com.freemud.sdk.api.assortment.order.response.order.BaseOrderResponse;
import com.freemud.sdk.api.assortment.order.response.order.CreateOrderResponse;
//import com.freemud.sdk.api.assortment.order.response.payment.OrderPayResponse;
import com.freemud.sdk.api.assortment.order.service.OrderCenterSdkService;
import com.freemud.sdk.api.assortment.payment.request.UnifiedOrderRequest;
import com.freemud.sdk.api.assortment.payment.response.UnifiedOrderResponse;
import com.freemud.sdk.api.assortment.payment.service.StandardPaymentService;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.freemud.sdk.api.assortment.order.domain.ResponseCodeConstant.RESPONSE_SUCCESS;
import static com.freemud.sdk.api.assortment.order.domain.ResponseCodeConstant.RESPONSE_SUCCESS_STR;

/**
 * 订单服务
 *
 * @author liming.guo
 * @date 2018/05/08
 */
@Service
public abstract class UniversalOrderService {

    @Autowired
    private SvcComPayClient svcComPayClient;
    @Autowired
    private ComPayClient comPayClient;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    protected CreateOrderAdapter createOrderAdapter;
    @Autowired
    private OrderAdapterService orderAdapterService;
    @Autowired
    protected AssortmentOpenPlatformWxappManager openPlatformWxappManager;
    @Autowired
    private AssortmentOpenPlatformPartnerConfigManager openPlatformPartnerConfigManager;
    @Autowired
    private PaymentNewService paymentNewService;
    @Autowired
    protected StandardPaymentService standardPaymentService;
    @Autowired
    private PaymentQueueService paymentQueueService;
    @Autowired
    private MQService mqService;
    @Autowired
    private AssortmentOpenPlatformPartnerManager assortmentOpenPlatformPartnerManager;
    @Autowired
    private AssortmentOpenPlatformIappWxappStoreManager assortmentOpenPlatformIappWxappStoreManager;
    @Autowired
    private OrderSdkService orderSdkService;
    @Autowired
    private OrderSdkAdapter orderSdkAdapter;
    @Autowired
    private StoreCenterService storeCenterService;
    @Autowired
    private StockSdkService stockSdkService;
    @Autowired
    private PromotionSdkService promotionSdkService;
    @Autowired
    protected OrderCenterSdkService orderCenterSdkService;
    @Autowired
    private MemberScoreService memberScoreService;
    @Autowired
    private OfflineCouponSdkService offlineCouponSdkService;
    @Autowired
    private CreateOrderServiceFactory createOrderServiceFactory;
    @Value("${saas.reverseNotifyiDcUrl}")
    protected String reverseNotifyiDcUrl;
    @Value("${collage.pre_order_pay.goods_tag}")
    private String collageGoodsTag;
    @Value("${collage.pre_order_pay.goods_tag_list}")
    private String collageGoodsTagList;
    @Value("${program.backorders_change_order_status_consumer_queue}")
    protected String backOrdersChangeOrderStatusConsumerQueue;
    @Value("${program.backorders_notify_activity_exchange}")
    protected String backOrdersNotifyActivityExchange;

    private CheckOrderUniversal checkOrder;

    /**
     * 创建订单
     */
    public final BaseResponse createOrder(CreateOrderBaseVo createOrderBaseVo, AssortmentCustomerInfoVo userLoginInfoDto, CreateOrderSceneEnum createOrderSceneEnum) {
        String trackingNo = LogThreadLocal.getTrackingNo();
        //参数校验是否满足 标签注解
        validParam(createOrderBaseVo);

        // 组装业务对象
        CreateOrderBo createOrderBo = getCreateOrderBo(createOrderBaseVo, userLoginInfoDto, trackingNo);
        createOrderBo.setCreateOrderScene(createOrderSceneEnum);
//        AppLogUtil.infoLog("组装业务对象 begin:", createOrderBo,null);

        // 获取校验对象
        checkOrder = createOrderServiceFactory.getCheckOrderService(createOrderSceneEnum);
        checkOrder.checkCreateOrderParam(createOrderBo, trackingNo);

        // 额外信息字段对象存放
        getExtInfo(createOrderBo);

        //1.9.2套餐需求同步优化创建订单代码
        BaseResponse createOrderOperateDtoResponse = this.createSharedOrder(createOrderBo);
//        AppLogUtil.infoLog("组装业务对象  end:", createOrderBo, createOrderOperateDtoResponse);
        if (createOrderOperateDtoResponse == null || !ResponseResult.SUCCESS.getCode().equals(createOrderOperateDtoResponse.getCode()) || createOrderOperateDtoResponse.getResult() == null) {
            return createOrderOperateDtoResponse;
        }

        // 是否 自己拉起支付
        if (!defaultPrepayOrder()) {
            return createOrderOperateDtoResponse;
        }

        CreateOrderOperateDto response = new CreateOrderOperateDto();
        OrderBeanV1 productBeanListOne = (OrderBeanV1) createOrderOperateDtoResponse.getResult();
        long totalAmount = ((OrderBeanV1) createOrderOperateDtoResponse.getResult()).getAmount();
        response.setTotalAmount(totalAmount);
        response.setCardAmount(0);
        response.setFatherOrderBean(productBeanListOne);
        response.setProductOrderBean(productBeanListOne);
        createOrderBo.setCreateOrderOperateDto(response);

        //创建预支付
        CreatePrepayRequestDto createPrepayRequestDto = createOrderAdapter.convertToCreatePrepayRequestDto(createOrderBo);

        BaseResponse prepayOrder = createPrepayOrder(createPrepayRequestDto);
//        AppLogUtil.infoLog("创建预支付:", createPrepayRequestDto, prepayOrder);
        return prepayOrder;
    }

    /**
     * 是否需要拉起小程序 预支付逻辑
     * 默认 true
     * 商城有自己的逻辑 不走默认拉起支付
     * app 有其他逻辑 不拉起预支付
     *
     * @return
     */
    protected boolean defaultPrepayOrder() {
        return true;
    }

    /**
     * 组装业务对象
     *
     * @param createOrderBaseVo
     * @param userLoginInfoDto
     * @param trackingNo
     * @return
     */
    private CreateOrderBo getCreateOrderBo(CreateOrderBaseVo createOrderBaseVo, AssortmentCustomerInfoVo userLoginInfoDto, String trackingNo) {
        CreateOrderBo createOrderBo = new CreateOrderBo();
        // 普通对象 组装参数
        getUniversalCreateOrderBo(createOrderBo, createOrderBaseVo, userLoginInfoDto);
        // 特殊业务对象 组装参数
        getCustomCreateOrderBo(createOrderBo);
        return createOrderBo;
    }

    private void getUniversalCreateOrderBo(CreateOrderBo createOrderBo, CreateOrderBaseVo createOrderBaseVo, AssortmentCustomerInfoVo userLoginInfoDto) {
        createOrderBaseVo.setUserId(userLoginInfoDto.getMemberId());
        createOrderBaseVo.setSessionKey(userLoginInfoDto.getSessionKey());
        createOrderBo.setCreateOrderBaseVo(createOrderBaseVo);
        createOrderBo.setUserLoginInfoDto(userLoginInfoDto);

    }

    /**
     * 特殊业务对象 组装参数
     *
     * @param createOrderBo
     */
    public abstract void getCustomCreateOrderBo(CreateOrderBo createOrderBo);

    /**
     * 利用 HibernateValidator 校验  标签参数是否传递
     *
     * @param createOrderBaseVo
     */
    public abstract void validParam(CreateOrderBaseVo createOrderBaseVo);

    /**
     * 通用代码 创建
     * 发送请求 /order/v2/create
     *
     * @param createOrderBo
     * @return
     */
    protected BaseResponse createSharedOrder(CreateOrderBo createOrderBo) {
        CreateOrderRequest request = absGetCreateOrderRequest(createOrderBo);
        if (request == null) {
            return ResponseUtil.error(ResponseResult.PARAMETER_MISSING);
        }
        return this.sendCreateOrder(request, createOrderBo);
    }

    /**
     * 发送 /order/v2/create 基础服务请求
     * 可通过业务 diy 改造
     *
     * @param request
     * @param createOrderBo
     * @return
     */
    protected BaseResponse sendCreateOrder(CreateOrderRequest request, CreateOrderBo createOrderBo) {
        OrderBaseResp<OrderInfoReqs> order = orderSdkService.createOrder(request, LogThreadLocal.getTrackingNo());
        CreateOrderResponse createOrderResponse = orderSdkAdapter.convent2NEWOrderInfoReqs(order);
        if (!RESPONSE_SUCCESS.equals(createOrderResponse.getErrcode()) || createOrderResponse.getData() == null) {
            return ResponseUtil.error(createOrderResponse.getErrcode().toString(), createOrderResponse.getErrmsg(), null);
        }
        // 初始化扣减活动库存请求对象  可选逻辑  这里有公用逻辑 需要提取
        BaseResponse baseOrderResponse = this.sendActivityUpdateStock(createOrderBo, createOrderResponse);
        if (baseOrderResponse != null) {
            return baseOrderResponse;
        }
        return ResponseUtil.success(createOrderResponse.getData());
    }

    /**
     * 初始化扣减活动库存请求对象
     *
     * @param createOrderBo
     * @param createOrderResponse
     * @return 不需要实现&正常逻辑  默认返回null  --> 请求失败或者其他情况 请返回实体类
     */
    protected BaseResponse sendActivityUpdateStock(CreateOrderBo createOrderBo, CreateOrderResponse createOrderResponse) {
        // 子类 自身业务实现, 下面提供一个默认 公共实现
        return null;
    }

    /**
     * 扣减库存默认实现 子类按需求 提取
     *
     * @param createOrderBo
     * @param createOrderResponse
     * @return
     */
    protected BaseResponse defalutSendActivityUpdateStock(CreateOrderBo createOrderBo, CreateOrderResponse createOrderResponse) {
        ActivityUpdateStockRequest activityUpdateStockRequest = this.initActivityUpdateStockRequest(createOrderBo);
        MqMessageRequest mqMessageRequest = new MqMessageRequest();
        mqMessageRequest.setBackOrdersNotifyActivityExchange(backOrdersNotifyActivityExchange);
        mqMessageRequest.setBackOrdersNotifyActivityQueue(backOrdersChangeOrderStatusConsumerQueue);
        BaseOrderResponse baseOrderResponse = this.updateStockAndScore(createOrderResponse.getData(), createOrderBo.getShoppingCartGoodsDto(),
                activityUpdateStockRequest, mqMessageRequest, createOrderBo.getCreateOrderBaseVo().getMenuType(), createOrderBo.getCreateOrderBaseVo().getUseCustomerScore());
        if (!ResponseCodeConstant.RESPONSE_SUCCESS.equals(baseOrderResponse.getErrcode())) {
            return ResponseUtil.error(baseOrderResponse.getErrcode().toString(), baseOrderResponse.getErrmsg(), null);
        }
        return null;
    }

    /**
     * 拼接发送 创建订单请求对象
     *
     * @param createOrderBo
     * @return
     */
    protected abstract CreateOrderRequest absGetCreateOrderRequest(CreateOrderBo createOrderBo);

    protected BaseOrderResponse updateStockAndScore(OrderBeanV1 orderBean,
                                                    ShoppingCartGoodsDto shoppingCartGoodsDto,
                                                    ActivityUpdateStockRequest updateStockRequest,
                                                    MqMessageRequest mqMessageRequest,
                                                    String menuType,
                                                    Integer useCustomerScore) {
        //TODO 转换获取库存信息,扣减库存
        UpdateStocksRequest updateProductStock = createOrderAdapter.getUpdateProductStock(orderBean, shoppingCartGoodsDto.getProducts(), StockChangeType.REDUCE, menuType);
        BaseOrderResponse baseResponse = reducesStocks(updateProductStock, orderBean.getOid(), orderBean.getCompanyId());
        if (baseResponse != null) {
            // 表示 减库存失败
            return baseResponse;
        }

        //TODO 扣减活动库存
        ActivityUpdateStockRequest activityUpdateStockRequest = orderSdkAdapter.convert2ActivityUpdateStockRequest(orderBean, updateStockRequest);
        baseResponse = this.reducesActivityStocks(activityUpdateStockRequest, orderBean, mqMessageRequest);
        if (baseResponse != null) {
            // 表示 操作失败
            return baseResponse;
        }

        // 扣减积分，扣减失败冲正积分返回错误
        UserScoreRequest userScoreRequest = createOrderAdapter.convent2UserScoreRequest(orderBean, shoppingCartGoodsDto, true, useCustomerScore);
        baseResponse = this.reducesScore(userScoreRequest, orderBean, mqMessageRequest);
        if (baseResponse != null) {
            // 表示 操作失败
            return baseResponse;
        }

        // 下单成锁定劵，冻结失败提示下单失败并冲正库存积分等,判断支付渠道，积分和svc卡直接核销，无需锁定10102 = svc卡支付 || 支付金额小于等于0
        //todo:统一流程 下单锁券再核销
        CouponLockRequest couponLockRequest = createOrderAdapter.convert2CouponLockRequest(orderBean, shoppingCartGoodsDto);
        baseResponse = this.couponLock(couponLockRequest, orderBean, mqMessageRequest);
        if (baseResponse != null) {
            // 表示 操作失败
            return baseResponse;
        }
        return BaseOrderResponse.getErrorBaseOrderResponse(RESPONSE_SUCCESS_STR, "");
    }

    /**
     * 下单成锁定劵，冻结失败提示下单失败并冲正库存积分等,判断支付渠道，积分和svc卡直接核销，无需锁定10102 = svc卡支付 || 支付金额小于等于0
     *
     * @param couponLockRequest
     * @param orderBean
     * @param mqMessageRequest
     * @return
     */
    protected BaseOrderResponse couponLock(CouponLockRequest couponLockRequest, OrderBeanV1 orderBean, MqMessageRequest mqMessageRequest) {
        if (couponLockRequest != null) {
            com.freemud.application.sdk.api.base.BaseResponse<CouponLockResponse> couponLockResponseBaseResponse = offlineCouponSdkService.couponLock(couponLockRequest, "");
            if (!ObjectUtils.equals(RESPONSE_SUCCESS_STR, couponLockResponseBaseResponse.getCode())) {
                CancelOrderRequest cancelOrderRequest = orderSdkAdapter.convent2CancelOrderRequest(orderBean.getOid(), orderBean.getCompanyId(), AfterSalesType.SYSTEM_CANCEL, "锁定券码失败", "", null);
                orderCancel(cancelOrderRequest);
                //失败异步冲正库存，取消订单
                backOrdersNotifyActivity(orderBean, mqMessageRequest.getBackOrdersNotifyActivityQueue(), mqMessageRequest.getBackOrdersNotifyActivityExchange());
                return BaseOrderResponse.getErrorBaseOrderResponse(couponLockResponseBaseResponse.getCode(), couponLockResponseBaseResponse.getMessage());
            }
        }
        return null;
    }

    /**
     * 扣减积分，扣减失败冲正积分返回错误
     *
     * @param userScoreRequest
     * @param orderBean
     * @param mqMessageRequest
     * @return
     */
    protected BaseOrderResponse reducesScore(UserScoreRequest userScoreRequest, OrderBeanV1 orderBean, MqMessageRequest mqMessageRequest) {
        if (userScoreRequest != null) {
            com.freemud.application.sdk.api.base.BaseResponse userScoreResponse = memberScoreService.useScore(userScoreRequest, "");
            if (!ObjectUtils.equals(RESPONSE_SUCCESS_STR, userScoreResponse.getCode())) {
                //TODO 失败取消订单,异步冲正库存
                CancelOrderRequest cancelOrderRequest = orderSdkAdapter.convent2CancelOrderRequest(orderBean.getOid(), orderBean.getCompanyId(),
                        AfterSalesType.SYSTEM_CANCEL, "积分不足", "", null);
                orderCancel(cancelOrderRequest);
                //失败异步冲正库存，取消订单
                backOrdersNotifyActivity(orderBean, mqMessageRequest.getBackOrdersNotifyActivityQueue(), mqMessageRequest.getBackOrdersNotifyActivityExchange());
                return BaseOrderResponse.getErrorBaseOrderResponse(userScoreResponse.getCode(), "积分不足");
            }
        }
        return null;
    }

    /**
     * 扣减活动库存
     *
     * @param activityUpdateStockRequest
     * @param orderBean
     * @param mqMessageRequest
     * @return
     */
    protected BaseOrderResponse reducesActivityStocks(ActivityUpdateStockRequest activityUpdateStockRequest, OrderBeanV1 orderBean, MqMessageRequest mqMessageRequest) {
        // update by miaohui 20201030 for 【ID1018638】【C端服务端】活动库存接口调整对接
        // 原逻辑只检测扣减库存不为空时调用，现改为扣减库存不为空或参加活动不为空时调用
        if (activityUpdateStockRequest != null && (CollectionUtils.isNotEmpty(activityUpdateStockRequest.getStock()) || CollectionUtils.isNotEmpty(activityUpdateStockRequest.getActivityList()))) {
            com.freemud.application.sdk.api.base.BaseResponse activityResponse = promotionSdkService.subtractStock(activityUpdateStockRequest, "");
            ThirdPartyLog.infoConvertJson(System.currentTimeMillis(), System.currentTimeMillis(),
                    "subtractStock", JSONObject.toJSONString(activityUpdateStockRequest), JSONObject.toJSONString(activityResponse));
            if (!ObjectUtils.equals(RESPONSE_SUCCESS_STR, activityResponse.getCode())) {
                //TODO 失败取消订单,异步冲正库存
                CancelOrderRequest cancelOrderRequest = orderSdkAdapter.convent2CancelOrderRequest(orderBean.getOid(), orderBean.getCompanyId(),
                        AfterSalesType.SYSTEM_CANCEL, "活动库存不足", "", null);
                orderCancel(cancelOrderRequest);
                //失败异步冲正库存，取消订单
                backOrdersNotifyActivity(orderBean, mqMessageRequest.getBackOrdersNotifyActivityQueue(), mqMessageRequest.getBackOrdersNotifyActivityExchange());
                return BaseOrderResponse.getErrorBaseOrderResponse(activityResponse.getCode(), "活动库存不足");
            }
        }
        return null;
    }

    /**
     * TODO 扣减活动库存
     *
     * @param updateStocksRequest
     * @param orderId
     * @param partnerId
     * @return
     */
    protected BaseOrderResponse reducesStocks(UpdateStocksRequest updateStocksRequest, String orderId, String partnerId) {
        if (updateStocksRequest != null && CollectionUtils.isNotEmpty(updateStocksRequest.getProductStocks())) {
            com.freemud.application.sdk.api.base.BaseResponse baseResponse = stockSdkService.reducesStocks(updateStocksRequest, "");
            ThirdPartyLog.infoConvertJson(System.currentTimeMillis(), System.currentTimeMillis(),
                    "updateStock", JSONObject.toJSONString(updateStocksRequest), JSONObject.toJSONString(baseResponse));
            if (!ObjectUtils.equals(RESPONSE_SUCCESS_STR, baseResponse.getCode())) {
                //TODO 失败取消订单
                CancelOrderRequest cancelOrderRequest = orderSdkAdapter.convent2CancelOrderRequest(orderId, partnerId,
                        AfterSalesType.SYSTEM_CANCEL, "商品库存不足", "", null);
                orderCancel(cancelOrderRequest);
                return BaseOrderResponse.getErrorBaseOrderResponse(baseResponse.getCode(), "商品库存不足");
            }
        }
        return null;
    }

    private void backOrdersNotifyActivity(OrderBeanV1 orderBean, String backOrdersChangeOrderStatusConsumerQueue, String backOrdersNotifyActivityExchange) {
        if (null == orderBean || backOrdersChangeOrderStatusConsumerQueue == null || backOrdersNotifyActivityExchange == null) {
            return;
        }
        String oid = orderBean.getOid();
        Integer status = orderBean.getStatus();
        if (StringUtils.isEmpty(oid)) {
            return;
        }
        AppLogUtil.infoLog("backOrdersStatusChange", oid, String.valueOf(status));
        OrderStatusChangeRequestDto requestDto = new OrderStatusChangeRequestDto();
        requestDto.setOid(oid);
        requestDto.setOrderStatus(status);
        try {
            Header header = new Header(MQAction.INSERT.getAction(), "backOrdersStatusChange", oid, backOrdersChangeOrderStatusConsumerQueue);
            MQMessage<OrderStatusChangeRequestDto> message = new MQMessage<>(header, requestDto);
            mqService.convertAndSend(backOrdersNotifyActivityExchange, backOrdersChangeOrderStatusConsumerQueue, message);
        } catch (Exception e) {
            //TODO 邮件告警
            ThirdPartyLog.infoConvertJson(System.currentTimeMillis(), System.currentTimeMillis(),
                    "backOrdersStatusChangeNotify_error", backOrdersNotifyActivityExchange, backOrdersChangeOrderStatusConsumerQueue);
        }
    }

    public BaseOrderResponse orderCancel(CancelOrderRequest cancelOrderRequest) {
        OrderCancelReq request = new OrderCancelReq();
        //商户号必传
        request.setPartnerId(cancelOrderRequest.getPartnerId());
        request.setOrderCode(cancelOrderRequest.getOrderId());
        request.setReqRemark(cancelOrderRequest.getReqRemark());
        //售后单类型 1:其他取消 2:用户取消 3:商户取消 4:未支付超时关单 5:商户接单超时取消 6:商家拒单
        // 7:配送用户拒收 8:用户售后退货/售后退款 9:系统取消 10:客服取消 11:用户统一取消，
        request.setAfterSalesType(cancelOrderRequest.getAfterSalesType().getIndex() == null ? 1 : cancelOrderRequest.getAfterSalesType().getIndex());
        request.setOrderClient(cancelOrderRequest.getOrderClient());
        request.setCancelReason(cancelOrderRequest.getReason());
        request.setAfterSalesReason(cancelOrderRequest.getReason());
        //若为4，默认做【创建】+【同意并退款完成】，售后单状态为【完成】
        request.setCreateEvent(cancelOrderRequest.getCreateEvent());
        request.setAfterSerialNo(cancelOrderRequest.getRefundSerialNo());
        request.setOperator(cancelOrderRequest.getOperator());
        //为空发起售后单
        if (cancelOrderRequest.getCreateEvent() == null) {
            // 查询门店服务配置，退单模式为2为自动退款，判断自动退款时间,设置为0则不传入timeout,
            StoreResponse.Configuration configuration = getStoreAutoConfiguration(cancelOrderRequest.getPartnerId(), cancelOrderRequest.getStoreId(), cancelOrderRequest.getTrackingNo());
            if (configuration != null && ObjectUtils.equals("2", configuration.getAutoChargebackOrderType())
                    && !ObjectUtils.equals("0", configuration.getAutoChargebackOrderTime())) {
                request.setTimeOut(AutoOrderConfigTime.getTime(configuration.getAutoChargebackOrderTime()));
            }
            OrderTaskReq orderTask = setAfterSalesOrderTimeOutTask(configuration);
            request.setOrderTask(orderTask);
        }
        request.setRefundDeliveryAmount(cancelOrderRequest.isRefundDeliveryAmount());
        OrderBaseResp response = orderSdkService.cancelOrder(request, cancelOrderRequest.getTrackingNo());
        return orderSdkAdapter.convent2BaseOrderResponse(response);
    }

    /**
     * 设置申请退款时间
     * 如果时间的类型是2表示采用原来的48小时逻辑
     * 如果时间的类型是1表示在指定的时间之后进行退款
     * 为了防止同一时刻的退款量太大，需要将退款的订单分散到指定的时间后半小时内
     */
    private OrderTaskReq setAfterSalesOrderTimeOutTask(StoreResponse.Configuration configuration) {

        if (configuration == null) {
            return null;
        }

        if ((configuration.getTimeTypeOfRefund() != null && 2 == configuration.getTimeTypeOfRefund().intValue()) || configuration.getTimeTypeOfRefund() == null) {// 48小时之后
            OrderTaskReq orderTask = new OrderTaskReq();
            orderTask.setTaskType(4);
            orderTask.setTimeout(1);
            //1000*60*60*24*2 毫秒（48小时）
            long timeout = 172800000;
            long timeMillis = System.currentTimeMillis();
            Date processingDate = new Date(timeMillis + timeout - 60000);
            Date taskTime = new Date(timeMillis + timeout);
            orderTask.setTaskTime(DateUtil.convert2String(taskTime, DateUtil.FORMAT_YYYY_MM_DD_HHMMSS));
            //处理时间，当前时间加48小时减1分钟
            orderTask.setProcessingTime(DateUtil.convert2String(processingDate, DateUtil.FORMAT_YYYY_MM_DD_HHMMSS));

            return orderTask;
        } else if (configuration.getTimeTypeOfRefund() != null && 1 == configuration.getTimeTypeOfRefund().intValue()) {// 指定时间
            OrderTaskReq orderTask = new OrderTaskReq();
            orderTask.setTaskType(4);
            orderTask.setTimeout(1);
            // 获取门店配置的当天指定的退款时间
            String timeOfRefund = configuration.getTimeOfRefund();
            // 获取半小时的随机数
            Random r = new Random();
            int minute = r.ints(1, 31).findFirst().getAsInt() * 60000;
            String today = DateUtil.getCurrentDate(DateUtil.FORMAT_YMD);

            Date configTime = DateUtil.convert2Date(today + " " + timeOfRefund, DateUtil.FORMAT_YYYY_MM_DD_HHMMSS);
            // 判断当前时间是否已经超过门店设置的截至时间
            if (new Date().after(configTime)) {
                today = DateUtil.convert2String(DateUtil.addDays(new Date(), 1), DateUtil.FORMAT_YMD);
                configTime = DateUtil.convert2Date(today + " " + timeOfRefund, DateUtil.FORMAT_YYYY_MM_DD_HHMMSS);
            }
            Date processingDate = new Date(configTime.getTime() + minute);
            String processingStr = DateUtil.convert2String(processingDate, DateUtil.FORMAT_YYYY_MM_DD_HHMMSS);
            orderTask.setTaskTime(processingStr);
            orderTask.setProcessingTime(processingStr);

            return orderTask;
        }
        return null;
    }

    /**
     * 获取门店自动配置信息
     *
     * @param partnerId
     * @param storeCode
     * @param trackingNo
     * @return
     */
    private StoreResponse.Configuration getStoreAutoConfiguration(String partnerId, String storeCode, String trackingNo) {
        if (StringUtils.isEmpty(storeCode)) {
            return null;
        }
        StoreInfoRequest storeInfoRequest = new StoreInfoRequest();
        storeInfoRequest.setPartnerId(partnerId);
        storeInfoRequest.setStoreCode(storeCode);
        StoreResponse storeResponse = storeCenterService.getStoreInfo(storeInfoRequest, trackingNo);
        if (storeResponse == null || storeResponse.getBizVO() == null || storeResponse.getBizVO().getStoreConfig() == null) {
            return null;
        }

        return storeResponse.getBizVO().getStoreConfig();
    }

    /**
     * @param createOrderBo
     * @return
     */
    protected OrderExtInfoDto getExtInfo(CreateOrderBo createOrderBo) {

        return createOrderBo.getExtInfo();
    }

    public OrderExtInfoDto getExtInfoUniversal(CreateOrderBo createOrderBo) {

        CreateOrderBaseVo createOrderBaseVo = createOrderBo.getCreateOrderBaseVo();
        StoreResponse.BizVO storeResponseDto = createOrderBo.getStoreResponseDto();
        AssortmentCustomerInfoVo userLoginInfoDto = createOrderBo.getUserLoginInfoDto();
        ShoppingCartGoodsDto shoppingCartGoodsDto = createOrderBo.getShoppingCartGoodsDto();

        Integer serviceTime = storeResponseDto.getServiceTime();
        String sessionId = createOrderBaseVo.getSessionId();
        String version = createOrderBaseVo.getVersion();
        CreateOrderBaseVo.BuyMemberCard memberCard = createOrderBaseVo.getBuyMemberCard();

        String deliveryHoursDayStart = "";
        String deliveryHoursDayEnd = "";
        if (StringUtils.isNotBlank(storeResponseDto.getDeliveryHoursDay())) {
            String[] days = storeResponseDto.getDeliveryHoursDay().split("-");
            if (days.length == 2) {
                deliveryHoursDayStart = DateUtil.convert2String(new Date(), DateUtil.FORMAT_yyyyMMdd_date) + " " + days[0] + ":00";
            }
        }

        OrderExtInfoDto orderExtInfoDto = new OrderExtInfoDto();
        orderExtInfoDto.setDeliveryHoursDayStart(deliveryHoursDayStart);
        orderExtInfoDto.setDeliveryHoursDayEnd(deliveryHoursDayEnd);
        orderExtInfoDto.setOpenid(userLoginInfoDto.getOpenId());
        orderExtInfoDto.setAppid(userLoginInfoDto.getWxAppId());
        orderExtInfoDto.setSessionId(sessionId);
        if (Objects.equals(serviceTime, null)) {
            serviceTime = 50;
        }
        orderExtInfoDto.setServiceTime(serviceTime);
        // pushOrderTime 在master 版本中 一直都是 0
        orderExtInfoDto.setPushOrderTime(0);
        orderExtInfoDto.setVersion(version);
        if (memberCard != null && memberCard.getRuleId() != null) {
            orderExtInfoDto.setRuleId(memberCard.getRuleId());
        }
        // 扩展字段中存储 sessionKey
        orderExtInfoDto.setSessionKey(userLoginInfoDto.getSessionKey());
        orderExtInfoDto.setFormId(createOrderBaseVo.getFormId());
        orderExtInfoDto.setFromAppId(userLoginInfoDto.getWxAppId());
        if (StringUtils.isNotBlank(createOrderBaseVo.getScene())) {
            orderExtInfoDto.setScene(createOrderBaseVo.getScene());
        }
        if (null != shoppingCartGoodsDto && null != shoppingCartGoodsDto.getSendPoint()) {
            ShoppingCartGoodsDto.SendPoint sendPoint = shoppingCartGoodsDto.getSendPoint();
            orderExtInfoDto.setSendPointValidityDateDays(sendPoint.getValidityDateDays());
            orderExtInfoDto.setSendPointMaxNum(sendPoint.getMaxNum());
            orderExtInfoDto.setSendPointActivityCode(sendPoint.getActivityCode());
            orderExtInfoDto.setSendPointEndTime(sendPoint.getEndTime().getTime() + "");
            orderExtInfoDto.setSendPointSendPoint(sendPoint.getSendPoint());
            orderExtInfoDto.setSendPointStoreLevel(sendPoint.getStoreLevel());
            orderExtInfoDto.setIsContinueExchange(sendPoint.getIsContinueExchange());
        }

        return orderExtInfoDto;
    }


    /**
     * 创建预支付订单
     */
    public BaseResponse createPrepayOrder(CreatePrepayRequestDto createPrepayRequestDto) {

        CreateOrderResponseVo createOrderResponse;
        OrderPayResponse orderPayResponse;
        PaymentRequest paymentRequest = orderBodyConvertToPaymentBody(createPrepayRequestDto.getOpenId(), createPrepayRequestDto.getPartnerId(), createPrepayRequestDto.getWxAppId(), createPrepayRequestDto.getPayCode());
        long totalAmount = createPrepayRequestDto.getTotalAmount();
        String cardCode = createPrepayRequestDto.getCardCode();
        String transId = createPrepayRequestDto.getTransId();
        if (totalAmount < 0) {
            throw new ServiceException(ResponseResult.PAY_AMOUNT_ERROR);
        } else if (totalAmount > 0 && Objects.nonNull(createPrepayRequestDto.getUnionPayCard())) {
            // 电子风味卡支付
            orderPayResponse = uSvcPay(createPrepayRequestDto.getFatherOrderBean(), paymentRequest, transId, LogThreadLocal.getTrackingNo(), createPrepayRequestDto.getUnionPayCard());
        } else if (totalAmount > 0 && StringUtils.isBlank(cardCode)) {
            String partnerPayOvertime = this.getPartnerPayOvertime(createPrepayRequestDto.getPartnerId());
            orderPayResponse = getPreOrderPay(createPrepayRequestDto.getFatherOrderBean(), paymentRequest, LogThreadLocal.getTrackingNo(), createPrepayRequestDto.getCardAmount(), transId, partnerPayOvertime, createPrepayRequestDto.getOrderExtInfoDTO());
        }
        //混合支付+svc卡
        else if (totalAmount > 0 && StringUtils.isNotBlank(cardCode)) {
            orderPayResponse = switchSvcOrComb(totalAmount, cardCode, createPrepayRequestDto.getFatherOrderBean(), paymentRequest, transId, createPrepayRequestDto.getChannel(), createPrepayRequestDto.getStoreId(), LogThreadLocal.getTrackingNo());
        } else {
            // 0元订单如果不需要支付，自定义支付单号
            orderPayResponse = getOrderPayResponse(paymentRequest, createPrepayRequestDto.getFatherOrderBean());
        }
        if (orderPayResponse == null || Objects.isNull(orderPayResponse.getFmId())) {
            BaseResponse baseResponse = failPreOrderPay(LogThreadLocal.getTrackingNo(), createPrepayRequestDto.getProductOrderBean(), cardCode, createPrepayRequestDto.getOrderClient(),
                    Objects.isNull(orderPayResponse) ? null : orderPayResponse.getMsg());
            // 电子风味卡需要给用户特殊的返回
            if (totalAmount > 0 && Objects.nonNull(createPrepayRequestDto.getUnionPayCard())) {
                baseResponse.setMessage(Objects.nonNull(orderPayResponse) &&
                        (ResponseCodeConstant.PASSWORD_WRONG.equals(orderPayResponse.getPayTransId())
                                || ResponseCodeConstant.NOT_SUFFICIENT_FUND.equals(orderPayResponse.getPayTransId())) ?
                        orderPayResponse.getMsg() : "支付失败");
            }
            return baseResponse;
        }
        createOrderResponse = createOrderAdapter.convent2CreateFatherSonOrderResponseVo(orderPayResponse, createPrepayRequestDto.getProductOrderBean());
        BaseResponse baseEditResponse = this.updateOrderInfo(orderPayResponse, createPrepayRequestDto.getOrderExtInfoDTO(), createPrepayRequestDto.getProductOrderBean(), LogThreadLocal.getTrackingNo());
        if (baseEditResponse != null) {
            return baseEditResponse;
        }
        if (totalAmount > 0 && StringUtils.isBlank(cardCode) && Objects.isNull(createPrepayRequestDto.getUnionPayCard())) {
            createOrderResponse.setPaySuccess(false);
        } else if (orderPayResponse.getPayChannelType() != null && PayChannelType.COMB.getEbcode().equals(orderPayResponse.getPayChannelType().getEbcode())) {
            createOrderResponse.setPaySuccess(false);
        } else if (orderPayResponse.getPayChannelType() != null  && PayChannelType.SVC.getEbcode().equals(orderPayResponse.getPayChannelType().getEbcode())) {
            createOrderResponse.setPaySuccess(true);
        } else {
            createOrderResponse.setPaySuccess(true);
        }
        // 推荐优惠插件用户下单数据上报
        //wechatPushProcessor.statusOrderCreate(userLoginInfoDto, createOrderResponse.getOid(), createOrderVo.getPartnerId());
        if (!createOrderResponse.getPaySuccess()) {
            return ResponseUtil.success(createOrderResponse);
        } else {
            //如果是商品券支付0元，调用回调接口
            /**
             *  - 老的svc卡或是0元订单下单成功直接核销 这里不处理
             *  - 混合支付纯svc支付 需要直接内部核销 其他支付方式需依靠外部回调
             *  -
             */
            PaysuccessNoticeMessage message = new PaysuccessNoticeMessage();
            message.setResult_code(100);
            message.setOut_trade_no(createOrderResponse.getFmId());
            message.setTrans_id(createOrderResponse.getOid());
            message.setTotal_fee(0);
            message.setOpenid(createPrepayRequestDto.getOpenId());
            message.setPlatform_coupon(0);
            message.setMerchant_coupon(0);
            message.setSource(PaySuccessSource.OUTSIDE.getSource());
            orderAdapterService.paySuccessCallback(message);
            return ResponseUtil.success(createOrderResponse);
        }
    }

    /**
     * 编辑订单，保持预支付信息
     */
    public BaseResponse updateOrderInfo(OrderPayResponse orderPayResponse, OrderExtInfoDto orderExtInfo
            , OrderBeanV1 orderBean, String trackingNo) {
        orderExtInfo.setOpenid(orderPayResponse.getOpenId());
        orderExtInfo.setFmId(orderPayResponse.getFmId());
        orderExtInfo.setAppid(orderPayResponse.getWxAppid());
        String prepayId = "";
        OrderPayResponse.PayOrderBean payOrder = orderPayResponse.getPayOrder();
        // StringUtils 依赖修改
        if (payOrder != null && !StringUtils.isEmpty(payOrder.getPackageX())) {
            String[] prepayIds = payOrder.getPackageX().split("=");
            prepayId = prepayIds.length > 1 ? prepayIds[1] : "";
        }
        //支付宝保存交易流水号发送支付宝模板消息
        if (payOrder != null && !StringUtils.isEmpty(payOrder.getAliPayOrder())) {
            prepayId = payOrder.getAliPayOrder();
        }
        orderExtInfo.setPrepayId(prepayId);

        OrderEditRequest orderEditRequest = new OrderEditRequest(orderPayResponse.getPayTransId(),
                JSONObject.toJSONString(orderExtInfo));
        orderEditRequest.setOrderId(orderBean.getOid());
        orderEditRequest.setTrackingNo(trackingNo);
        if (orderPayResponse.getPayChannelType() != null) {
            orderEditRequest.setPayChannel(orderPayResponse.getPayChannelType().getEbcode());
            orderEditRequest.setPayChannelName(orderPayResponse.getPayChannelType().getName());
            orderEditRequest.setPayChannelType(orderPayResponse.getPayChannelType().getIndex());
        }
        //混合支付项
        if (CollectionUtils.isNotEmpty(orderPayResponse.getPayItem())) {
            orderEditRequest.setOrderPayItem(orderPayResponse.getPayItem());
            orderEditRequest.setPayChannel(PayChannelType.COMB.getEbcode());
            orderEditRequest.setPayChannelName(PayChannelType.COMB.getName());
            orderEditRequest.setPayChannelType(PayChannelType.COMB.getIndex());
        }
        BaseOrderResponse baseOrderResponse = orderCenterSdkService.orderEdit(orderEditRequest);
        if (!ObjectUtils.equals(ResponseCodeConstant.RESPONSE_SUCCESS, baseOrderResponse.getErrcode())) {
            CancelOrderRequest cancelOrderRequest = createOrderAdapter.convent2CancelOrderRequest(orderBean.getOid(), orderBean.getCompanyId(),
                    AfterSalesType.SYSTEM_CANCEL, "编辑订单支付信息失败", trackingNo, null);
            orderCenterSdkService.orderCancel(cancelOrderRequest);
            //如果编辑订单支付信息失败，冲正
            backOrdersStatusChange(orderBean.getOid(), orderBean.getStatus());
            return ResponseUtil.error(baseOrderResponse.getErrcode().toString(), baseOrderResponse.getErrmsg());

        }

        return null;
    }

    private BaseResponse failPreOrderPay(String trackingNo, OrderBeanV1 orderBean, String cardCode, OrderClientType orderClient, String msg) {
        List<OrderClientType> notCancelOrderClientList = Lists.newArrayList(OrderClientType.APP);
        if (!notCancelOrderClientList.contains(orderClient)) {
            //失败冲正库存，冲正活动库存，取消订单
            CancelOrderRequest cancelOrderRequest = createOrderAdapter.convent2CancelOrderRequest(orderBean.getOid(), orderBean.getCompanyId(),
                    AfterSalesType.SYSTEM_CANCEL, StringUtils.join(new String[]{"获取预支付失败", msg}, '-'), trackingNo, null);
            orderCenterSdkService.orderCancel(cancelOrderRequest);
            // TODO: 2019/9/10 hubowen mq推送变更
            backOrdersStatusChange(orderBean.getOid(), orderBean.getStatus());
        }
        //svc 卡支付失败
        if (StringUtils.isNotBlank(cardCode)) {
            return ResponseUtil.error(ResponseResultEnum.PAY_BACKEND_CONFIG_ERROR.getCode(), StringUtils.isNotBlank(msg) ? msg : ResponseResultEnum.PAY_BACKEND_CONFIG_ERROR.getMessage(), null);
        }
        return ResponseUtil.error(ResponseResultEnum.PAY_UNIFIED_ORDER_ERROR.getCode(), ResponseResultEnum.PAY_UNIFIED_ORDER_ERROR.getMessage(), null);
    }

    public void backOrdersStatusChange(String oid, Integer orderStatus) {
        if (StringUtils.isEmpty(oid)) {
            return;
        }
        AppLogUtil.infoLog("backOrdersStatusChange", oid, String.valueOf(orderStatus));
        OrderStatusChangeRequestDto requestDto = new OrderStatusChangeRequestDto();
        requestDto.setOid(oid);
        requestDto.setOrderStatus(orderStatus);

        try {
            Header header = new Header(MQAction.INSERT.getAction(), "backOrdersStatusChange", oid, backOrdersChangeOrderStatusConsumerQueue);
            MQMessage<OrderStatusChangeRequestDto> message = new MQMessage<>(header, requestDto);
            mqService.convertAndSend(backOrdersNotifyActivityExchange, backOrdersChangeOrderStatusConsumerQueue, message);
        } catch (Exception e) {
            AppLogUtil.errorLog("ActivityReverse", JSON.toJSONString(requestDto), "", e);
        }
    }

    protected OrderPayResponse getOrderPayResponse(PaymentRequest paymentRequest, OrderBeanV1 orderBean) {
        OrderPayResponse orderPayResponse;
        String fmId = "SPAY" + ValidationCode.getRandomUuid();
        orderPayResponse = createOrderAdapter.getOrderPayResponse(orderBean.getCompanyId(), paymentRequest,
                orderBean.getOid(), fmId);
        return orderPayResponse;
    }

    private OrderPayResponse switchSvcOrComb(Long totalAmount
            , String cardCode
            , OrderBeanV1 orderBean
            , PaymentRequest paymentRequest
            , String transId
            , String channel
            , String storeId
            , String trackingNo) {

        OrderPayResponse orderPayResponse = new OrderPayResponse();
        boolean check = this.checkSvcComPay(orderBean.getCompanyId(), orderBean.getShopId());
        //混合支付
        if (check && StringUtils.isNotBlank(channel)) {
            String partnerPayOvertime = this.getPartnerPayOvertime(orderBean.getCompanyId());
            orderPayResponse = comPayOrder(cardCode, orderBean, paymentRequest, partnerPayOvertime, totalAmount.intValue(), channel, storeId, LogThreadLocal.getTrackingNo());
        } else {//svc 支付
            orderPayResponse = svcPay(cardCode, orderBean, paymentRequest, transId, trackingNo);
        }
        return orderPayResponse;
    }

    public OrderPayResponse svcPay(String cardCode, OrderBeanV1 orderBean, PaymentRequest paymentRequest, String transId, String trackingNo) {
        CodePayRequest request = new CodePayRequest();
        OrderPayResponse orderPayResponse = new OrderPayResponse();
        String partnerId = orderBean.getCompanyId();
        request.setBody(orderBean.getShopName());
        request.setBusinessDate(DateUtil.convert2String(new Date(), DateUtil.FORMAT_YYYY_MM_DD_HHMMSS));
        request.setCode(cardCode);
        request.setPartnerId(partnerId);
        request.setStoreId(orderBean.getShopId());
        request.setAmount(orderBean.getAmount());
        request.setTransId(transId);
        request.setStationId("1");
        request.setOperatorId("1");
        request.setVer("2");
        SVCCardAmountRequest svcRequest = new SVCCardAmountRequest();
        svcRequest.setPartnerId(partnerId);
        svcRequest.setCardCodes(Arrays.asList(cardCode));
        //查询svc卡余额
        com.freemud.application.sdk.api.base.BaseResponse<SVCCardAmountResponse> svcCardAmountResponseBaseResponse = paymentNewService.querySVCCardAmount(svcRequest, trackingNo);
        if (svcCardAmountResponseBaseResponse == null || !ResponseCodeConstant.RESPONSE_SUCCESS_STR.equals(svcCardAmountResponseBaseResponse.getCode())
                || svcCardAmountResponseBaseResponse.getData().getData() == null || CollectionUtils.isEmpty(svcCardAmountResponseBaseResponse.getData().getData().getCardSimpleInfos())) {
            orderPayResponse.setMsg(Objects.isNull(svcCardAmountResponseBaseResponse) ? "获取svc卡余额异常" : svcCardAmountResponseBaseResponse.getMessage());
            return orderPayResponse;
        }
        Integer amount = svcCardAmountResponseBaseResponse.getData().getData().getCardSimpleInfos().get(0).getAmount();
        Integer vamount = svcCardAmountResponseBaseResponse.getData().getData().getCardSimpleInfos().get(0).getVamount();
        if (amount + vamount < orderBean.getAmount()) {
            orderPayResponse.setMsg("svc卡余额不足");
            return orderPayResponse;
        }
        //svc卡支付
        com.freemud.application.sdk.api.base.BaseResponse<CodePayResponse> responseBase = paymentNewService.codePay(request, trackingNo);
        if (responseBase == null || !ResponseCodeConstant.RESPONSE_SUCCESS_STR.equals(responseBase.getCode()) || responseBase.getData().getData() == null) {
            // 1.9.34 预支付失败需要把原因进行记录
            orderPayResponse.setMsg(Objects.isNull(responseBase) ? "svc卡支付异常" : responseBase.getMessage());
        } else {
            CodePayResponse.CodePayDate codePayResponse = responseBase.getData().getData();
            orderPayResponse.setFmId(codePayResponse.getFmId());
            orderPayResponse.setPayTransId(codePayResponse.getPayTransId());
            orderPayResponse.setWxAppid(paymentRequest.getWxAppId());
            orderPayResponse.setOpenId(paymentRequest.getOpenId());
            orderPayResponse.setPartnerId(orderBean.getCompanyId());
            orderPayResponse.setOrderId(orderBean.getOid());
            orderPayResponse.setPayChannelType(PayChannelType.SVC);
        }
        return orderPayResponse;
    }

    /**
     * 创建混合支付
     *
     * @param orderBean
     * @param paymentRequest
     * @param partnerPayOvertime
     * @param totalAmount
     * @param channel
     * @param storeId
     * @param trackingNo
     * @return
     */
    public OrderPayResponse comPayOrder(String cardNo
            , OrderBeanV1 orderBean
            , PaymentRequest paymentRequest
            , String partnerPayOvertime
            , Integer totalAmount
            , String channel
            , String storeId
            , String trackingNo) {

        String ebCode = this.getPayCodeByChanel(paymentRequest.getWxAppId(), channel, storeId);
        OrderPayResponse orderPayResponse = new OrderPayResponse();
        ;
        if (StringUtils.isBlank(ebCode)) {
            orderPayResponse.setMsg("请先联系相关人员配置商户对应的支付渠道");
            return orderPayResponse;
        }

        CombPayRequest combPayRequest = createOrderAdapter.convent2CombPayOrderRequest(cardNo, orderBean, paymentRequest, partnerPayOvertime, ebCode, channel, totalAmount);
        //todo :xxxooooo xxxoooxox
        CombPayResponse combPayResponse = null;
        int i = 1;
        do {
            try {
                combPayResponse = comPayClient.combPay(combPayRequest, combPayRequest.getPartner_id());
                if (combPayResponse != null) {
                    break;
                }
            } catch (Exception exception) {
            }
            i++;
        } while (i < 4);

        if (combPayResponse == null || !ResponseCodeConstant.PAYMENT_RESPONSE_SUCCESS.equals(combPayResponse.getCode())) {
            orderPayResponse.setMsg(combPayResponse != null ? "支付：" + combPayResponse.getMsg() : "混合支付忙不过来啦，请稍后再试");
            return orderPayResponse;
        }
        Integer svcAmount = 0;
        Integer svcVAmount = 0;
        List<CombPayResponse.PayPlatform> payPlatforms = combPayResponse.getData().getPayPlatformResponseList();
        if (payPlatforms.size() == 0) {
            orderPayResponse.setMsg("混合支付:生成预支付失败");
            return orderPayResponse;
        }
        //生成预支付参数
        CombPayResponse.PayPlatform cashPay = payPlatforms
                .stream()
                .filter(f -> !PayChannelType.SVC.getEbcode().equals(f.getEbCode())).findFirst()
                .orElse(null);

        //现金+svc   现金
        if (payPlatforms.size() == 2 || (payPlatforms.size() == 1 && cashPay != null)) {
            orderPayResponse = createOrderAdapter.convent2OrderCombPayResponse(combPayResponse.getData().getPayPlatformResponseList(), orderBean.getCompanyId(), storeId);
            orderPayResponse.setOpenId(paymentRequest.getOpenId());
            orderPayResponse.setWxAppid(paymentRequest.getWxAppId());
            orderPayResponse.setPartnerId(orderBean.getCompanyId());
            orderPayResponse.setOrderId(orderBean.getOid());
            orderPayResponse.setPayChannelType(orderPayResponse.getPayChannelType());
            //隐射关系
            redisCache.save(RedisUtil.getPaymentTransIdOrderKey(cashPay.getTransId()), orderBean.getOid(), 1L, TimeUnit.DAYS);
            //加入轮训队列
            putDelMq(orderBean.getCompanyId(), storeId, orderPayResponse.getFmId(), orderBean.getOid(), PayChannelType.getByEbcode(cashPay.getEbCode()).getIndex().intValue());
            return orderPayResponse;
        }
        //单svc
        else {
            CombPayResponse.PayPlatform svcPay = payPlatforms
                    .stream()
                    .filter(f -> PayChannelType.SVC.getEbcode().equals(f.getEbCode())).findFirst()
                    .orElse(null);
            orderPayResponse.setFmId(svcPay.getTransId());
            orderPayResponse.setPayTransId(svcPay.getTransId());
            orderPayResponse.setWxAppid(paymentRequest.getWxAppId());
            orderPayResponse.setOpenId(paymentRequest.getOpenId());
            orderPayResponse.setPartnerId(orderBean.getCompanyId());
            orderPayResponse.setOrderId(orderBean.getOid());
            orderPayResponse.setPayChannelType(PayChannelType.SVC);
            for (CombPayResponse.PayPlatform pt : payPlatforms) {
                OrderPayItemResp payItem = new OrderPayItemResp();
                payItem.setFmTradeNo(pt.getFmTradeNo());
                payItem.setStoreId(storeId);
                payItem.setPayChannelType(PayChannelType.getByEbcode(pt.getEbCode()).getIndex().intValue());
                payItem.setPayChannelName(PayChannelType.getByEbcode(pt.getEbCode()).getName());
                payItem.setTransId(pt.getTransId());
                payItem.setPartnerId(combPayRequest.getPartner_id());
                payItem.setPayAmount(new BigDecimal(pt.getPayAmount()));
                orderPayResponse.getPayItem().add(payItem);
            }
        }

        return orderPayResponse;


    }

    /**
     * 通过payCode ->clientCode
     *
     * @param wxAppid
     * @param channel
     * @param storeId
     * @return
     */
    private String getPayCodeByChanel(String wxAppid, String channel, String storeId) {
        if (StringUtils.isBlank(channel)) return "";

        PayChannelType byIndex = PayChannelType.getByIndex(Byte.parseByte(channel));
        if (byIndex.getEbcode() == null) return "";
        AssortmentOpenPlatformIappWxappStore wxAppStore = assortmentOpenPlatformIappWxappStoreManager.selectWxappStoreByWxAppIdAndStoreId(wxAppid
                , storeId
                , byIndex.getEbcode());
        if (wxAppStore != null && StringUtils.isNotBlank(wxAppStore.getClientCode())) {
            return wxAppStore.getClientCode();
        }
        return "";
    }

    /**
     * 获取混合支付是否开启
     *
     * @param partnerId
     * @return
     */
    private boolean checkSvcComPay(String partnerId, String storeId) {
        SvcComPayRequestDto requestDto = new SvcComPayRequestDto();
        requestDto.setPartnerId(partnerId);
        requestDto.setStoreId(storeId);
        SvcComPayResponseDto query = null;
        for (int i = 0; i < 3; i++) {
            query = svcComPayClient.query(requestDto);
            if (query != null) break;
        }
        if (query == null || !ResponseResult.SUCCESS.getCode().equals(query.getCode())) {
            return false;
        }
        if (query.getResult() != null && query.getResult().getValue()) {
            return true;
        } else {
            return false;
        }
    }

    public OrderPayResponse getPreOrderPay(OrderBeanV1 orderBean, PaymentRequest paymentRequest, String trackingNo, Integer cardAmount, String transId, String partnerPayOvertime, OrderExtInfoDto orderExtInfoDto) {
        OrderPayResponse orderPayResponse;
        try {
            UnifiedOrderRequest request = createOrderAdapter.convent2UnifiedOrderRequest(orderBean, paymentRequest, orderBean.getAmount(), cardAmount, transId, partnerPayOvertime);
            // add by miaohui for 拼单群收款判断拼单人数大于1时参与微信平台补贴活动，创建预支付时上送goodsTag，后续抽成促销服务活动时删除此逻辑 start
            if (null != orderBean.getMarketingType() && OrderMarketType.COLLAGE.getIndex() == orderBean.getMarketingType()) {
                // 拼单人数大于1人时参加拼单补贴
                if (null != orderBean.getProductList() && orderBean.getProductList().stream().map(ProductBeanV1::getUserId).distinct().collect(Collectors.toList()).size() > 1) {
                    Map<String, String> goodsTagMap = this.goodsTagMap();
                    String goodsTag = goodsTagMap.get(orderBean.getCompanyId());
                    request.setGoodsTag(StringUtils.isBlank(goodsTag) ? collageGoodsTag : goodsTag);
                }
            }
            // add by miaohui for 拼单群收款判断拼单人数大于1时参与微信平台补贴活动，创建预支付时上送goodsTag，后续抽成促销服务活动时删除此逻辑 end
            if (orderExtInfoDto != null && StringUtils.isNotBlank(orderExtInfoDto.getStationId())) {
                request.setStationId(orderExtInfoDto.getStationId());
            }
            com.freemud.application.sdk.api.base.BaseResponse<UnifiedOrderResponse> responseBase = standardPaymentService.unifiedOrder(request, trackingNo);
            if (!ResponseCodeConstant.RESPONSE_SUCCESS_STR.equals(responseBase.getCode())) {
                // 1.9.34 预支付失败需要把原因进行记录
                orderPayResponse = new OrderPayResponse();
                orderPayResponse.setMsg(responseBase.getMessage());
            } else {
                putDelMq(request.getPartnerId(), request.getStoreId(), responseBase.getData().getFmId(), orderBean.getOid());
                orderPayResponse = createOrderAdapter.convent2OrderPayResponse(responseBase.getData());
                orderPayResponse.setWxAppid(paymentRequest.getWxAppId());
                orderPayResponse.setOpenId(paymentRequest.getOpenId());
                orderPayResponse.setPartnerId(orderBean.getCompanyId());
                orderPayResponse.setOrderId(orderBean.getOid());
                orderPayResponse.setPayChannelType(PayChannelType.WECHAT);
                if (orderPayResponse.getPayOrder() != null && StringUtils.isNotBlank(orderPayResponse.getPayOrder().getAliPayOrder())) {
                    orderPayResponse.setPayChannelType(PayChannelType.ALIPAY);
                }
                //抖音支付
                if (PayChannelType.TIKTOKPAY.getIndex().toString().equalsIgnoreCase(orderBean.getPayChannelType())) {
                    orderPayResponse.setPayChannelType(PayChannelType.TIKTOKPAY);
                }

            }
        } catch (Exception e) {
            AppLogUtil.errorLog("getPreOrderPay_error", JSONObject.toJSONString(orderBean), JSONObject.toJSONString(paymentRequest),e);
            //TODO 邮件告警
            orderPayResponse = new OrderPayResponse();
            orderPayResponse.setMsg("getPreOrderPay_error");
        }
        return orderPayResponse;
    }

    /**
     * 获取预支付成功，将信息放入死心队列，当支付成功没有回掉的时候处理
     *
     * @param partnerId
     * @param storeId
     * @param fmId
     * @param orderId
     */
    private void putDelMq(String partnerId, String storeId, String fmId, String orderId) {
        PaymentMqMessageDto dto = new PaymentMqMessageDto();
        try {
            dto.setFmId(fmId);
            dto.setOrderId(orderId);
            dto.setPartnerId(partnerId);
            dto.setStoreId(storeId);
            dto.setTrackingNo(LogThreadLocal.getTrackingNo());
            paymentQueueService.paymentCallback(dto);
        } catch (Exception e) {
            AppLogUtil.errorLog("paymentQueueService.paymentCallback" , JSONObject.toJSONString(dto) , null, e);
        }
    }

    /**
     * 获取预支付成功，将信息放入死心队列，当支付成功没有回掉的时候处理
     *
     * @param partnerId
     * @param storeId
     * @param fmId
     * @param orderId
     */
    private void putDelMq(String partnerId, String storeId, String fmId, String orderId, Integer payChanelType) {
        PaymentMqMessageDto dto = new PaymentMqMessageDto();
        try {
            dto.setFmId(fmId);
            dto.setOrderId(orderId);
            dto.setPartnerId(partnerId);
            dto.setStoreId(storeId);
            dto.setPayChannelType(payChanelType);
            dto.setTrackingNo(LogThreadLocal.getTrackingNo());
            paymentQueueService.paymentCallback(dto);
        } catch (Exception e) {
            AppLogUtil.errorLog("paymentQueueService.paymentCallback",JSONObject.toJSONString(dto) , null, e);
        }
    }

    private Map<String, String> goodsTagMap() {
        Map<String, String> map = new HashMap<>();
        try {
            for (String pg : collageGoodsTagList.split(",")) {
                map.put(pg.split(":")[0], pg.split(":")[1]);
            }
        } catch (Exception e) {
            AppLogUtil.errorLog("goodsTag解析失败", JSONObject.toJSONString(collageGoodsTagList), null, e);
        }
        return map;
    }

    /**
     * unionPay card pay
     *
     * @param orderBean
     * @param paymentRequest
     * @param transId
     * @param trackingNo
     * @param unionPayCard
     * @return
     */
    private OrderPayResponse uSvcPay(OrderBeanV1 orderBean, PaymentRequest paymentRequest, String transId, String trackingNo, CreateOrderVo.UnionPayCard unionPayCard) {
        OrderPayResponse orderPayResponse = new OrderPayResponse();
        if (Objects.isNull(unionPayCard) || StringUtils.isBlank(unionPayCard.getCode()) || StringUtils.isBlank(unionPayCard.getPassword())) {
            orderPayResponse.setMsg("电子风味卡信息缺失");
            return orderPayResponse;
        }
        CodePayRequest request = new CodePayRequest();
        String partnerId = orderBean.getCompanyId();
        request.setBody(orderBean.getShopName());
        request.setBusinessDate(DateTimeFormatter.ofPattern(DateUtil.FORMAT_YYYY_MM_DD_HHMMSS).format(LocalDateTime.now()));
        // paymentRequest 的 payCode可能为null,不保险
        request.setPayCode(PayChannel.USVCP.getCode());
        // 电子风味卡信息
        request.setCode(unionPayCard.getCode());
        request.setCardPassword(unionPayCard.getPassword());
        request.setPartnerId(partnerId);
        request.setStoreId(orderBean.getShopId());
        request.setAmount(orderBean.getAmount());
        request.setTransId(transId);
        request.setStationId("2");
        request.setOperatorId("2");
        request.setVer("2");
        com.freemud.application.sdk.api.base.BaseResponse<CodePayResponse> responseBase = paymentNewService.codePay(request, trackingNo);
        if (responseBase == null || !ResponseCodeConstant.RESPONSE_SUCCESS_STR.equals(responseBase.getCode()) || responseBase.getData().getData() == null) {
            // 需要把2种特殊情况抛给用户
            if (Objects.nonNull(responseBase) && (ResponseCodeConstant.PASSWORD_WRONG.equals(responseBase.getCode()) || ResponseCodeConstant.NOT_SUFFICIENT_FUND.equals(responseBase.getCode()))) {
                orderPayResponse.setPayTransId(responseBase.getCode());
            }
            orderPayResponse.setMsg(Objects.isNull(responseBase) ? "电子风味卡支付异常" : responseBase.getMessage());
        } else {
            CodePayResponse.CodePayDate codePayResponse = responseBase.getData().getData();
            orderPayResponse.setFmId(codePayResponse.getFmId());
            orderPayResponse.setPayTransId(codePayResponse.getPayTransId());
            orderPayResponse.setWxAppid(paymentRequest.getWxAppId());
            orderPayResponse.setOpenId(paymentRequest.getOpenId());
            orderPayResponse.setPartnerId(orderBean.getCompanyId());
            orderPayResponse.setOrderId(orderBean.getOid());
            orderPayResponse.setPayChannelType(PayChannelType.USVCP);
        }
        return orderPayResponse;
    }

    /**
     * 获取商户支付超时时间
     *
     * @param partnerId 商户Id
     * @return 超时时间
     */
    private String getPartnerPayOvertime(String partnerId) {
        return Optional.ofNullable(openPlatformPartnerConfigManager.selectPartnerConfigByPartnerKey(partnerId, RedisKeyConstant.PARTNER_PAY_OVERTIME))
                .orElseGet(() -> {
                    AssortmentOpenPlatformPartnerConfig config = new AssortmentOpenPlatformPartnerConfig();
                    config.setPartnerValue("30");
                    openPlatformPartnerConfigManager.addPartnerConfig(partnerId, RedisKeyConstant.PARTNER_PAY_OVERTIME, config);
                    return config;
                }).getPartnerValue();
    }

    private PaymentRequest orderBodyConvertToPaymentBody(String openId, String partnerId, String appId, String payCode) {
        //设置支付信息
        PaymentRequest paymentRequest = new PaymentRequest();
        paymentRequest.setOpenId(openId);
        paymentRequest.setWxAppId(appId);
        AssortmentOpenPlatformWxapp wxApp = openPlatformWxappManager.findByPartnerIdAndWxappId(partnerId, appId);
        AssortmentOpenPlatformPartner platformPartner = null;
        if (wxApp == null) {
            platformPartner = assortmentOpenPlatformPartnerManager.selectOpenPlatformPartner(partnerId);
        }
        paymentRequest.setPrincipalName(wxApp != null ? wxApp.getPrincipalName() : platformPartner != null ? platformPartner.getCompanyName() : "上海非码网络科技有限公司");
        paymentRequest.setReverseNotifyiDcUrl(reverseNotifyiDcUrl);
        paymentRequest.setPayCode(payCode);
        return paymentRequest;
    }

//    public CreateOrderResponse getCreateOrderResponse(String code, String message, OrderBeanV1 data) {
//        CreateOrderResponse baseOrderResponse = new CreateOrderResponse();
//        baseOrderResponse.setErrcode(Integer.valueOf(code));
//        baseOrderResponse.setErrmsg(message);
//        baseOrderResponse.setData(data);
//        return baseOrderResponse;
//    }

    /**
     * 初始化扣减活动库存请求对象
     * add by miaohui 20201030 for 【ID1018638】【C端服务端】活动库存接口调整对接
     *
     * @return
     */
    protected ActivityUpdateStockRequest initActivityUpdateStockRequest(CreateOrderBo createOrderBo) {
        List<ActivityDiscountsDto> activityDiscountsDtos = createOrderBo.getShoppingCartGoodsDto().getActivityDiscountsDtos();
        // 原逻辑只检测扣减库存不为空时调用，现改为扣减库存不为空或参加活动不为空时调用
        ActivityUpdateStockRequest activityUpdateStockRequest = new ActivityUpdateStockRequest();
        List<String> activityList = new ArrayList<>();
        // 只记录订单级别的活动ID
        if (CollectionUtils.isNotEmpty(activityDiscountsDtos)) {
            activityList.addAll(activityDiscountsDtos.stream().filter(ad -> StringUtils.isNotBlank(ad.getActivityCode())).map(ActivityDiscountsDto::getActivityCode).distinct().collect(Collectors.toList()));
        }
        //设置活动编号
        activityUpdateStockRequest.setActivityList(CollectionUtils.isNotEmpty(activityList) ? activityList : null);

        //设置库存扣减
        if (createOrderBo.getShoppingCartGoodsDto() != null && CollectionUtils.isNotEmpty(createOrderBo.getShoppingCartGoodsDto().getStocks())) {
            List<ActivityUpdateStockRequest.StockBeanDto> stocks = new ArrayList<>();
            for (SubtractStockVO stockVO : createOrderBo.getShoppingCartGoodsDto().getStocks()) {
                ActivityUpdateStockRequest.StockBeanDto stockBeanDto = new ActivityUpdateStockRequest.StockBeanDto();
                stockBeanDto.setActivityCode(stockVO.getActivityCode());
                stockBeanDto.setActivityStock(stockVO.getActivityStock() == null ? 1 : stockVO.getActivityStock());
                if (CollectionUtils.isNotEmpty(stockVO.getGoodsStock())) {
                    List<GoodsStockDTO> goodsStockDTOS = new ArrayList<>();
                    stockVO.getGoodsStock().forEach(goods -> {
                        GoodsStockDTO goodsStockDTO = new GoodsStockDTO();
                        goodsStockDTO.setBenefitSeq(goods.getBenefitSeq());
                        goodsStockDTO.setGoodsId(goods.getGoodsId());
                        int stock = goods.getStock() == null ? 1 : goods.getStock();
                        goodsStockDTO.setStock(stock);
                        goodsStockDTOS.add(goodsStockDTO);
                    });
                    stockBeanDto.setGoodsStock(goodsStockDTOS);
                }
                stocks.add(stockBeanDto);
            }
            activityUpdateStockRequest.setStock(stocks);
        }

        return activityUpdateStockRequest;
    }
}
