package com.yc.ship.module.trade.service.ota.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.util.DateUtils;
import com.yc.ship.framework.common.exception.ServerException;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.util.collection.CollectionUtils;
import com.yc.ship.framework.tenant.core.context.TenantContextHolder;
import com.yc.ship.module.infra.api.config.ConfigApi;
import com.yc.ship.module.ota.api.OtaDistributorApi;
import com.yc.ship.module.ota.api.dto.DistributorProductRespDTO;
import com.yc.ship.module.ota.api.dto.DistributorRespDTO;
import com.yc.ship.module.product.api.ProductApi;
import com.yc.ship.module.product.api.dto.ProductSaleRuleRespDTO;
import com.yc.ship.module.product.api.dto.ProductSpuRespDTO;
import com.yc.ship.module.product.api.dto.RoutePlanRespDTO;
import com.yc.ship.module.trade.api.dto.TradeOrderCreateReqDTO;
import com.yc.ship.module.trade.api.dto.TradeOrderRespDTO;
import com.yc.ship.module.trade.api.dto.TradeRefundCreateReqDTO;
import com.yc.ship.module.trade.api.dto.TradeRefundRespDTO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.PayOrderReqVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.PayOrderRespVO;
import com.yc.ship.module.trade.convert.order.TradeRefundConvert;
import com.yc.ship.module.trade.convert.ota.OtaConvert;
import com.yc.ship.module.trade.dal.dataobject.order.TradeDetailDO;
import com.yc.ship.module.trade.dal.dataobject.order.TradeOrderDO;
import com.yc.ship.module.trade.dal.mysql.order.TradeDetailMapper;
import com.yc.ship.module.trade.enums.*;
import com.yc.ship.module.trade.enums.ota.OtaConstants;
import com.yc.ship.module.trade.framework.common.ota.AesUtil;
import com.yc.ship.module.trade.framework.common.ota.MeiTuanUSGroupAuthUtils;
import com.yc.ship.module.trade.framework.mq.TradePublishUtils;
import com.yc.ship.module.trade.service.notifylog.NotifyLogService;
import com.yc.ship.module.trade.service.order.TradeOrderRepositoryService;
import com.yc.ship.module.trade.service.order.TradeOrderService;
import com.yc.ship.module.trade.service.ota.MeiTuanService;
import com.yc.ship.module.trade.service.ota.OtaService;
import com.yc.ship.module.trade.service.ota.bo.SignBackBean;
import com.yc.ship.module.trade.service.pay.TradeOrderPayService;
import com.yc.ship.module.trade.service.refund.TradeRefundService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.REFUND_AUDITING;
import static com.yc.ship.module.trade.enums.TravelhubDictTypeConstants.PRODUCT_BASE_TYPE_SELF;
import static com.yc.ship.module.trade.enums.ota.OtaConstants.OTA_STATUS;
import static com.yc.ship.module.trade.service.ota.OtaService.meiTuan;


/**
 * @description：美团接口实现
 */
@Slf4j
@Service(meiTuan)
public class MeiTuanServiceImpl implements MeiTuanService, OtaService {

    @Resource
    private OtaDistributorApi otaDistributorApi;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ProductApi productApi;
    @Resource
    private TradeOrderService tradeOrderService;
    @Resource
    private TradeOrderRepositoryService tradeOrderRepositoryService;
    @Resource
    private ConfigApi configApi;
    @Resource
    private TradeOrderPayService tradeOrderPayService;
    @Resource
    private NotifyLogService notifyLogService;
    @Resource
    private MeiTuanService meiTuanService;
    @Resource
    private TradeRefundService tradeRefundService;
    @Resource
    private TradeDetailMapper tradeDetailMapper;
    @Autowired
    private TradePublishUtils tradePublishUtils;

    private static final String keySuffix = "123a456b789c012d345e678f";

    /**
     * @param request  HttpServletRequest
     * @param path     请求名
     * @param jsonStr  请求参数
     * @param tenantId 租户ID
     * @return java.lang.String
     * @description 美团请求入口
     */
    @Override
    public String doRequest(HttpServletRequest request, String path, String jsonStr, Long tenantId) {
        TenantContextHolder.setTenantId(tenantId);
        TenantContextHolder.setIgnore(false);
        SignBackBean signBackBean = this.checkSign(jsonStr, request);
        DistributorRespDTO distributor = signBackBean.getDistributor();
        if (distributor == null) {
            distributor = new DistributorRespDTO();
        }
        if (!signBackBean.isSignSuccess()) {
            return meituanReturn(signBackBean.getErrCode(), signBackBean.getErrMsg(),
                    distributor.getOtaAccount(), "", "", "", -1);
        }
        try {
            // 创建订单
            if ("apply_code_mt".equals(path)) {
                return createOrder(signBackBean.getBody(), distributor);
            } else if ("pay_code_mt".equals(path)) {
                // 支付订单
                return orderPay(signBackBean.getBody(), distributor);
                // 取消订单(退单、退票)
            } else if ("discard_code_mt".equals(path)) {
                return refundOrder(signBackBean.getBody(), distributor);
                // 查询订单
            } else if ("query_order_code_mt".equals(path)) {
                return queryOrder(signBackBean.getBody(), distributor);
                //关闭订单
            } else if ("close_order_code_mt".equals(path)) {
                return cancelOrder(signBackBean.getBody(), distributor);
                // 已退款消息
            } else if ("refunded_order_code_mt".equals(path)) {
                return refundedOrder(signBackBean.getBody(), distributor);
            } else if ("calendar_price_stock_v2_mt".equals(path)) {
                return pullMtTicketLevelPriceStockV2(signBackBean.getBody(), distributor);
            } else {
                return meituanReturn("401", "无匹配的方法", distributor.getOtaAccount(), "", "", "", -1);
            }
        } catch (Exception ex) {
            //返回统一错误码
            return meituanReturn("401", "操作失败！", distributor.getOtaAccount(), "", "", "", -1);
        }

    }


    /**
     * 下单
     *
     * @param jsonObject
     * @param distributor
     * @return
     */
    String createOrder(JSONObject jsonObject, DistributorRespDTO distributor) {

        // body - 创建订单请求内容
        JSONObject body = jsonObject.getJSONObject("body");
        String otaOrderNo = body.getStr("orderId");

        RLock lock = null;
        try {
            // 封装订单
            TradeOrderCreateReqDTO tradeOrderCreateReqDTO = this.parseOrder(body, distributor);
            // 校验、下单，锁用于防止同时下单
            String key = tradeOrderCreateReqDTO.getExternalOrderNo() + "#" + distributor.getId();
            lock = redissonClient.getLock(key);
            if (lock.tryLock(10, 60, TimeUnit.SECONDS)) {
                // 订单创建（不支付）
                return meiTuanService.savePayOrder(tradeOrderCreateReqDTO.getExternalOrderNo(), tradeOrderCreateReqDTO, false, distributor);
            } else {
                log.error("美团：重复提交");
                return meituanReturn("410", "重复提交", distributor.getOtaAccount(), "",
                        tradeOrderCreateReqDTO.getExternalOrderNo(), "", 0);
            }

        } catch (ServerException e) {
            return meituanReturn("410", e.getMessage(), distributor.getOtaAccount(), "", otaOrderNo, "", 0);
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "美团下单", "createOrder", otaOrderNo, e.getMessage());
            return meituanReturn("410", "下单失败", distributor.getOtaAccount(), "", otaOrderNo, "", 0);
        } finally {
            if (null != lock && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * @param body        请求内容
     * @param distributor 分销商
     * @return com.wisesoft.ticket.api.entity.order.OrderEntity
     * @description 封装下单
     */
    private TradeOrderCreateReqDTO parseOrder(JSONObject body, DistributorRespDTO distributor) throws Exception {
        Long distributorId = distributor.getId();
        TradeOrderCreateReqDTO order = new TradeOrderCreateReqDTO();
        List<TradeOrderCreateReqDTO.OrderItem> itemlList = new ArrayList<>();
        List<TradeOrderCreateReqDTO.OrderDetail> detailList = new ArrayList<>();
        order.setExternalOrderNo(body.getStr("orderId"));
        Date travelDate = body.getDate("travelDate");
        order.setTravelDate(travelDate);
        Long partnerDealId = body.getLong("partnerDealId");
        String partnerPrimaryKey = body.getStr("partnerPrimaryKey");

        DistributorProductRespDTO distributorProduct = otaDistributorApi.getDistributorProductById(partnerDealId).getCheckedData();
        ProductSpuRespDTO productSpuRespDTO = productApi.getProduct(distributorProduct.getProductId()).getCheckedData();
        TradeOrderCreateReqDTO.OrderProperty orderProperty = OtaConvert.INSTANCE.convert(distributorId.toString(), distributor.getName());
        order.setOrderProperty(orderProperty);
        //剧场票sku维度唯一标识ID，对接多层价格日历V2接口的产品创建订单必传
        if (StringUtils.isNotBlank(partnerPrimaryKey)){
            String useDate = DateUtil.format(travelDate, DatePattern.PURE_DATE_PATTERN);
            String decryptStr = AesUtil.decryptBase64(partnerPrimaryKey, useDate + StrUtil.sub(distributor.getSecret(), 0, 8));
            order.setRoutePlanId(Long.parseLong(decryptStr));
        }else {
            order.setRoutePlanId(distributorProduct.getRoutePlanId());
        }
        // 购买数量
        Integer ticketsCount = body.getInt("quantity");
        //已下架
        if (null == distributorProduct || distributorProduct.getIsUse() != 1) {
            throw exception0(421, "此产品已下架，全部日期场次均不可售");
        }

        // 单位:元
        BigDecimal price = distributorProduct.getSalePrice();

//        if (null != travelDate) {
//            StockDayPriceQueryReqDTO queryReqDTO = new StockDayPriceQueryReqDTO();
//            queryReqDTO.setBusinessId(partnerDealId);
//            queryReqDTO.setType(StockTypeEnum.DISTRIBUTOR_PRODUCT.getValue());
//            queryReqDTO.setDate(DateUtil.format(travelDate, DatePattern.NORM_DATE_FORMATTER));
//
//            //查询日历价格
//            CommonResult<StockDayPriceRespDTO> stockDayPrice = stockApi.getStockDayPrice(queryReqDTO);
//            if (stockDayPrice.getCode() == 0) {
//                price = stockDayPrice.getData().getPrice();
//            }
//        }
        if (price != null) {
            BigDecimal orderAmt = price.multiply(new BigDecimal(ticketsCount));
            order.setAmount(orderAmt);
            order.setPayAmount(orderAmt);
        }

        TradeOrderCreateReqDTO.OrderItem orderItem = new TradeOrderCreateReqDTO.OrderItem();
        orderItem.setProductId(productSpuRespDTO.getId().toString());
        orderItem.setProductType(PRODUCT_BASE_TYPE_SELF);
        orderItem.setUseDate(travelDate);
        List<DistributorProductRespDTO.DistributorProjectDTO> distributorProjectDTOS = otaDistributorApi.getProductByDistributor(distributor.getId(), distributorProduct.getProductId(), distributorProduct.getId()).getCheckedData();
        TradeOrderCreateReqDTO.OrderDetail orderDetail = OtaConvert.INSTANCE.convert(distributorProduct, ticketsCount, distributorProjectDTOS, price);
        // 日库存验证 420 库存不足
        //TODO
        //验证分销产品库存
//        StockDistributorDeductReqDTO deductReqDTO = new StockDistributorDeductReqDTO();
//        deductReqDTO.setDistributorProductId(partnerDealId);
//        deductReqDTO.setNum(ticketsCount);
//        deductReqDTO.setUseDate(travelDate);
//        try {
//            stockApi.deductDistributorStock(deductReqDTO).getCheckedData();
//        } catch (Exception e) {
//            log.error("美团扣减库存异常：{}", e.getMessage());
//            throw exception0(420, "库存不足");
//        }

        JSONArray visitorsJson = JSONUtil.parseArray(body.getStr("visitors"));

        List<TradeOrderCreateReqDTO.Visitor> visitors = new ArrayList<>();
        // 售票规则验证购票方式(1:实名购票，0：无需实名)
        ProductSaleRuleRespDTO productSaleRule = productSpuRespDTO.getProductSaleRule();
        if (productSaleRule != null && productSaleRule.getIsLimitType() == 1 && CollUtil.isNotEmpty(visitorsJson)) {

            for (int i = 0; i < visitorsJson.size(); i++) {
                JSONObject item = visitorsJson.getJSONObject(i);
                TradeOrderCreateReqDTO.Visitor visitor = new TradeOrderCreateReqDTO.Visitor();
                // 游客身份证
                String credentials = item.getStr("credentials");
                List<Map<String, Object>> cardList = this.gtCard(credentials);
                if (CollUtil.isNotEmpty(cardList)) {
                    Map<String, Object> cardMap = cardList.get(0);
                    visitor.setCredentialNo(MapUtil.getStr(cardMap, "cardId"));
                    Integer cardType = changeCertificateType(MapUtil.getInt(cardMap, "cardType"));
                    visitor.setCredentialType(cardType);
                }
                if (StringUtils.isEmpty(visitor.getCredentialNo())) {
                    // log.info("美团：创建订单：游客身份证为空不能下单.");
                    throw new ServerException(410, "游客身份证为空不能下单！");
                }
                visitor.setName(item.getStr("name"));
                visitor.setMobile(item.getStr("mobile"));
                visitors.add(visitor);
                if (i == 0) {
                    // 联系人，取第一个游客信息
                    TradeOrderCreateReqDTO.OrderUser orderUser = new TradeOrderCreateReqDTO.OrderUser();
                    orderUser.setCredentialType(visitor.getCredentialType());
                    orderUser.setMobile(visitor.getMobile());
                    orderUser.setContactName(visitor.getName());
                    orderUser.setCredentialNo(visitor.getCredentialNo());
                    order.setOrderUser(orderUser);
                }
            }
        } else {
            // 非实名制购票
            if (productSaleRule != null && productSaleRule.getIsLimitType() == 0) {
                if (CollUtil.isNotEmpty(visitorsJson)) {
                    for (int i = 0; i < visitorsJson.size(); i++) {
                        JSONObject item = visitorsJson.getJSONObject(i);
                        if (i == 0) {
                            // 联系人，取第一个游客信息
                            TradeOrderCreateReqDTO.OrderUser orderUser = new TradeOrderCreateReqDTO.OrderUser();
                            orderUser.setMobile(item.getStr("mobile"));
                            order.setOrderUser(orderUser);
                        }
                    }
                }
                visitors = OtaConvert.INSTANCE.convertOtaVisitorList(ticketsCount);
            }
        }
        //一票多人
        if (productSaleRule != null && productSaleRule.getOneLimitNum() != null && ticketsCount * productSaleRule.getOneLimitNum() > visitors.size()) {
            throw exception0(410, "一张票需要" + productSaleRule.getOneLimitNum() + "个游玩人信息");
        }
        orderDetail.setVisitorList(visitors);
        detailList.add(orderDetail);
        orderItem.setDetailList(detailList);
        itemlList.add(orderItem);
        order.setItemlList(itemlList);
        return order;
    }

    /**
     * @param otaOrderNo  第三方订单号
     * @param order       订单信息
     * @param isPay       是否支付
     * @param distributor 分销商
     * @return java.lang.String
     * @description 带事务的创建订单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String savePayOrder(String otaOrderNo, TradeOrderCreateReqDTO order, boolean isPay, DistributorRespDTO distributor) {
        //重复验证
        TradeOrderDO orderDB = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(otaOrderNo, order.getOrderProperty().getSourceId());
        if (orderDB == null) {
            //创建订单
            CommonResult<TradeOrderRespDTO> result = tradeOrderService.createOrder(order);
            if (result.isError()) {
                return meituanReturn("410", result.getMsg(), distributor.getOtaAccount(), "", otaOrderNo, "", 0);
            }
            TradeOrderRespDTO tradeOrderRespDTO = result.getCheckedData();
            //支付订单
            if (isPay) {
                PayOrderReqVO payOrderReqVO = new PayOrderReqVO();
                payOrderReqVO.setAccountId(distributor.getId().toString());
                //ota
                payOrderReqVO.setAccountType(PayAccountTypeEnum.OTA.getType());

                payOrderReqVO.setOrderId(tradeOrderRespDTO.getOrderId());
                payOrderReqVO.setPayAmount(order.getPayAmount());
                //预付款>授信
                Object[] payTypes = distributor.getPayType().split(",");
                if (CollectionUtils.containsAny("1", payTypes)) {
                    payOrderReqVO.setPayType(PayTypeEnum.ADVANCE_PAYMENT.getType());
                } else if (CollectionUtils.containsAny("2", payTypes)) {
                    payOrderReqVO.setPayType(PayTypeEnum.CREDIT_GRANTING.getType());
                }
                //统一支付、扣款、修改订单状态
                CommonResult<PayOrderRespVO> payResult = tradeOrderPayService.payOrder(payOrderReqVO);
                if (payResult.isError()) {
                    return meituanReturn("499", result.getMsg(), distributor.getOtaAccount(), tradeOrderRespDTO.getOrderNo(), otaOrderNo, "", 0);

                }
            }
            //返回订单创建成功
            return meituanReturn("200", "success", distributor.getOtaAccount(), tradeOrderRespDTO.getOrderNo(),
                    otaOrderNo, "", 0);
        } else {
            //订单已存在，表示也下单成功，返回订单号
            return meituanReturn("200", "success", distributor.getOtaAccount(), orderDB.getOrderNo(),
                    otaOrderNo, "", 0);
        }
    }


    /**
     * @param distributor 请求参数
     * @return java.lang.String
     * @description 支付订单
     */
    String orderPay(JSONObject jsonObject, DistributorRespDTO distributor) {

        // usGroup - body - 创建订单请求内容
        JSONObject jsonBodyObj = jsonObject.getJSONObject("body");
        // 订单ID
        String partnerOrderId = jsonBodyObj.getStr("partnerOrderId");
        // 美团订单Id
        String orderId = jsonBodyObj.getStr("orderId");
        // usGroup - partnerId - 合作方ID
        String partnerId = jsonObject.getStr("partnerId");
        RLock lock = null;
        try {
            // 查询我方订单信息
            TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getOrderByNo(partnerOrderId);

            List<TradeDetailDO> ticketList = tradeOrderRepositoryService.queryDetailByOrderId(tradeOrderDO.getId());
            // 订单不存在或已过期
            if (tradeOrderDO == null || tradeOrderDO.getOrderStatus() <= 0) {
                return meituanReturn("599", "订单号不存在或已过期", partnerId, partnerOrderId, orderId, "", 0);
                // 订单已支付
            } else if (tradeOrderDO.getOrderStatus() > 1) {
                return meituanReturn("200", "success", partnerId, partnerOrderId, orderId, ticketList, 4);
            }
            String key = tradeOrderDO.getExternalOrderNo() + "#" + distributor.getId();
            lock = redissonClient.getLock(key);
            if (lock.tryLock(10, 60, TimeUnit.SECONDS)) {
                PayOrderReqVO payOrderReqVO = new PayOrderReqVO();
                payOrderReqVO.setAccountId(distributor.getId().toString());
                //ota
                payOrderReqVO.setAccountType(PayAccountTypeEnum.OTA.getType());

                payOrderReqVO.setOrderId(tradeOrderDO.getId());
                payOrderReqVO.setPayAmount(tradeOrderDO.getPayAmount());
                //预付款>授信
                Object[] payTypes = distributor.getPayType().split(",");
                if (CollectionUtils.containsAny("1", payTypes)) {
                    payOrderReqVO.setPayType(PayTypeEnum.ADVANCE_PAYMENT.getType());
                } else if (CollectionUtils.containsAny("2", payTypes)) {
                    payOrderReqVO.setPayType(PayTypeEnum.CREDIT_GRANTING.getType());
                }
                //统一支付、扣款、修改订单状态
                CommonResult<PayOrderRespVO> result = tradeOrderPayService.payOrder(payOrderReqVO);
                if (result.isSuccess()) {
                    return meituanReturn("200", "success", partnerId, partnerOrderId, orderId, ticketList, 4);
                } else {
                    return meituanReturn("599", result.getMsg(), partnerId, partnerOrderId, orderId, "", 0);
                }
            } else {
                return meituanReturn("599", "重复提交", partnerId, partnerOrderId, orderId, "", 0);
            }
        } catch (ServerException e) {
            // log.info("美团：解析失败." + e.getMessage());
            return meituanReturn("501", "虚拟账户余额不足", partnerId, partnerOrderId, orderId, "", 0);
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "美团订单支付", "orderPay", orderId, e.getMessage());
            return meituanReturn("599", "订单出票失败", partnerId, partnerOrderId, orderId, "", 0);
        } finally {
            if (null != lock && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

    }

    /**
     * @param jsonObject
     * @param distributor 请求参数
     * @return java.lang.String
     * @description 取消订单(退单 、 退票)
     */
    String refundOrder(JSONObject jsonObject, DistributorRespDTO distributor) {
        // usGroup - body - 创建订单请求内容
        String body = jsonObject.getStr("body");
        JSONObject jsonBodyObj = JSONUtil.parseObj(body);
        // 美团订单id
        String otaOrderId = jsonBodyObj.getStr("orderId");
        // 美团退款流水ID，退款流水号标记唯一退款申请，幂等
        String refundId = jsonBodyObj.getStr("refundId");
        // 合作方订单ID
        String orderNo = jsonBodyObj.getStr("partnerOrderId");
        // 产品ID
        String partnerDealId = jsonBodyObj.getStr("partnerDealId");
        RLock lock = null;
        try {
            // 退票列表
            JSONArray voucherList = jsonBodyObj.getJSONArray("voucherList");
            voucherList = voucherList == null ? new JSONArray() : voucherList;
            List<String> voucherCodeList = new ArrayList<>();
            for (Object e : voucherList) {
                String code = (String) e;
                voucherCodeList.add(code);
            }
            // 退款票数
            Integer refundQuantity = jsonBodyObj.getInt("refundQuantity");
            // isForceRefund == 1 强制退票
            Integer isForceRefund = jsonBodyObj.getInt("isForceRefund");
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(otaOrderId, distributor.getId().toString());
            if (order == null || !order.getOrderNo().equals(orderNo)) {
                return meituanReturn("699", "订单不存在", distributor.getOtaAccount(), orderNo, otaOrderId, refundId, 3);
            }

            TradeRefundCreateReqDTO refundCreateReqDTO = TradeRefundConvert.INSTANCE.convertOtaRefund(order.getId(), distributor.getId(), isForceRefund, refundId);

            if (CollUtil.isNotEmpty(voucherCodeList)) {
                //获取可退门票
                int refund_count = 0;
                List<TradeDetailDO> detailDOList = tradeOrderRepositoryService.queryDetailByOrderId(order.getId());
                //计算订单金额
                List<TradeRefundCreateReqDTO.RefundItem> refundItemList = new ArrayList<>();
                BigDecimal refundAmt = BigDecimal.ZERO;
                for (TradeDetailDO detailDO : detailDOList) {
                    for (String voucherCode : voucherCodeList) {
                        if (StringUtils.equals(detailDO.getVoucherCode(), voucherCode)) {
                            //验证门票是否可退：0初始状态，1待使用，2已取票，3已核销，4退款中,5已退票，6逾期核销,7已过期
                            if (detailDO.getVoucherStatus() > 0 && detailDO.getVoucherStatus() < 3) {
                                TradeRefundCreateReqDTO.RefundItem refundItem = TradeRefundConvert.INSTANCE.convertRefundItem(detailDO.getActualPrice(), order.getId(), detailDO.getId(), detailDO.getVoucherCode());
                                refundItemList.add(refundItem);
                                refundAmt = refundAmt.add(detailDO.getActualPrice());
                            } else if (detailDO.getVoucherStatus() == 5) {
                                refund_count += 1;
                            }
                        }
                    }
                }
                if (CollUtil.isEmpty(refundItemList) && refundQuantity == refund_count) {
                    return meituanReturn("601", "订单已退票成功", distributor.getOtaAccount(), orderNo,
                            otaOrderId, refundId, 3);
                } else if (refundItemList.size() != refundQuantity) {
                    return meituanReturn("699", "申请退票张数与可退票张数数量不等", distributor.getOtaAccount(), orderNo,
                            otaOrderId, refundId, 3);
                }
                refundCreateReqDTO.setRefundAmount(refundAmt);
                refundCreateReqDTO.setItemlList(refundItemList);
            }
            Long effectyCount = tradeOrderRepositoryService.queryEffectyCount(order.getId());
            if (effectyCount == 0) {
                return meituanReturn("699", "该订单无可退门票", distributor.getOtaAccount(), orderNo,
                        otaOrderId, refundId, 3);
            }

            // 执行退单
            String key = order.getExternalOrderNo() + "#" + order.getSourceId();
            lock = redissonClient.getLock(key);

            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {

                CommonResult<TradeRefundRespDTO> refundResult = tradeRefundService.refund(refundCreateReqDTO);
                String code = "699";
                String msg = refundResult.getMsg();
                if (refundResult.isSuccess()) {
                    code = "200";
                    msg = "success";
                } else if (Objects.equals(refundResult.getCode(), REFUND_AUDITING.getCode())) {
                    // 602:退款审批中
                    code = "602";
                    msg = refundResult.getMsg();
                }
                return meituanReturn(code, msg, distributor.getOtaAccount(), orderNo, otaOrderId, refundId, 3);

            } else {
                return meituanReturn("699", "重复提交", distributor.getOtaAccount(), orderNo, otaOrderId, refundId, 3);
            }
        } catch (ServerException ex) {
            log.error("分销{},{},{},{}", "美团退单", "refundOrder", orderNo, ex.getMessage());
            return meituanReturn("699", ex.getMessage(), distributor.getOtaAccount(), orderNo, otaOrderId,
                    refundId, 3);
        } catch (Exception ex) {
            log.error("分销{},{},{},{}", "美团退单", "refundOrder", orderNo, ex.getMessage());
            return meituanReturn("699", "退单异常", distributor.getOtaAccount(), orderNo, otaOrderId,
                    refundId, 3);
        } finally {
            if (null != lock && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * @param distributor HttpServletRequest
     * @param distributor 请求参数
     * @return java.lang.String
     * @description 查询订单
     */
    String queryOrder(JSONObject jsonObject, DistributorRespDTO distributor) {

        try {
            // usGroup - body - 创建订单请求内容
            String body = jsonObject.getStr("body");
            JSONObject jsonBodyObj = JSONUtil.parseObj(body);
            // usGroup - partnerOrderId String必须合作方订单ID
            String orderNo = jsonBodyObj.getStr("partnerOrderId");
            // usGroup - orderId 美团订单Id
            String otaOrderNo = jsonBodyObj.getStr("orderId");
            // 查询订单
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(otaOrderNo, distributor.getId().toString());
            if (order == null || !order.getOrderNo().equals(orderNo)) {
                return meituanReturn("399", "订单不存在", distributor.getOtaAccount(), orderNo, otaOrderNo,
                        "", 0);
            }
            // 组装返回数据
            JSONObject object = new JSONObject();
            object.put("code", 200);
            object.put("describe", "success");
            object.put("partnerId", Long.valueOf(distributor.getOtaAccount()));
            JSONObject bodyReturn = new JSONObject();
            bodyReturn.put("orderId", Long.valueOf(order.getExternalOrderNo()));
            bodyReturn.put("partnerOrderId", order.getOrderNo());
            bodyReturn.put("orderStatus", changeOrderStatusToMt(order.getOrderStatus()));
            List<TradeDetailDO> detailList = tradeOrderRepositoryService.queryDetailByOrderId(order.getId());
            bodyReturn.put("orderQuantity", detailList.size());
            bodyReturn.put("voucherType", 1);
            // 后台管理的互联网地址
            String travelhubOpsInternetUrl = configApi.getConfigValueByKey("travelhubOpsInternetUrl");


            int useCount = 0;
            int cancelCount = 0;
            JSONArray voucherList = new JSONArray();
            JSONArray voucherPics = new JSONArray();
            for (TradeDetailDO tradeDetailDO : detailList) {

                JSONObject vchObj = new JSONObject();
                vchObj.put("voucher", tradeDetailDO.getVoucherCode());
                vchObj.put("voucherPics",
                        travelhubOpsInternetUrl + OtaConstants.toQrCodeUrl + tradeDetailDO.getVoucherCode());
                voucherPics.add(
                        travelhubOpsInternetUrl + OtaConstants.toQrCodeUrl + tradeDetailDO.getVoucherCode());
                vchObj.put("voucherInvalidTime",
                        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tradeDetailDO.getUseDate()));
                int status = 3;
                //0初始状态，1待使用，2已取票，3已核销，4退款中,5已退票，6逾期核销,7已过期
                // 退票
                if (ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.REFUND.getStatus())
                        || ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.PART_REFUND.getStatus())
                        || ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.REFUNDING.getStatus())) {
                    status = 2;
                    cancelCount += 1;
                    // 3:部分检票 4:全部检票 6:逾期核销(已使用)
                } else if (ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.VERIFIED.getStatus())
                        || ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.OVERDUE_VERIFIED.getStatus())
                        || ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.EXPIRED.getStatus())) {
                    status = 1;
                    useCount += 1;
                    // 未使用
                } else if (ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.UNUSED.getStatus()) || ObjectUtil.equals(tradeDetailDO.getVoucherStatus(), VoucherStatusEnum.TOOK.getStatus())) {
                    status = 0;
                }
                vchObj.put("quantity", 1);
                vchObj.put(OTA_STATUS, status);
                voucherList.add(vchObj);
            }
            bodyReturn.put("voucherPics", voucherPics);
            bodyReturn.put("voucherList", voucherList);
            bodyReturn.put("usedQuantity", useCount);
            // 退票数
            bodyReturn.put("refundedQuantity", cancelCount);
            object.put("body", bodyReturn);
            return object.toString();
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "美团", "queryOrder", jsonObject.getJSONObject("body").getStr("orderId"), "查询订单");
            return meituanReturn("399", "订单查询失败", distributor.getOtaAccount(), "", "", "", -1);
        }
    }

    /**
     * @param distributor 请求参数
     * @return java.lang.String
     * &#064;description  关闭订单
     */
    String cancelOrder(JSONObject jsonObject, DistributorRespDTO distributor) {
        try {
            // usGroup - partnerId - 合作方ID
            String partnerId = jsonObject.getStr("partnerId");
            // usGroup - body - 创建订单请求内容
            String body = jsonObject.getStr("body");
            JSONObject jsonBodyObj = JSONUtil.parseObj(body);
            // usGroup - orderId 美团订单Id
            String otaOrderNo = jsonBodyObj.getStr("orderId");
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(otaOrderNo, distributor.getId().toString());
            if (order == null) {
                return meituanReturn("300", "订单不存在！", partnerId, "", otaOrderNo, "", 0);
            }
            CommonResult<?> commonResult = tradeOrderPayService.cancelOrder(order.getId());
            if (commonResult.isSuccess()) {
                return meituanReturn("200", "关闭成功！", distributor.getOtaAccount(), order.getId().toString(), order.getExternalOrderNo(), "", -1);
            } else {
                return meituanReturn("300", commonResult.getMsg(), distributor.getOtaAccount(), order.getId().toString(), order.getExternalOrderNo(), "", -1);
            }
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "美团", "closeOrder", jsonObject.getJSONObject("body").getStr("orderId"), "关闭订单异常");
            return meituanReturn("300", "关闭失败！", distributor.getOtaAccount(), "", "", "", -1
            );
        }
    }

    /**
     * @param distributor HttpServletRequest
     * @param jsonObject  请求参数
     * @return java.lang.String
     * @description 已退款消息(退单 、 退票)
     */
    String refundedOrder(JSONObject jsonObject, DistributorRespDTO distributor) {
        // 请求参数
        // usGroup - body - 创建订单请求内容
        String body = jsonObject.getStr("body");
        JSONObject jsonBodyObj = JSONUtil.parseObj(body);
        // 合作方订单ID
        String orderNo = jsonBodyObj.getStr("partnerOrderId");
        // 美团退款流水ID，退款流水号标记唯一退款申请，幂等
        String refundId = jsonBodyObj.getStr("refundSerialNo");
        // 美团订单id
        String otaOrderId = jsonBodyObj.getStr("orderId");
        RLock lock = null;
        try {
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(otaOrderId, distributor.getId().toString());
            if (order == null || !order.getOrderNo().equals(orderNo)) {
                return meituanReturn("300", "订单不存在", distributor.getOtaAccount(), orderNo, otaOrderId,
                        refundId, -1);
            }
            // 订单不存在或订单无效时 同意退款
            if (order == null || order.getOrderStatus().equals(TradeOrderStatusEnum.CANCELED.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.FAIL.getStatus())) {
                return meituanReturn("200", "无效订单", distributor.getOtaAccount(), orderNo, otaOrderId,
                        refundId, -1);
            } else if (order.getOrderStatus().equals(TradeOrderStatusEnum.INIT.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.UNPAID.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.PAYING.getStatus())) {
                CommonResult<?> commonResult = tradeOrderPayService.cancelOrder(order.getId());
                if (commonResult.isSuccess()) {
                    return meituanReturn("200", "退款成功", distributor.getOtaAccount(), orderNo, otaOrderId, refundId, -1);
                } else {
                    return meituanReturn("300", commonResult.getMsg(), distributor.getOtaAccount(), orderNo, otaOrderId, refundId, -1);
                }
            } else if (order.getOrderStatus() >= TradeOrderStatusEnum.PAID.getStatus()
                    && order.getOrderStatus() < TradeOrderStatusEnum.ALL_REFUND.getStatus()) {
                // 执行退单
                String key = order.getExternalOrderNo() + "#" + distributor.getId();
                lock = redissonClient.getLock(key);
                if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                    Integer isForceRefund = 1;
                    TradeRefundCreateReqDTO refundCreateReqDTO = TradeRefundConvert.INSTANCE.convertOtaRefund(order.getId(), distributor.getId(), isForceRefund, order.getOrderNo());
                    // 退票列表
                    List<TradeRefundCreateReqDTO.RefundItem> refundItemList = new ArrayList<>();
                    BigDecimal refundAmt = BigDecimal.ZERO;
                    JSONArray voucherList = jsonBodyObj.getJSONArray("voucherList");
                    voucherList = voucherList == null ? new JSONArray() : voucherList;
                    List<String> ticketNoList = new ArrayList<>();
                    if (CollUtil.isNotEmpty(voucherList)) {
                        for (Object e : voucherList) {
                            String code = (String) e;
                            TradeDetailDO tradeDetail = tradeOrderRepositoryService.queryDetailVoucherCode(code, order.getId());
                            TradeRefundCreateReqDTO.RefundItem refundItem = TradeRefundConvert.INSTANCE.convertRefundItem(tradeDetail.getActualPrice(), order.getId(), tradeDetail.getId(), tradeDetail.getVoucherCode());
                            refundAmt = refundAmt.add(tradeDetail.getActualPrice());
                            refundItemList.add(refundItem);
                        }
                    }
                    refundCreateReqDTO.setRefundAmount(refundAmt);
                    refundCreateReqDTO.setItemlList(refundItemList);
                    tradeRefundService.refund(refundCreateReqDTO);
                    return meituanReturn("200", "success", distributor.getOtaAccount(), orderNo, otaOrderId, refundId, -1);
                } else {
                    return meituanReturn("300", "重复提交", distributor.getOtaAccount(), orderNo, otaOrderId, refundId, -1);
                }
            } else {
                return meituanReturn("200", "已退款", distributor.getOtaAccount(), orderNo, otaOrderId, refundId, -1);
            }

        } catch (
                ServerException ex) {
            log.error("分销{},{},{},{}", "美团已退款消息", "refundedOrder", orderNo, ex.getMessage());
            return meituanReturn("300", ex.getMessage(), distributor.getOtaAccount(), orderNo, otaOrderId,
                    refundId, -1);
        } catch (
                Exception ex) {
            log.error("分销{},{},{},{}", "美团已退款消息", "refundedOrder", orderNo, ex.getMessage());
            return meituanReturn("300", "已退款消息处理异常", distributor.getOtaAccount(), orderNo, otaOrderId,
                    refundId, -1);
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

    }

    /**
     * 拉取多层价格日历V2
     *
     * @return
     */
    String pullMtTicketLevelPriceStockV2(JSONObject jsonObject, DistributorRespDTO distributor) {
        try {
            String body = jsonObject.getStr("body");
            JSONObject jsonBodyObj = JSONUtil.parseObj(body);
            // 合作方产品ID
            Long productId = jsonBodyObj.getLong("partnerDealId");
            String startDate = jsonBodyObj.getStr("startTime");
            String endDate = jsonBodyObj.getStr("endTime");
            //是否异步返回数据标识，0表示同步,1表示异步；默认为同步方式
            Integer asyncType = jsonBodyObj.getInt("asyncType");
            if (null != asyncType && asyncType == 1) {
                String data = distributor.getId() + "_" + productId + "_" + startDate + "_" + endDate;
                tradePublishUtils.publishOtaProductPriceStockNotifyMsg(data);
                return meituanReturn("200", "success", distributor.getOtaAccount(), productId.toString(), "", null, 7);
            } else {
                JSONArray bodyArray = this.queryMtpTicketLevelPriceStock(productId, startDate, endDate, distributor.getSecret());
                return meituanReturn("200", "success", distributor.getOtaAccount(), productId.toString(), "", bodyArray, 7);
            }
        } catch (ServerException ex) {
            log.error("分销{}：", "拉取多层价格日历V2", ex);
            return meituanReturn("500", ex.getMessage(), distributor.getOtaAccount(), "", "", "", -1);
        } catch (Exception ex) {
            log.error("美团拉取多层价格日历V2：", ex);
            return meituanReturn("500", "拉取多层价格日历V2理异常", distributor.getOtaAccount(), "", "", "", -1);
        }

    }

    /**
     * 异步推送日历价格库存
     *
     * @param distributor
     * @param productId
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public void asynOtaProductPriceStockNotice(DistributorRespDTO distributor, Long productId, String startTime, String endTime) {
        String sendUrl = "";
        String param = "";
        try {
            Map<String, Object> paramMap = new HashMap<>(16);
            JSONObject reqParams = new JSONObject();
            // 合作方ID  该ID不明确，需要通过BA认证生成
            reqParams.set("partnerId", Long.valueOf(distributor.getOtaAccount()));
            // 合作方产品ID
            reqParams.set("partnerDealId", productId);
            // 价格日历的开始时间
            reqParams.set("startTime", startTime);
            // 价格日历的开始时间
            reqParams.set("endTime", endTime);
            JSONArray bodyArray = this.queryMtpTicketLevelPriceStock(productId, startTime, endTime,distributor.getSecret());
            reqParams.put("body", bodyArray);

            Map<String, String> headMap = new HashMap<>(16);
            headMap.put("Content-Type", "application/json");
            // https://opengateway-trip.meituan.com/rhone/mtp/api/order/consume/notice
//            sendUrl = "https://connectivity-adapter.meituan.com/rhone/mtp/api/level/price/notice/v2";
            sendUrl = "";
            // 认证信息：BA认证
            String uri = sendUrl.substring(sendUrl.replace("//", "_").indexOf("/") + 1);
            String method = "POST";
            Map<String, String> baAuth = MeiTuanUSGroupAuthUtils
                    .generateSign(method, uri, distributor.getAppid(), distributor.getSecret(), "");
            headMap.putAll(baAuth);
            headMap.put(MeiTuanUSGroupAuthUtils.HTTP_HEADER_PARTNERID, distributor.getOtaAccount());
            param = reqParams.toString();
            // 发送POST请求给 美团
            log.info("美团多层价格日历变化通知V2;入参：" + param);
            String discardJsonStr = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .headerMap(headMap, false)
                    .body(param)
                    .timeout(15000)
                    .execute().body();

            log.info("美团多层价格日历变化通知V2;美团返回：" + discardJsonStr);
            JSONObject res = JSONUtil.parseObj(discardJsonStr);
            // 记录消费通知
            boolean codeFlag = res != null && "200".equals(res.getStr("code"));
            if (codeFlag) {
//                notifyLogService.saveNotifyLog(map, sendUrl, param, discardJsonStr, 1);
            }
            // 记录消费通知
//            notifyLogService.saveNotifyLog(map, sendUrl, param, discardJsonStr, 0);

        } catch (Exception e) {
            // 记录消费通知
//            notifyLogService.saveNotifyLog(map, sendUrl, param, e.getMessage(), 0);
            log.error("分销{}", "美团多层价格日历变化V2通知失败", e);

        }
    }

    private JSONArray queryMtpTicketLevelPriceStock(Long distributorProductId, String startTime, String endTime, String secretKey) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDate = LocalDate.parse(startTime, formatter);
        LocalDate endDate = LocalDate.parse(endTime, formatter);
        JSONArray bodyArray = new JSONArray();
        // 遍历日期范围（包括开始和结束日期）
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            CommonResult<DistributorProductRespDTO> calendarProductRes = otaDistributorApi.getCalendarProductById(distributorProductId, date.format(formatter));
            if (calendarProductRes.isError()) {
                continue;
            }
            DistributorProductRespDTO distributorProduct = calendarProductRes.getCheckedData();
            List<RoutePlanRespDTO> routePlanRespDTOList = tradeDetailMapper.getRoutePlanByProductId(distributorProduct.getProductId(), 1L);
            LocalDate finalDate = date;
            List<RoutePlanRespDTO> planList = new ArrayList<>(routePlanRespDTOList.stream().filter(plan -> {
                        LocalDate useStartTime = DateUtil.parse(plan.getUseStartTime()).toLocalDateTime().toLocalDate();
                        ;
                        LocalDate useEndTime = DateUtil.parse(plan.getUseEndTime()).toLocalDateTime().toLocalDate();
                        ;
                        return !useStartTime.isAfter(finalDate) && !useEndTime.isBefore(finalDate);
                    }).collect(Collectors.groupingBy(RoutePlanRespDTO::getName,
                            Collectors.collectingAndThen(
                                    Collectors.maxBy(Comparator.comparingInt(RoutePlanRespDTO::getPlanType)),
                                    Optional::get)))
                    .values());

            if (CollUtil.isNotEmpty(planList)) {
                planList.forEach(plan -> {
                    JSONObject bodyObject = new JSONObject();
                    JSONObject skuObject = new JSONObject();
                    JSONArray levelInfoList = new JSONArray();
                    JSONObject levelInfo = new JSONObject();
                    //层级序号，最多支持3层
                    levelInfo.set("levelNo", 1);
                    //层级名称信息，如：”上午场“、”贵宾席“
                    levelInfo.set("levelName", plan.getName());
                    levelInfoList.add(levelInfo);
                    skuObject.set("levelInfoList", levelInfoList);
                    Map<String, Object> timeMap = tradeDetailMapper.queryRoutePlanDetailTimeByPlanId(plan.getId());
                    //场次开始时间，要求格式：HH:mm
                    skuObject.set("startTime", MapUtil.getStr(timeMap, "startTime"));
                    //场次结束时间，要求格式：HH:mm
                    skuObject.set("endTime", MapUtil.getStr(timeMap, "endTime"));
                    Date useDate = Date.from(finalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
//                    StockDistributorDeductReqDTO deductReqDTO = new StockDistributorDeductReqDTO();
//                    deductReqDTO.setDistributorProductId(distributorProduct.getProductId());
//                    deductReqDTO.setDistributorId(distributorProduct.getDistributorId());
//                    deductReqDTO.setRoutePlanId(plan.getId().toString());
//                    deductReqDTO.setNum(0);
//                    deductReqDTO.setUseDate(useDate);
//                    // 原价（划线价），单位：分
//                    CommonResult<Integer> distributorStock = stockApi.getDistributorStock(deductReqDTO);
//                    // 日库存(指剩余可售卖库存)
//                    if (distributorStock.isSuccess()) {
//                        Integer stock = distributorStock.getCheckedData();
//                        bodyObject.set("stock", stock);
//                    } else {
//                        bodyObject.set("stock", 0);
//                    }
                    //价格日历的日期,格式 yyyy-MM-dd
                    bodyObject.set("priceDate", DateUtil.formatDate(useDate));
                    //sku维度信息
                    bodyObject.set("skuInfo", skuObject);
                    //市场价，单位：元，保留两位小数
                    bodyObject.set("marketPrice", distributorProduct.getSalePrice());
                    //美团售卖价，单位：元，保留两位小数
                    bodyObject.set("mtPrice", distributorProduct.getSalePrice());
                    //结算价，单位：元，保留两位小数
                    bodyObject.set("settlementPrice", distributorProduct.getSalePrice());
                    String dateStr = DateUtil.format(useDate, DatePattern.PURE_DATE_PATTERN);
                    String partnerPrimaryKey = AesUtil.encryptBase64(plan.getId().toString(), dateStr+StrUtil.sub(secretKey,0,8));
                    bodyObject.set("partnerPrimaryKey", partnerPrimaryKey);
                    bodyArray.add(bodyObject);
                });
            } else {
                JSONObject bodyObject = new JSONObject();
                JSONObject skuObject = new JSONObject();
                JSONArray levelInfoList = new JSONArray();
                JSONObject levelInfo = new JSONObject();
                //层级序号，最多支持3层
                levelInfo.set("levelNo", 1);
                //层级名称信息，如：”上午场“、”贵宾席“
                levelInfo.set("levelName", "");
                levelInfoList.add(levelInfo);
                skuObject.set("levelInfoList", levelInfoList);

                //场次开始时间，要求格式：HH:mm
                skuObject.set("startTime", "");
                //场次结束时间，要求格式：HH:mm
                skuObject.set("endTime", "");
                Date useDate = Date.from(finalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
//                StockDistributorDeductReqDTO deductReqDTO = new StockDistributorDeductReqDTO();
//                deductReqDTO.setDistributorProductId(distributorProduct.getProductId());
//                deductReqDTO.setDistributorId(distributorProduct.getDistributorId());
//                deductReqDTO.setNum(0);
//                deductReqDTO.setUseDate(useDate);
                // 原价（划线价），单位：分
//                CommonResult<Integer> distributorStock = stockApi.getDistributorStock(deductReqDTO);
                // 日库存(指剩余可售卖库存)
//                if (distributorStock.isSuccess()) {
//                    Integer stock = distributorStock.getCheckedData();
//                    bodyObject.set("stock", stock);
//                } else {
//                    bodyObject.set("stock", 0);
//                }
                //价格日历的日期,格式 yyyy-MM-dd
                bodyObject.set("priceDate", DateUtil.formatDate(useDate));
                //sku维度信息
                bodyObject.set("skuInfo", skuObject);
                //市场价，单位：元，保留两位小数
                bodyObject.set("marketPrice", distributorProduct.getSalePrice());
                //美团售卖价，单位：元，保留两位小数
                bodyObject.set("mtPrice", distributorProduct.getSalePrice());
                //结算价，单位：元，保留两位小数
                bodyObject.set("settlementPrice", distributorProduct.getSalePrice());
                String dateStr = DateUtil.format(useDate, DatePattern.PURE_DATE_PATTERN);
                String partnerPrimaryKey = AesUtil.encryptBase64(distributorProduct.getRoutePlanId().toString(), dateStr+dateStr);
                bodyObject.set("partnerPrimaryKey", partnerPrimaryKey);
                bodyArray.add(bodyObject);
            }
        }
        return bodyArray;
    }

    /**
     * @param distributor 分销商信息
     * @param map         请求参数
     * @return boolean
     * @description 消费通知天时同程
     */
    @Override
    public boolean consumeNotice(DistributorRespDTO distributor, Map<String, Object> map) {
        String orderNo = MapUtil.getStr(map, "orderNo");
        String sendUrl = "";
        String param = "";
        try {
            Long orderId = MapUtil.getLong(map, "orderId");

            Map<String, Object> ticketMap = tradeOrderRepositoryService.queryDetailCountByOrderId(orderId);
            int totalTicket = MapUtil.getInt(ticketMap, "allCount");
            int checkCount = MapUtil.getInt(ticketMap, "checkCount");
            int refundCount = MapUtil.getInt(ticketMap, "refundCount");
            int outTimeCount = MapUtil.getInt(ticketMap, "outTimeCount");
            int partCount = MapUtil.getInt(ticketMap, "partCount");
            int useCount = checkCount + partCount + outTimeCount;
            Map<String, Object> paramMap = new HashMap<>(16);
            JSONObject body = new JSONObject();
            // 判断组合票
            String otaOrderNo = MapUtil.getStr(map, "external_order_no");
            // 已使用：消费更新接口
            // 合作方ID  该ID不明确，需要通过BA认证生成
            paramMap.put("partnerId", Long.valueOf(distributor.getOtaAccount()));
            // 美团订单ID
            body.put("orderId", Long.valueOf(otaOrderNo));
            // 我方订单ID
            body.put("partnerOrderId", orderNo);
            // 订单门票总数量
            body.put("quantity", totalTicket);
            // 订单已使用门票总数量
            body.put("usedQuantity", useCount);
            // 订单已退款门票总数量
            body.put("refundedQuantity", refundCount);
            JSONArray voucherList = new JSONArray();
            JSONObject visObj = new JSONObject();
            // voucher String必须凭证码
            visObj.put("voucher", MapUtil.getStr(map, "voucherCode"));
            // voucherPics String可选二维码凭证
            visObj.put("voucherPics", "");
            // voucherInvalidTime String必须凭证被消费或者被退款或者凭证最初生效时间;格式:YYYY-MM-DD HH:MM:SS 与status配合使用，status是退款则是退款时间，status是消费则是消费时间，status是未使用则是凭证生效时间
            String dateTime = MapUtil.getStr(map, "checkTime");
            if (StrUtil.isBlank(dateTime)) {
                visObj.put("voucherInvalidTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            } else {
                visObj.put("voucherInvalidTime", DateUtil.parse(MapUtil.getStr(map, "checkTime"), DatePattern.NORM_DATETIME_PATTERN));
            }

            // quantity Integer (int32)必须与status配合使用，此状态下的凭证代表的数量
            visObj.put("quantity", 1);
            // status Integer (int32)必须凭证状态，0为未使用，1为已使用，2为已退款，3为已废弃（对应的门票还未消费，但是此凭证码作废了）
            int status = 3;

            int voucherStatus = MapUtil.getInt(map, "voucherStatus");
            //0初始状态，1待使用，2已取票，3已核销，4退款中,5已退票，6逾期核销,7已过期,8部分核销
            // 退票
            if (ObjectUtil.equals(voucherStatus, VoucherStatusEnum.REFUND.getStatus())
                    || ObjectUtil.equals(voucherStatus, VoucherStatusEnum.PART_REFUND.getStatus())
                    || ObjectUtil.equals(voucherStatus, VoucherStatusEnum.REFUNDING.getStatus())) {
                status = 2;
                // ，3已核销，6逾期核销,7已过期,8部分核销
            } else if (ObjectUtil.equals(voucherStatus, VoucherStatusEnum.VERIFIED.getStatus())
                    || ObjectUtil.equals(voucherStatus, VoucherStatusEnum.OVERDUE_VERIFIED.getStatus())
                    || ObjectUtil.equals(voucherStatus, VoucherStatusEnum.EXPIRED.getStatus())
                    || ObjectUtil.equals(voucherStatus, VoucherStatusEnum.PART_VERIFIED.getStatus())) {
                status = 1;
                // 未使用
            } else if (ObjectUtil.equals(voucherStatus, VoucherStatusEnum.UNUSED.getStatus()) || ObjectUtil.equals(voucherStatus, VoucherStatusEnum.TOOK.getStatus())) {
                status = 0;
            }
            visObj.put(OTA_STATUS, status);
            voucherList.add(visObj);
            body.put("voucherList", voucherList);
            paramMap.put("body", body);
            JSONObject jsonObject = JSONUtil.parseObj(paramMap);
            Map<String, String> headMap = new HashMap<>(16);
            headMap.put("Content-Type", "application/json");
            // https://opengateway-trip.meituan.com/rhone/mtp/api/order/consume/notice
            sendUrl = distributor.getServerUrl() + "/consume/notice";
            // 认证信息：BA认证
            String uri = sendUrl.substring(sendUrl.replace("//", "_").indexOf("/") + 1);
            String method = "POST";
            Map<String, String> baAuth = MeiTuanUSGroupAuthUtils
                    .generateSign(method, uri, distributor.getAppid(), distributor.getSecret(), "");
            headMap.putAll(baAuth);
            headMap.put(MeiTuanUSGroupAuthUtils.HTTP_HEADER_PARTNERID, distributor.getOtaAccount());
            param = jsonObject.toString();
            // 发送POST请求给 美团
            log.info("美团核销通知,orderNo=" + orderNo + ";consumeNotice;入参：" + param);
            String discardJsonStr = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .headerMap(headMap, false)
                    .body(param)
                    .timeout(15000)
                    .execute().body();

            log.info("美团核销通知,orderNo=" + orderNo + ";consumeNotice;美团返回：" + discardJsonStr);
            JSONObject res = JSONUtil.parseObj(discardJsonStr);
            // 记录消费通知
            boolean codeFlag = res != null && "200".equals(res.getStr("code"));
            if (codeFlag) {
                notifyLogService.saveNotifyLog(map, sendUrl, param, discardJsonStr, 1);
                return true;
            }
            // 记录消费通知
            notifyLogService.saveNotifyLog(map, sendUrl, param, discardJsonStr, 0);
            return false;
        } catch (Exception e) {
            // 记录消费通知
            notifyLogService.saveNotifyLog(map, sendUrl, param, e.getMessage(), 0);
            log.error("分销{},{},{}", "美团消费通知失败", "consumeNotice", orderNo, e);
            return false;
        }
    }


    /**
     * @param distributor 分销商信息
     * @param map         参数
     * @return boolean
     * @description 退票结果异步通知美团
     */
    @Override
    public boolean refundNotice(DistributorRespDTO distributor, Map<String, Object> map) {
        String sendUrl = "";
        String param = "";
        String discardJsonStr = "";
        // 订单ID
        String orderNo = MapUtil.getStr(map, "order_no");
        String refundNo = MapUtil.getStr(map, "refund_id");
        map.put("pid", orderNo);
        map.put("ticket_no", refundNo);
        try {
            Map<String, Object> paramMap = new HashMap<>(16);
            // 审核状态：0待审核，1审核通过，2审核驳回
            String auditStatus = MapUtil.getStr(map, "audit_status");
            boolean auditStatusFlag = StrUtil.equals(auditStatus, "1");
            boolean auditSatausRejectFlag = StrUtil.equals(auditStatus, "0");
            if (auditStatusFlag) {
                // 交互码 200为退款成功
                paramMap.put("code", 200);
                // 交互描述
                paramMap.put("describe", "success");
            } else if (auditSatausRejectFlag) {
                // 交互码 604为产品本身不能退款 699为其它退款错误
                paramMap.put("code", 604);
                // 交互描述
                paramMap.put("describe", "产品本身不能退款");
            }
            // 合作方ID
            paramMap.put("partnerId", Long.valueOf(distributor.getOtaAccount()));

            JSONObject body = new JSONObject();
            // 美团订单ID
            body.put("orderId", Long.valueOf(MapUtil.getStr(map, "external_order_no")));
            // 美团退款流水ID
            body.put("refundId", refundNo);
            // 订单ID
            body.put("partnerOrderId", orderNo);
            // 请求时间
            body.put("requestTime", MapUtil.getStr(map, "create_time"));
            // 响应时间
            body.put("responseTime", DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            paramMap.put("body", body);
            JSONObject jsonObject = JSONUtil.parseObj(paramMap);
            Map<String, String> headMap = new HashMap<>(16);
            headMap.put("Content-Type", "application/json");
            // https://opengateway-trip.meituan.com/rhone/mtp/api/order/refund/notice
            sendUrl = distributor.getServerUrl() + "/refund/notice";
            // 认证信息：BA认证
            String uri = sendUrl.substring(sendUrl.replace("//", "_").indexOf("/") + 1);
            String method = "POST";
            Map<String, String> baAuth = MeiTuanUSGroupAuthUtils
                    .generateSign(method, uri, distributor.getAppid(), distributor.getSecret(), "");
            headMap.putAll(baAuth);
            headMap.put(MeiTuanUSGroupAuthUtils.HTTP_HEADER_PARTNERID, distributor.getOtaAccount());
            param = jsonObject.toString();
            // 发送POST请求给 美团
            log.info("退票结果异步通知美团,orderNo=" + orderNo + ";refundNotice;入参：" + param);
            discardJsonStr = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .headerMap(headMap, false)
                    .body(param)
                    .timeout(15000)
                    .execute().body();
            log.info("退票结果异步通知美团,orderNo=" + orderNo + ";refundNotice;美团返回：" + discardJsonStr);
            JSONObject res = JSONUtil.parseObj(discardJsonStr);
            // 处理结果返回码
            boolean codeFlag = res != null && "200".equals(res.getStr("code"));
            if (codeFlag) {
                // 退票结果异步通知美团
                notifyLogService.saveNotifyLog(map, sendUrl, param, discardJsonStr, 1);
                return true;
            }
            // 退票结果异步通知美团
            notifyLogService.saveNotifyLog(map, sendUrl, param, discardJsonStr, 0);
            return false;
        } catch (Exception e) {
            // 退票结果异步通知美团
//            notifyLogService.saveNotifyLog(map, sendUrl, param, discardJsonStr + "==" + e.getMessage(), 0);
            log.error("分销{},{},{},{}", "退票结果异步通知美团", "refundNotice", orderNo, e.getMessage());
            return false;
        }
    }

    /**
     * @param json 请求参数
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.String>>
     * @description 获取证件
     */
    public List<Map<String, Object>> gtCard(String json) {
        // 证件号码,json 对象,属性key是证件类型, value 是证件号码 如 {0=xxxxx} 表示, 身份证号为 'xxxxx',其中0表示身份证，1表示护照，2表示军官证，3表示台胞证，4表示港澳通行证，5表示大陆居民往来台湾通行证
        // 6表示港澳居民来往内地通行证，7表示外国人永久居留证，8表示港澳台居民居住证
        List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
        Map<String, Object> map = null;
        if (StringUtils.isEmpty(json)) {
            return mapList;
        }
        JSONObject obj = JSONUtil.parseObj(json);
        if (obj.containsKey("0")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("0"));
            map.put("cardType", 0);
            mapList.add(map);
        } else if (obj.containsKey("1")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("1"));
            map.put("cardType", 1);
            mapList.add(map);
        } else if (obj.containsKey("2")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("2"));
            map.put("cardType", 2);
            mapList.add(map);
        } else if (obj.containsKey("3")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("3"));
            map.put("cardType", 3);
            mapList.add(map);
        } else if (obj.containsKey("4")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("4"));
            map.put("cardType", 4);
            mapList.add(map);
        } else if (obj.containsKey("5")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("5"));
            map.put("cardType", 5);
            mapList.add(map);
        } else if (obj.containsKey("6")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("6"));
            map.put("cardType", 6);
            mapList.add(map);
        } else if (obj.containsKey("7")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("7"));
            map.put("cardType", 7);
            mapList.add(map);
        } else if (obj.containsKey("8")) {
            map = new HashMap<>();
            map.put("cardId", obj.getStr("8"));
            map.put("cardType", 8);
            mapList.add(map);
        }
        return mapList;
    }

    private Integer changeCertificateType(Integer type) {
        Integer certificateType = null;
        switch (type) {
            case 0:
                certificateType = CredentialTypeEnum.ID_CARD.getType();
                break;
            case 1:
                certificateType = CredentialTypeEnum.PASSPORT.getType();
                break;
            case 3:
                certificateType = CredentialTypeEnum.TAI_BAO_CARD.getType();
                ;
                break;
            case 4:
                certificateType = CredentialTypeEnum.GANG_AO_PASS_CARD.getType();
                ;
                break;
            case 5:
                certificateType = CredentialTypeEnum.TW_PASS_CARD.getType();
                ;
                break;
            case 7:
                certificateType = CredentialTypeEnum.FORERVER_LIVE_CARD.getType();
                ;
                break;
            case 8:
                certificateType = CredentialTypeEnum.GAT_PASS_CARD.getType();
                ;
                break;
            default:
                certificateType = CredentialTypeEnum.OTHER.getType();
                ;
                break;
        }

        return certificateType;
    }

    /**
     * @param orderStatus 订单状态
     * @return int
     * @description 转换订单状态
     */
    public static int changeOrderStatusToMt(Integer orderStatus) {
        // 2为创建订单成功，3为创建订单失败，4为出票成功，5为出票中 不能明确出票状态时使用该状态，6为出票失败
        int rtStatus = 6;
        switch (orderStatus) {
            // 下单失败
            case -1:
                rtStatus = 3;
                break;
            // 待支付
            case 0:
                rtStatus = 5; // 已下单未支付
                break;
            case 1:
                rtStatus = 2;
                break;
            // 已取票
            case 2:
                rtStatus = 5;
                break;
            case 3:
                rtStatus = 5; // 已取票
                break;
            // 已检票
            case 4:
                rtStatus = 5; // 已检票
                break;
            // 已退票
            case 5:
                rtStatus = 6; // 已退票
                break;
            // 待使用
            case 6:
                rtStatus = 4; //
                break;
            // 部分核销
            case 7:
                rtStatus = 4; //
                break;
            // 全部核销
            case 8:
                rtStatus = 4; //
                break;
            // 部分退款
            case 9:
                rtStatus = 4; //
                break;
            // 退款中
            case 10:
                rtStatus = 4; //
                break;
            // 全部退款
            case 11:
                rtStatus = 4;
                break;
            default:
                break;
        }
        return rtStatus;
    }

    /**
     * @param code         编码
     * @param msg          消息
     * @param otaAccountId 分销账户
     * @param orderId      订单号
     * @param otaOrderId   分销订单号
     * @param obj          内容
     * @param type         类型
     * @return java.lang.String
     * @description 美团操作，统一返回
     */
    public String meituanReturn(String code, String msg, String otaAccountId, String orderId,
                                String otaOrderId, Object obj, int type) {
        JSONObject object = new JSONObject();
        // 退单成功，返回退单成功信息,并更新记录
        if (StringUtils.isNotBlank(code)) {
            object.set("code", Long.valueOf(code));
        }
        object.set("describe", msg);
        if (StringUtils.isNotBlank(otaAccountId)) {
            object.set("partnerId", Long.valueOf(otaAccountId));
        }
        if (type != -1) {
            JSONObject body = new JSONObject();
            if (StringUtils.isNotBlank(otaOrderId)) {
                body.set("orderId", Long.valueOf(otaOrderId));
            }
            body.set("partnerOrderId", orderId);
            // 退单
            if (type == 3) {
                body.set("refundId", obj);
            }
            // 支付成功
            if (type == 4) {
                body.set("voucherType", 1);
                body.set("voucherAdditionalList", new JSONArray());
                List<TradeDetailDO> ticketList = (List<TradeDetailDO>) obj;
                JSONArray vouchers = new JSONArray();
                JSONArray voucherPics = new JSONArray();
                // 后台管理的互联网地址
                String travelhubOpsInternetUrl = configApi.getConfigValueByKey("travelhubOpsInternetUrl");
                if (ticketList != null) {
                    for (TradeDetailDO tradeDetailDO : ticketList) {
                        vouchers.add(tradeDetailDO.getVoucherCode());
                        voucherPics.add(travelhubOpsInternetUrl + OtaConstants.toQrCodeUrl + tradeDetailDO.getVoucherCode());
                    }
                }
                body.set("voucherPics", voucherPics);
                body.set("vouchers", vouchers);
            }
            object.set("body", body);
        }
        if (type == 7) {
            object.set("body", obj);
            //商家产品id
            object.set("partnerDealId", orderId);
        }
        return object.toString();
    }

    /**
     * @param body    请求参数
     * @param request HttpServletRequest
     * @return com.wisesoft.ticket.api.resolver.SignBackBean
     * @description 签名认证
     */
    SignBackBean checkSign(String body, HttpServletRequest request) {
        SignBackBean bean = new SignBackBean();
        bean.setSignSuccess(false);
        try {
            // 解析参数 封装下单参数
            JSONObject jsonObject = JSONUtil.parseObj(body);
            bean.setBody(jsonObject);
            // 获取分销商信息
            DistributorRespDTO distributor = otaDistributorApi.getDistributorByOtaAccount(jsonObject.getStr("partnerId"), "").getCheckedData();
            if (distributor == null) {
                log.error("分销{},{},{},{}", "美团", "checkSign", "", "帐号校验失败");
                bean.setErrCode("402");
                bean.setErrMsg("帐号校验失败");
                return bean;
            }
            bean.setDistributor(distributor);
            if (!MeiTuanUSGroupAuthUtils
                    .checkSign(request, distributor.getAppid(), distributor.getSecret())) {
                log.error("分销{},{},{},{}", "美团", "checkSign", "", "sign校验失败");
                bean.setErrCode("410");
                bean.setErrMsg("sign校验失败");
                return bean;
            }
            bean.setSignSuccess(true);
            return bean;
        } catch (ServerException e) {
            log.error("分销{},{},{},{}", "美团", "checkSign", "", e.getMessage());
            bean.setErrCode("410");
            bean.setErrMsg(e.getMessage());
            return bean;
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "美团", "checkSign", "", e.getMessage());
            bean.setErrCode("410");
            bean.setErrMsg("sign校验失败");
            return bean;
        }
    }
}
