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.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yc.ship.framework.common.exception.ServerException;
import com.yc.ship.framework.common.lang.Asserts;
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.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.*;
import com.yc.ship.module.product.enums.RefundRuleEnum;
import com.yc.ship.module.trade.api.dto.*;
import com.yc.ship.module.trade.api.dto.ota.SpecificationsDTO;
import com.yc.ship.module.trade.api.dto.ota.SyncTicketRespDTO;
import com.yc.ship.module.trade.api.dto.ota.TiktokCalendarReqDTO;
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.dataobject.order.TradeVisitorDO;
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.Sha256Util;
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.TradeOrderRuleValidate;
import com.yc.ship.module.trade.service.order.TradeOrderService;
import com.yc.ship.module.trade.service.ota.OtaService;
import com.yc.ship.module.trade.service.ota.TiktokCalendarService;
import com.yc.ship.module.trade.service.ota.TiktokLifeService;
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.TradeRefundRepositoryService;
import com.yc.ship.module.trade.service.refund.TradeRefundService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
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.TravelhubDictTypeConstants.PRODUCT_BASE_TYPE_SELF;
import static com.yc.ship.module.trade.service.ota.OtaService.tiktokCalendar;


/**
 * @description：抖音日历票
 */
@Service(tiktokCalendar)
@Slf4j
public class TiktokCalendarServiceImpl implements TiktokCalendarService, OtaService {

    @Resource
    private OtaDistributorApi otaDistributorApi;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ProductApi productApi;
    @Resource
    private TradeOrderService tradeOrderService;
    @Resource
    private TradeOrderRepositoryService tradeOrderRepositoryService;
    @Resource
    private TradeOrderPayService tradeOrderPayService;
    @Resource
    private NotifyLogService notifyLogService;
    @Resource
    private TiktokLifeService tiktokLifeService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private TradeRefundService tradeRefundService;

    @Resource
    private TradeRefundRepositoryService tradeRefundRepositoryService;
    @Resource
    private TradeDetailMapper tradeDetailMapper;


//    public String getShopPoiId(DistributorRespDTO distributor, String orderId){
//        // https://open.douyin.com/goodlife/v1/shop/poi/query/?page=1&size=20&account_id=12345
//        String sendUrl = distributor.getServerUrl() + "/goodlife/v1/shop/poi/query/";
//        String param = "page=1&size=20&third_id=" + orderId;
//        String result = SendHttps.sendGet(sendUrl, param, null);
//        return "";
//    }

    public boolean getVerifyResults(String sendResult, List<String> ticketNoList, String sendUrl, String param, Map<String, Object> map) {
        JSONObject returnObject = JSONUtil.parseObj(sendResult);
        JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("data"));
        // 是否包含验券结果信息
        boolean verifyResultsFlag = dataObject.containsKey("verify_results");
        if (verifyResultsFlag) {
            // 验券结果
            JSONArray verifyResults = JSONUtil.parseArray(dataObject.getStr("verify_results"));
            if (null != ticketNoList && !ticketNoList.isEmpty()) {
                JSONObject result = verifyResults.getJSONObject(0);
                // 记录消费通知 0 验券成功 1208 券码已核销
                boolean resultFlag = "0".equals(result.getStr("result")) || "1208".equals(result.getStr("result"));
                if (resultFlag) {
                    notifyLogService.saveNotifyLog(map, sendUrl, param, result.toString(), 1);
                    return true;
                    // 记录消费通知
                } else {
                    notifyLogService.saveNotifyLog(map, sendUrl, param, sendResult, 0);
                    return false;
                }
            }
        }
        // 未返回验券结果时,记录返回内容
        notifyLogService.saveNotifyLog(map, sendUrl, param, sendResult, 0);
        return false;
    }

    /**
     * @param distributor 分销商信息
     * @return java.lang.String
     * @description 获取客户端token
     * @author 杨涛
     * @date 2025/4/18 14:45
     */
    public String getClientToken(DistributorRespDTO distributor) {
        String clientToken = "";
        String clientTokenKey = "tiktok_calendar_access_token_" + distributor.getAppid();
        String accessToken = stringRedisTemplate.opsForValue().get(clientTokenKey);
        if (StringUtils.isNotBlank(accessToken)) {
            clientToken = accessToken;
        } else {
            RLock lock = null;
            try {
                lock = redissonClient.getLock("tiktok_access_token_lock");
                if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                    Map<String, String> header = new HashMap<>(16);
                    JSONObject reqBody = new JSONObject();
                    reqBody.set("client_key", distributor.getAppid());
                    reqBody.set("client_secret", distributor.getSecret());
                    reqBody.set("grant_type", "client_credential");
                    // serverurl:https://open.douyin.com
//                    String clientTokenResult = SendHttps.sendPostForm(distributor.getServerUrl() + "/oauth/client_token/", clientTokenParam, header);
                    String clientTokenResult = HttpUtil.createPost(distributor.getServerUrl() + "/oauth/client_token/").contentType("application/json")
                            .body(reqBody.toString())
                            .timeout(15000)
                            .execute().body();
                    log.error("分销{}：,{}", "抖音日历票生成client_token", clientTokenResult);
                    if (StringUtils.isNotBlank(clientTokenResult)) {
                        JSONObject clientTokenObject = JSONUtil.parseObj(clientTokenResult);
                        JSONObject clientTokenDataObject = JSONUtil.parseObj(clientTokenObject.getStr("data"));
                        if (clientTokenDataObject.containsKey("access_token") &&
                                StringUtils.isNotBlank(clientTokenDataObject.getStr("access_token"))) {
                            clientToken = clientTokenDataObject.getStr("access_token");
                            // 118 分钟 7080秒
                            stringRedisTemplate.opsForValue().set(clientTokenKey, clientToken, 7080, TimeUnit.SECONDS);
                        }
                    }
                }
            } catch (Exception ex) {
                log.error("分销{},{},{},{}", "生成 client_token", "/oauth/client_token/", "", ex.getMessage());
            } finally {
                if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
//        return "douyin.23UfpdWgJkgUyy1o1s6ytexso5TOFq5hViGnpxouScp3V38qeMYlmGy9Emg5";
        return clientToken;
    }

    /**
     * @param appId 账号
     * @return com.wisesoft.ticket.api.entity.ota.DistributorRespDTO
     * @description 获取抖音租户信息
     * @author 杨涛
     * @date 2025/4/15 10:55
     */
    DistributorRespDTO getServer(String appId) {
        // 获取抖音租户信息
        CommonResult<DistributorRespDTO> result = otaDistributorApi.getDistributorByOtaAccount(appId, OtaConstants.OTA_TIKTOK_TYPECODE);
        if (result.isError()) {
            return null;
        }
        return result.getCheckedData();
    }

    /**
     * @param appId 账号
     * @return com.wisesoft.ticket.api.entity.ota.DistributorRespDTO
     * @description 获取抖音租户信息
     */
    DistributorRespDTO getServerTiktokLife(String appId) {
        //获取分销商信息
        CommonResult<DistributorRespDTO> result = otaDistributorApi.getDistributorByOtaAccount(appId, OtaConstants.OTA_TIKTOK_LIFE_TYPECODE);
        if (result.isError()) {
            return null;
        }
        return result.getCheckedData();
    }

    DistributorRespDTO getServerTiktokCalendar(String appId) {
        // 获取抖音租户信息
        CommonResult<DistributorRespDTO> result = otaDistributorApi.getDistributorByOtaAccount(appId, OtaConstants.OTA_TIKTOK_CALENDAR_TYPECODE);
        if (result.isError()) {
            return null;
        }
        return result.getCheckedData();
    }

    /**
     * @return java.lang.String
     * @description 抖音日历票-同步产品请求
     */
    @Override
    public CommonResult<?> doRequestTiktokCalendarSyncTicket(TiktokCalendarReqDTO reqDTO) {
        String path = reqDTO.getMethodName();
        // 分销商编号
        Long distributorId = reqDTO.getDistributorId();
        // 产品编号
        Long productId = reqDTO.getProductId();

        DistributorRespDTO distributorRespDTO = otaDistributorApi.getDistributorById(distributorId);
        ProductSpuRespDTO productSpuRespDTO = productApi.getProduct(productId).getCheckedData();
        // 抖音日历票同步产品
        if ("syncTicketSave".equals(path)) {

            return syncTicketSave(distributorRespDTO, productSpuRespDTO, reqDTO);
            // 抖音日历票同步状态
        } else if ("syncTicketQuery".equals(path)) {

            return syncTicketQuery(distributorRespDTO, productSpuRespDTO, reqDTO);
            // 抖音日历票同步价格库存
        } else if ("syncTicketCalendarSave".equals(path)) {
            return syncTicketCalendarSave(distributorRespDTO, productSpuRespDTO, reqDTO);
            // 抖音日历票同步上下架
        } else if ("syncProductOperate".equals(path)) {
//            // 分销商商家ID
//            String externalAccountId = reqDTO.getExternalAccountId();
//            Integer opType = reqDTO.getOpType();
//            return syncProductOperate(distributorRespDTO, productSpuRespDTO, reqDTO);
        }
        return null;
    }

    /**
     * @param product 票种信息
     * @return com.alibaba.fastjson.JSONObject
     * @description 设置商品类目
     */
    private String setCategoryId(ProductSpuRespDTO product) {
        // 18004009 园内套票(不含门票)
        boolean taopFlagNotTicket = (product.getProductName().contains("+") || product.getProductName().contains("套票")) && !product.getProductName().contains("门");
        // 18004008 园内套票(含门票)
        boolean taopFlag = (product.getProductName().contains("+") || product.getProductName().contains("套票") || product.getProductName().contains("门船")) && product.getProductName().contains("门");
        // 18004004 园内游玩项目
        // boolean ywTypeFlag = !tt.getName().contains("门票");
        // 18004003 园内交通
        boolean jtdFlag = product.getProductName().contains("索道") || product.getProductName().contains("观光车");
        // 18003007 国内短途游轮
        boolean boatFlag = product.getProductName().contains("船") || product.getProductName().contains("水") || product.getProductName().contains("江");
        // 18004001 单景点门票
        String categoryId = "";
        if (taopFlagNotTicket) {
            categoryId = "18004009";
        } else if (taopFlag) {
            categoryId = "18004008";
//            // 游玩且非交通
//        } else if (ywTypeFlag && !jtdFlag) {
//            categoryId = "18004004";
        } else if (jtdFlag) {
            categoryId = "18004003";
        } else if (boatFlag) {
            categoryId = "18003007";
        } else {
            categoryId = "18004001";
        }
        return categoryId;
    }

    public Map<String, String> setHeadMap(DistributorRespDTO distributor) {
        Map<String, String> headParam = new HashMap<String, String>(16);
        // 一次验券的标识(客户端token)
        String clientToken = getClientToken(distributor);
        headParam.put("access-token", clientToken);
        // 沙箱token验证开关  默认填写1
        // headParam.put("X-Sandbox-Token", "1");
        return headParam;
    }

    /**
     * // 日历票创建/更新商品接口
     *
     * @param distributor
     * @param productSpu
     * @param reqDTO
     * @return
     */
    public CommonResult<?> syncTicketSave(DistributorRespDTO distributor, ProductSpuRespDTO productSpu, TiktokCalendarReqDTO reqDTO) {
        // 日历票创建/更新商品接口
        String code = "201";
        String message = "";
        String externalTicketTypeIdTemp = "";
        String externalProductIdTemp = "";
        // 分销商商家ID
        String externalAccountId = reqDTO.getExternalAccountId();
        // 分销商门店ID
        String externalPoiIds = reqDTO.getExternalPoiIds();
        // 分销商票种ID
        String externalTicketTypeId = reqDTO.getExternalTicketTypeId();
        // 分销商商品ID
        String externalProductId = reqDTO.getExternalProductId();
        try {
            // 售票规则验证购票方式(1:实名购票，0：无需实名)
            ProductSaleRuleRespDTO saleRule = productSpu.getProductSaleRule();
            // 退票规则
            ProductRefundRuleRespDTO productRefundRule = productSpu.getProductRefundRule();
            // 票种详情
            CategoryRespDTO categoryResp = productApi.getCategory(productSpu.getCategoryId()).getCheckedData();
            // serverurl:https://open.douyin.com
            String sendUrl = distributor.getServerUrl() + "/goodlife/v1/trip/ticket/save";
            JSONObject requestBody = new JSONObject();
            // 商家ID
            requestBody.put("account_id", externalAccountId);
            // 商品
            JSONObject ticket = new JSONObject();
            // 商品类目 18004001单景点门票 18004008园内套票
            if (StringUtils.isNotBlank(reqDTO.getExternalCategoryId())) {
                ticket.put("category_id", reqDTO.getExternalCategoryId());
            } else {
                ticket.put("category_id", setCategoryId(productSpu));
            }

            // 抖音POI_ID（即门店id，可以多个）
            String poiIdsStr = externalPoiIds;
            if (StringUtils.isNotBlank(poiIdsStr)) {
                String[] pois = poiIdsStr.split(",");
                List<String> poiIds = Arrays.asList(pois);
                if (!poiIds.isEmpty()) {
                    // 抖音POI_ID
                    ticket.put("poi_ids", poiIds);
                }
            }
            // 票种名称
            ticket.put("ticket_type_name", categoryResp.getCateName());
            // 商家票种id
            ticket.put("ticket_type_out_id", categoryResp.getId());
            // 票种id，对应M-Spu，抖音侧下发id，创建时不传
            if (StringUtils.isBlank(externalTicketTypeId)) {
//                externalTicketTypeId = categoryResp.getExternalTicketTypeId();
            }
            if (StringUtils.isNotBlank(externalTicketTypeId)) {
                ticket.put("ticket_type_id", externalTicketTypeId);
            }
            // 商品名称 票种下的商品罗列
            ticket.put("product_name", productSpu.getProductName());
            // 商家商品id，更新商品时
            ticket.put("product_out_id", productSpu.getId());
            // 商品id，更新商品时
            String productId = externalProductId;
            if (StringUtils.isNotBlank(productId)) {
                ticket.put("product_id", productId);
            }
            // 商品图片URL列表
            List<String> imgUrls = new ArrayList<>();
            String coverImgHorizontal = productSpu.getCoverImgHorizontal();
            String coverImgVertical = productSpu.getCoverImgVertical();

            if (StringUtils.isBlank(coverImgHorizontal) && StringUtils.isBlank(coverImgVertical)) {
                // return productSpu.getProductName()+"产品图片必传，请上传后重试";
                return CommonResult.error(202, productSpu.getProductName() + "产品图片必传，请上传后重试");
            } else {
                if (StringUtils.isNotBlank(coverImgHorizontal)) {
                    imgUrls.add(coverImgHorizontal);
                }
                if (StringUtils.isNotBlank(coverImgVertical)) {
                    imgUrls.add(coverImgVertical);
                }
            }
            ticket.put("img_urls", imgUrls);
            // 人群类型 -1 无限制 Child=1 儿童 Adult=2 成人 Old=3 老人
            JSONArray crowds = new JSONArray();
            JSONObject crowd = new JSONObject();
            crowd.put("crowd_type", -1);
            // 人群数量
            crowd.put("crowd_num", 1);
            crowds.add(crowd);
            // 人群列表
            ticket.put("crowds", crowds);

            // 票务规则，包含凭证方式、券码类型、券码服务商
            JSONObject ticketRule = new JSONObject();
            // 1:实名购票，3：无需实名
            if (saleRule != null && saleRule.getIsLimitType() == 0) {
                List<String> codeSendingInfo = new ArrayList<>();
                // 券号
                codeSendingInfo.add("2");
                // 券码
                codeSendingInfo.add("3");
                ticketRule.put("code_sending_info", codeSendingInfo);
            } else if (saleRule != null && ObjectUtil.equals(saleRule.getIsLimitType(), "1")) {
                List<String> codeSendingInfo = new ArrayList<>();
                // 身份证
                codeSendingInfo.add("1");
                // 券号
                codeSendingInfo.add("2");
                // 券码
                codeSendingInfo.add("3");
                ticketRule.put("code_sending_info", codeSendingInfo);
            } else {
                List<String> codeSendingInfo = new ArrayList<>();
                // 身份证
                codeSendingInfo.add("1");
                ticketRule.put("code_sending_info", codeSendingInfo);
            }
            // 2 第三方券
            ticketRule.put("code_type", 2);
            // 人群列表
            ticket.put("ticket_rule", ticketRule);
            // 是否需要换票 取票后是否可退 0 否 1 是
            if (productRefundRule.getRefundRule() == 0) {
                ticket.put("need_ticket", true);
            } else {
                ticket.put("need_ticket", false);
            }

            // 游玩人留资
            JSONObject customerReservedInfo = new JSONObject();
            // 1:实名购票，3：无需实名
            if (saleRule != null && ObjectUtil.equals(saleRule.getIsLimitType(), "1")) {
                // 游玩人留资信息类型 3 全部游玩人
                customerReservedInfo.put("reserved_type", 3);
                List<String> reservedRules = new ArrayList<>();
                // 姓名
                reservedRules.add("1");
                // 手机号
                reservedRules.add("2");
                if (StringUtils.isNotBlank(saleRule.getLimitCertificate())) {
                    List<String> certificateList = Arrays.asList(saleRule.getLimitCertificate().split(","));
                    for (String certificateType : certificateList) {
                        reservedRules.add(this.handleOtaCertificateType(certificateType));
                    }
                } else {
                    // 证件号
                    reservedRules.add("3");
                }
                customerReservedInfo.put("reserved_rules", reservedRules);
            } else {
                // 游玩人留资信息类型 1 无需填写
                customerReservedInfo.put("reserved_type", 1);
            }
            // 人群列表
            ticket.put("customer_reserved_info", customerReservedInfo);

            // 预定时间限制规则
            JSONObject preOrderTime = new JSONObject();
            // 开园时间
            long earliestTime = 0;
//            if (StringUtils.isNotBlank(openStart)) {
//                String[] earliestTimes = openStart.split(":");
//                // 小时 * 3600 秒 + 分钟 * 60 秒
//                earliestTime = (Integer.valueOf(earliestTimes[0]) * 3600) + (Integer.valueOf(earliestTimes[1]) * 60);
//            } else {
//                earliestTime = 28800;
//            }
//            // 闭园时间
            long latestTime = 64800;
            long preTime = 21600;

            // 需提前几天预购
            Integer prebuyLimitDay = saleRule.getPrebuyLimitDay();
            // 提前购买天数非空且大于0时 [0天-30天]
            if (prebuyLimitDay != null && prebuyLimitDay > 0 && prebuyLimitDay <= 30) {
                preTime = 86400 * prebuyLimitDay;
            }
            // 最晚预定时间 以日历票入园当天24点向前计算 单位秒 目前支持传值[0,2592000] [0天-30天]
            preOrderTime.put("pre_time", preTime);
            // 预定后最早入园时间 从预定时间向后计算 单位秒 本期默认为0 购买后可以立刻生效
            preOrderTime.put("enter_time", 0);
            // 人群列表
            ticket.put("pre_order_time", preOrderTime);
            // 有效期规则天数 1 游玩当天有效 2 游玩2天内有效
            ticket.put("ticket_period", productSpu.getDayNum());
            // use_times 入园次数 0 无限制
            ticket.put("use_times", 1);
            // 费用包含说明
            ticket.put("fee_include", productSpu.getProductName());
            // 费用不包含说明
            ticket.put("fee_not_include", "");

            // 退改规则
            JSONObject refundRule = new JSONObject();
            // 是否可以退票 0 否 1 是
            Integer isRefundFlag = productRefundRule.getRefundRule();
            // 过期操作类型 0不处理 1自动结算 2自动核销 3自动退票
            int isOverdueRefund = productRefundRule.getExpireOperateType();
            // 退改规则类型 1：未使用随时退 2：不可退 3：有条件退
            Integer refundType = 1;
            Integer overdueRefundDay = 0;
            if (!Objects.equals(isRefundFlag, RefundRuleEnum.NOT_REFUND.getValue())) {
//                if (StringUtils.isNotBlank(productRefundRule.getRefundChannel())) {
//                    Supplier<Stream<String>> refundChannel = () -> Stream.of(productRefundRule.getRefundChannel().split(","));
//                    // 判断是否在对应渠道过期期限内退票 1：OTA售票
//                    if (refundChannel.get().anyMatch(element -> Integer.parseInt(element) == SellMethodEnum.OTA.getType())) {
//
//                    }
//                }
                //随时退
                if (Objects.equals(isRefundFlag, RefundRuleEnum.REFUND_ANY.getValue())) {
                    refundType = 1;
                } else {
                    //有条件退
                    refundType = 3;
                }
            } else {
                refundType = 2;

            }
            refundRule.set("refund_type", refundType);
//            if (refundType == 1) {
//                // 若选择未用随时退，未填写该字段，则表示离园日24点自动退款 自动退款时间，单位秒
//                refundRule.put("auto_refund_time", 3600);
//            }
            // 是否支持未使用部分退 ture:支持 false:不支持
            refundRule.put("refund_part", true);

            if (refundType == 3) {
                JSONArray refundDetails = new JSONArray();
                JSONObject refundDetail;
                // 当退改规则类型为有条件退时，此字段必填，单位秒 正整数为入园日24点前 负整数为入园日24点后计算
                //如果没配置就不计算手续费
                long refundTime = 86400;
                if (StrUtil.isNotBlank(productRefundRule.getRefundFeeRule())) {
                    //计算手续费
                    JSONArray jsonArray = JSONUtil.parseArray(productRefundRule.getRefundFeeRule());

                    //如果设置了产品使用时间，则以使用时间为准，如果无则按当天0点计算
                    if (StrUtil.isNotBlank(productRefundRule.getRefundFeeStartTime())) {
                        String[] refundFeeStart = productRefundRule.getRefundFeeStartTime().split(":");
                        long refundFeeStartTime = (Integer.valueOf(refundFeeStart[0]) * 3600) + (Integer.valueOf(refundFeeStart[1]) * 60);
                        refundTime = refundTime - refundFeeStartTime;
                        earliestTime = refundFeeStartTime;
                    }
                    //过滤规则
                    for (int i = 0; i < jsonArray.size(); i++) {
                        refundDetail = new JSONObject();
                        JSONObject jsonObject = jsonArray.getJSONObject(i);
                        long minHour = jsonObject.getLong("minHour");
                        long maxHour = jsonObject.getLong("maxHour");
                        BigDecimal percent = jsonObject.getBigDecimal("fee");
                        //退票时间是入园当日24点往前计算,票务按当日24点-使用时间+前几个小时
                        refundDetail.put("refund_time", refundTime + minHour * 3600);
                        // 当退改规则类型为有条件退时，此字段必填 1.金额 2.比例
                        refundDetail.put("refund_fee_type", 2);
                        // 当退改规则类型为有条件退时，此字段必填
                        refundDetail.put("refund_fee", percent);
                        refundDetails.add(refundDetail);
                    }
                } else {
                    refundDetail = new JSONObject();
                    if (productRefundRule.getRefundDay() != null) {
//                        int refundDayTime = productRefundRule.getRefundDay() * 24 * 3600;
                        int refundDayTime = productRefundRule.getRefundDay() * 60;//分钟转为秒
                        refundDetail.put("refund_time", refundDayTime);
                    }
                    // 当退改规则类型为有条件退时，此字段必填 1.金额 2.比例
                    refundDetail.put("refund_fee_type", 1);
                    // 当退改规则类型为有条件退时，此字段必填
                    refundDetail.put("refund_fee", 0);
                    refundDetails.add(refundDetail);
                    // 有条件退必填 可以多个时段阶梯组合
                    refundRule.put("refund_details", refundDetails);

                }
                // 有条件退必填 可以多个时段阶梯组合
                refundRule.put("refund_details", refundDetails);

            }
            // 退改规则
            ticket.put("refund_rule", refundRule);
            // 限购规则 0 不限制
            ticket.put("buy_limit", 0);
            // 其他说明信息 ticketTypeDetailEntity.getBuyDetail()
            ticket.put("other_note", this.removeHtmlTags(productSpu.getBookingInstructions()));
            // 是否为测试商品
            // ticket.put("is_test_data", true);
            // 测试商品的uid
            // List<String> testUid = new ArrayList<>();
            // testUid.add("3742380179328589");
            // ticket.put("test_uid", testUid);
            // 当前景区门票，在游玩当天，可以在园内的游览时间
            JSONObject openingTime = new JSONObject();
            // 当前景区门票最早入园时间 单位秒 如果传0 为0点钟
            openingTime.put("earliest_time", earliestTime);
            // 当前景区门票最晚入园时间 单位秒 必须小于等于86400（24小时）
            openingTime.put("latest_time", latestTime);
            ticket.put("opening_time", openingTime);

            JSONArray ticketSpecifications = new JSONArray();
            List<RoutePlanRespDTO> routePlanRespDTOList = tradeDetailMapper.getRoutePlanByProductId(productSpu.getId(),1L);
            if (CollUtil.isNotEmpty(reqDTO.getSpecificationsList())) {
                for (SpecificationsDTO specificationsDTO : reqDTO.getSpecificationsList()) {
                    JSONObject ticketSpecification = new JSONObject();
                    // 日历票规格，SKU名称 POI名称+票种名称+人群+规格枚举名称
                    ticketSpecification.put("sku_name", productSpu.getProductName() + specificationsDTO.getSkuName());
                    // 收款方式（只支持总店结算）1 总店结算
                    ticketSpecification.put("settle_type", 1);
                    if (CollUtil.isNotEmpty(routePlanRespDTOList)) {
                        JSONArray ticketSessions = new JSONArray();
                        List<RoutePlanRespDTO> planList = routePlanRespDTOList.stream()
                                .collect(Collectors.groupingBy(RoutePlanRespDTO::getName,
                                        Collectors.reducing((a, b) -> a.getPlanType() > b.getPlanType() ? a : b)))
                                .values().stream()
                                .filter(Optional::isPresent)
                                .map(Optional::get)
                                .collect(Collectors.toList());
                        for (RoutePlanRespDTO planRespDTO : planList) {
                            JSONObject ticketSession = new JSONObject();
                            ticketSession.set("ticket_session_name", planRespDTO.getName());
                            ticketSessions.add(ticketSession);
                        }
                        ticketSpecification.set("ticket_session", ticketSessions);
                    }
                    ticketSpecification.set("sku_out_id", specificationsDTO.getDistributorProductId());
                    ticketSpecifications.add(ticketSpecification);
                }
            } else {
                JSONObject ticketSpecification = new JSONObject();
                // 日历票规格，SKU名称 POI名称+票种名称+人群+规格枚举名称
                ticketSpecification.put("sku_name", productSpu.getProductName());
                // 收款方式（只支持总店结算）1 总店结算
                ticketSpecification.put("settle_type", 1);
                // 商家日历票规格id
                ticketSpecification.put("sku_out_id", productSpu.getId());
                if (CollUtil.isNotEmpty(routePlanRespDTOList)) {
                    JSONArray ticketSessions = new JSONArray();
                    List<RoutePlanRespDTO> planList = routePlanRespDTOList.stream()
                            .collect(Collectors.groupingBy(RoutePlanRespDTO::getName,
                                    Collectors.reducing((a, b) -> a.getPlanType() > b.getPlanType() ? a : b)))
                            .values().stream()
                            .filter(Optional::isPresent)
                            .map(Optional::get)
                            .collect(Collectors.toList());
                    for (RoutePlanRespDTO planRespDTO : planList) {
                        JSONObject ticketSession = new JSONObject();
                        ticketSession.set("ticket_session_name", planRespDTO.getName());
                        ticketSessions.add(ticketSession);
                    }
                    ticketSpecification.set("ticket_session", ticketSessions);
                }
                ticketSpecifications.add(ticketSpecification);
            }

            // 日历票规格，单个商品多规格
            ticket.put("ticket_specifications", ticketSpecifications);

            // 产品信息
            requestBody.put("ticket", ticket);

            String param = requestBody.toString();
            Map<String, String> headParam = setHeadMap(distributor);
            String sendResult = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .body(param)
                    .headerMap(headParam, false)
                    .timeout(15000)
                    .execute().body();
            log.error("抖音日历票创建/更新商品返回结果：{}", sendResult);
            // 返回结果为空时
            if (StringUtils.isBlank(sendResult)) {
                return CommonResult.error(201, "抖音日历票创建/更新商品失败");
            }

            JSONObject returnObject = JSONUtil.parseObj(sendResult);
            JSONObject extraObject = JSONUtil.parseObj(returnObject.getStr("extra"));
            boolean errorCodeFlag = StringUtils.equals(extraObject.getStr("error_code"), "0");
            boolean flag = returnObject.containsKey("data");
            if (errorCodeFlag && flag) {
                JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("data"));
                SyncTicketRespDTO syncTicketRespDTO = new SyncTicketRespDTO();
                if (StringUtils.isNotBlank(dataObject.getStr("ticket_type_id"))
                        && StringUtils.isNotBlank(dataObject.getStr("product_id"))) {
                    externalTicketTypeIdTemp = dataObject.getStr("ticket_type_id");
                    externalProductIdTemp = dataObject.getStr("product_id");
                    syncTicketRespDTO.setDistributorId(distributor.getId());
                    syncTicketRespDTO.setProductId(productSpu.getId());
                    syncTicketRespDTO.setExternalTicketTypeId(externalTicketTypeIdTemp);
                    syncTicketRespDTO.setExternalProductId(externalProductIdTemp);
                }

                return CommonResult.success(syncTicketRespDTO, "抖音日历票创建/更新商品成功");
            } else {
                String reason = extraObject.getStr("description") + extraObject.getStr("sub_description") + "(" + extraObject.getStr("logid") + ")";
                message = productSpu.getProductName() + "抖音日历票创建/更新商品失败:" + reason;
                return CommonResult.error(202, message);
            }
        } catch (Exception ex) {
            log.error("抖音日历票创建/更新商品异常:", ex);
            throw exception0(202, "抖音日历票创建/更新商品失败");
        }
    }

    public CommonResult<?> syncTicketQuery(DistributorRespDTO distributor, ProductSpuRespDTO productSpu, TiktokCalendarReqDTO reqDTO) {
        // 日历票商品数据查询
        String code = "201";
        String message = "";
        String externalSkuId = "";
        try {
            // serverurl:https://open.douyin.com
            String sendUrl = distributor.getServerUrl() + "/goodlife/v1/trip/ticket/query";
            HttpRequest request = HttpUtil.createGet(sendUrl);

            String accountId = reqDTO.getExternalAccountId();
            if (StringUtils.isNotBlank(accountId)) {

                request.form("account_id", accountId);
            }
            request.form("page", 1);
            request.form("page_size", 50);

            String productOutIds = productSpu.getId().toString();
            if (StringUtils.isNotBlank(productOutIds)) {
                request.form("product_out_ids", productOutIds);
            }
            String productIds = reqDTO.getExternalProductId();
            if (StringUtils.isNotBlank(productIds)) {
                request.form("product_ids", productIds);
            }
            Map<String, String> headParam = setHeadMap(distributor);

            // 发送请求并获取响应内容
            String result = request.headerMap(headParam, false).execute().body();
            log.error("抖音日历票商品数据查询返回结果：{}", result);
            // 返回结果为空时
            if (StringUtils.isBlank(result)) {
                return CommonResult.error(201, "抖音日历票商品数据查询失败");
            }
            JSONObject returnObject = JSONUtil.parseObj(result);
            JSONObject extraObject = JSONUtil.parseObj(returnObject.getStr("extra"));
            boolean errorCodeFlag = StringUtils.equals(extraObject.getStr("error_code"), "0");
            // 等于0时
            boolean dataFlag = returnObject.containsKey("data");
            if (errorCodeFlag && dataFlag) {
                SyncTicketRespDTO syncTicketRespDTO = new SyncTicketRespDTO();
                JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("data"));
                JSONArray jsonArray = JSONUtil.parseArray(dataObject.getStr("tickets"));
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject contentObject = jsonArray.getJSONObject(i);
                    // 0 下线 1 已上线 2 审核中 3 审核失败
                    String status = contentObject.getStr("status");
                    // 最近一次的审核结果
                    String auditResult = contentObject.getStr("audit_result");
                    JSONObject auditResultObject = JSONUtil.parseObj(auditResult);
                    // 商品是否已上线 StringUtils.equals(status, "1") &&
                    boolean isProductPass = auditResultObject.getBool("is_product_pass")
                            && contentObject.containsKey("ticket_specifications");
                    if (StringUtils.equals(status, "1") && isProductPass) {
                        // 最近一次的审核结果
                        List<SpecificationsDTO> specificationsList = new ArrayList<>();
                        String ticketSpecification = contentObject.getStr("ticket_specifications");
                        JSONArray ticketSpecifications = JSONUtil.parseArray(ticketSpecification);
                        for (int j = 0; j < ticketSpecifications.size(); j++) {
                            SpecificationsDTO specificationsDTO = new SpecificationsDTO();
                            JSONObject object = ticketSpecifications.getJSONObject(j);
                            specificationsDTO.setDistributorProductId(Long.parseLong(object.getStr("sku_out_id")));
                            if (StringUtils.isNotBlank(object.getStr("sku_id"))) {
                                specificationsDTO.setExternalSkuId(object.getStr("sku_id"));
                            }
                            specificationsList.add(specificationsDTO);
                        }
                        syncTicketRespDTO.setSpecificationsList(specificationsList);
                        return CommonResult.success(syncTicketRespDTO, "抖音日历票商品数据查询成功");
                    } else {
                        if (StringUtils.equals(status, "2")) {
                            message = "抖音日历票商品审核中,请稍后重试";
                        } else if (StringUtils.equals(status, "3")) {
                            // 票种审核原因,商品拒绝原因
                            String reason = extraObject.getStr("description") + extraObject.getStr("sub_description") +
                                    auditResultObject.getStr("ticket_type_audit_reason") + auditResultObject.getStr("product_audit_reason") + "(" + extraObject.getStr("logid") + ")";
                            message = productSpu.getProductName() + "抖音日历票商品审核失败:" + reason;
                        }
                        return CommonResult.error(203, message);
                    }
                }
            } else {
                String reason = extraObject.getStr("description") + extraObject.getStr("sub_description");
                message = "抖音日历票商品数据查询失败:" + reason;
                return CommonResult.error(204, message);
            }
        } catch (Exception ex) {
            log.error("抖音日历票商品数据查询异常:", ex);
            throw exception0(202, "抖音日历票商品数据查询失败");
        }
        return CommonResult.error(201, "抖音日历票商品查询失败");
    }

    /**
     * 日历票价格库存同步
     *
     * @param distributor
     * @param productSpu
     * @return
     */
    public CommonResult<?> syncTicketCalendarSave(DistributorRespDTO distributor, ProductSpuRespDTO productSpu, TiktokCalendarReqDTO reqDTO) {
        // 日历票价格库存同步
        String code = "201";
        String message = "";
        try {
            // serverurl:https://open.douyin.com
//            String sendUrl = distributor.getServerUrl() + "/goodlife/v1/trip/ticket_calendar/save/";
            //日历票价格库存属性批量发布,日历票规格扩容的商家请使用该接口
            String sendUrl = distributor.getServerUrl() + "/goodlife/v1/trip/ticket_calendar/batch_save/";
            JSONObject requestBody = new JSONObject();
            // 商家ID
            requestBody.put("account_id", reqDTO.getExternalAccountId());
            // 商品id product_out_id&product_id二选一
            requestBody.put("product_out_id", productSpu.getId());
            // 服务商商品id product_out_id&product_id二选一
            if (StringUtils.isNotBlank(reqDTO.getExternalProductId())) {
                requestBody.put("product_id", reqDTO.getExternalProductId());
            }
            List ticketSkuAndCalendarList = new ArrayList<>();
            List<RoutePlanRespDTO> routePlanRespDTOList = tradeDetailMapper.getRoutePlanByProductId(productSpu.getId(),1L);
            if (CollUtil.isNotEmpty(reqDTO.getSpecificationsList())) {
                for (SpecificationsDTO specificationsDTO : reqDTO.getSpecificationsList()) {
                    JSONObject ticketSpecification = new JSONObject();
                    JSONObject ticketSkuAndCalendar = new JSONObject();
                    // 日历票规格，SKU名称 POI名称+票种名称+人群+规格枚举名称
                    ticketSpecification.put("sku_name", productSpu.getProductName() + specificationsDTO.getSkuName());
                    // 收款方式（只支持总店结算）1 总店结算
                    ticketSpecification.put("settle_type", 1);
                    Long planId = null;
                    if (CollUtil.isNotEmpty(routePlanRespDTOList)) {
                        JSONArray ticketSessions = new JSONArray();
                        List<RoutePlanRespDTO> planList = routePlanRespDTOList.stream()
                                .collect(Collectors.groupingBy(RoutePlanRespDTO::getName,
                                        Collectors.reducing((a, b) -> a.getPlanType() > b.getPlanType() ? a : b)))
                                .values().stream()
                                .filter(Optional::isPresent)
                                .map(Optional::get)
                                .collect(Collectors.toList());
                        for (RoutePlanRespDTO planRespDTO : planList) {
                            planId = planRespDTO.getId();
                            JSONObject ticketSession = new JSONObject();
                            ticketSession.set("ticket_session_name", planRespDTO.getName());
                            ticketSessions.add(ticketSession);
                        }
                        ticketSpecification.set("ticket_session", ticketSessions);
                    }
                    ticketSpecification.set("sku_out_id", specificationsDTO.getDistributorProductId());
                    ticketSpecification.set("sku_id", specificationsDTO.getExternalSkuId());
                    ticketSkuAndCalendar.set("ticket_specification", ticketSpecification);

                    Integer presaleLimitDay = productSpu.getProductSaleRule().getPresaleLimitDay();
                    if (presaleLimitDay == null) {
                        presaleLimitDay = 15;
                    }
                    LocalDate currentDate = LocalDate.now();

                    LocalDate endDate = currentDate.plusDays(presaleLimitDay);
                    // calendar支持四种方式传参
                    JSONArray calendars = new JSONArray();
                    // 循环处理每一天
                    for (LocalDate date = currentDate; !date.isAfter(endDate); date = date.plusDays(1)) {
                        JSONObject calendarValue = new JSONObject();

                        // 截止时间大于等于当天
                        LocalDate saleEndDate = productSpu.getProductBasicRule().getSaleEndDate();
                        LocalDate saleStartDate = productSpu.getProductBasicRule().getSaleStartDate();
                        boolean dateInRange = this.isDateInRange(date, saleStartDate, saleEndDate);
                        if (dateInRange) {
                            JSONObject calendar = new JSONObject();
                            // 商家日历票规格id 1: 日期范围，2:周范围，3:具体日期值，4:具体某几个星期
                            calendar.put("calendar_type", 1);
                            // 具体的某些日期
                            List<String> valueList = new ArrayList<>();
                            valueList.add(date.toString());
                            valueList.add(date.toString());
                            calendarValue.put("value_list", valueList);
                            List<String> excludeDateList = new ArrayList<>();
                            // 日历中排除某些天
                            calendarValue.put("exclude_dateList", excludeDateList);
                            // 日历值
                            calendar.put("calendar_value", calendarValue);

//                            Date useDate = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
//                            StockDistributorDeductReqDTO deductReqDTO = new StockDistributorDeductReqDTO();
//                            deductReqDTO.setDistributorProductId(productSpu.getId());
//                            deductReqDTO.setDistributorId(distributor.getId());
//                            deductReqDTO.setRoutePlanId(planId.toString());
//                            deductReqDTO.setNum(0);
//                            deductReqDTO.setUseDate(useDate);
//                            // 原价（划线价），单位：分
//                            CommonResult<Integer> distributorStock = stockApi.getDistributorStock(deductReqDTO);
//                            // -1无限库存/具体数量库存
//                            if (distributorStock.isSuccess()) {
//                                Integer stock = distributorStock.getCheckedData();
//                                calendar.put("stock_qty", stock);
//                            } else {
//                                calendar.put("stock_qty", 0);
//                            }
//                            StockDayPriceQueryReqDTO queryReqDTO = new StockDayPriceQueryReqDTO();
//                            queryReqDTO.setBusinessId(specificationsDTO.getDistributorProductId());
//                            queryReqDTO.setType(StockTypeEnum.DISTRIBUTOR_PRODUCT.getValue());
//                            queryReqDTO.setDate(date.toString());
//                            //查询日历价格(元）
//                            BigDecimal price = specificationsDTO.getPrice();
//                            CommonResult<StockDayPriceRespDTO> stockDayPrice = stockApi.getStockDayPrice(queryReqDTO);
//                            if (stockDayPrice.getCode() == 0) {
//                                price = stockDayPrice.getData().getPrice();
//                            }
                            // 原价（划线价），单位：分
                            calendar.put("origin_amount", specificationsDTO.getPrice().multiply(new BigDecimal(100)).intValue());
                            // 实际售价，单位：分
//                            calendar.put("actual_amount", price.multiply(new BigDecimal(100)).intValue());
                            // 1:上线 -1:下线
                            calendar.put("status", specificationsDTO.getStatus());
                            calendars.add(calendar);
                        }
                    }
                    ticketSkuAndCalendar.set("calendar_list", calendars);
                    ticketSkuAndCalendarList.add(ticketSkuAndCalendar);
                }
            }
            requestBody.set("ticket_sku_and_calendar", ticketSkuAndCalendarList);

            String param = requestBody.toString();
            Map<String, String> headParam = setHeadMap(distributor);
            String sendResult = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .body(param)
                    .headerMap(headParam, false)
                    .timeout(15000)
                    .execute().body();
            log.error("抖音日历票价格库存同步返回结果：{}", sendResult);
            // 返回结果为空时
            if (StringUtils.isBlank(sendResult)) {
                return CommonResult.error(201, "抖音日历票价格库存同步失败");
            }
            JSONObject returnObject = JSONUtil.parseObj(sendResult);
            JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("extra"));

            boolean errorCodeFlag = StringUtils.equals(dataObject.getStr("error_code"), "0");
            if (errorCodeFlag) {
                return CommonResult.success(null, "抖音日历票价格库存同步成功");
            } else {
                log.error("【抖音日历票价格库存同步】：" + sendResult);
                String reason = dataObject.getStr("description") + dataObject.getStr("sub_description") + "(" + dataObject.getStr("logid") + ")";
                message = "抖音日历票价格库存同步失败:" + reason;
                return CommonResult.error(204, message);
            }
        } catch (Exception ex) {
            log.error("抖音日历票价格库存同步异常:", ex);
            throw exception0(202, "抖音日历票价格库存同步失败");
        }
    }

    public CommonResult<?> syncProductOperate(DistributorRespDTO distributor, ProductSpuRespDTO productSpu, TiktokCalendarReqDTO reqDTO) {
        // 上下架商品
        String message = "";
        try {
            // serverurl:https://open.douyin.com
            String sendUrl = distributor.getServerUrl() + "/goodlife/v1/goods/product/operate/";

            JSONObject requestBody = new JSONObject();
            // 商家ID
            requestBody.put("account_id", reqDTO.getExternalAccountId());
            // 商家商品id
            requestBody.put("out_id", productSpu.getId());
            // 上下架 1 上线 2 下线
            requestBody.put("op_type", reqDTO.getOpType());

            String param = requestBody.toString();
            Map<String, String> headParam = setHeadMap(distributor);
//            String sendResult = SendHttps.sendPost(sendUrl, param, headParam);
            String sendResult = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .body(param)
                    .headerMap(headParam, false)
                    .timeout(15000)
                    .execute().body();
            // 返回结果为空时
            if (StringUtils.isBlank(sendResult)) {
                return CommonResult.error(201, "抖音日历票上下架商品失败");
            }
            JSONObject returnObject = JSONUtil.parseObj(sendResult);
            JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("data"));
            boolean errorCodeFlag = StringUtils.equals(dataObject.getStr("error_code"), "0");
            // 等于0时
            if (errorCodeFlag) {
                message = "抖音日历票上下架商品成功";
                return CommonResult.success(null, message);
            } else {
                String reason = dataObject.getStr("description") + dataObject.getStr("sub_description") + "(" + dataObject.getStr("logid") + ")";
                message = productSpu.getProductName() + "抖音日历票上下架商品失败:" + reason;
                return CommonResult.error(204, message);
            }
        } catch (Exception ex) {
            log.error("抖音日历票上下架商品异常:", ex);
            throw exception0(202, "抖音日历票上下架商品失败");
        }
    }

    /**
     * @param tenantId 租户
     * @param path     路径
     * @param request  HttpServletRequest
     * @param jsonStr  请求参数
     * @return java.lang.String
     * @description 抖音日历票-请求
     */
    @Override
    public String doRequestTiktokCalendar(Long tenantId, String path, HttpServletRequest request, String jsonStr) {
        try {
            TenantContextHolder.setTenantId(tenantId);
            TenantContextHolder.setIgnore(false);

            SignBackBean signBackBean = this.verifyTiktokCalendar(path, request, jsonStr, "");

            DistributorRespDTO distributor = signBackBean.getDistributor();
            if (distributor == null) {
                distributor = new DistributorRespDTO();
            }
            // 验签结果
            if (!signBackBean.isSignSuccess()) {
                return signBackBean.getErrMsg();
            }

            return doRequestByPathCalendar(path, request, jsonStr, distributor);

        } catch (ServerException e) {
            log.error("抖音日历票异常:", e);
            return returnVerifyTiktokCalendar(999999, e.getMessage());
        } catch (Exception e) {
            log.error("抖音日历票-请求异常:", e);
            // 其他异常
            return returnVerifyTiktokCalendar(999999, e.getMessage());
        }
    }

    /**
     * @param request HttpServletRequest
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @description 获取请求头参数
     */
    public Map<String, String> getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<String, String>();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    public SignBackBean verifyTiktokCalendar(String path, HttpServletRequest request, String jsonStr, String versionKey) {
        SignBackBean bean = new SignBackBean();
        bean.setSignSuccess(false);
        // 获取分销商信息
        long timestamp = Long.valueOf(request.getParameter("timestamp"));
        Map<String, String> headersInfoMap = getHeadersInfo(request);
        String xLifeClientkey = headersInfoMap.get("x-life-clientkey");
        String xLifeSign = headersInfoMap.get("x-life-sign");
        DistributorRespDTO distributor = this.getServerTiktokCalendar(xLifeClientkey);
        // 账号不存在
        if (distributor == null) {
            bean.setErrMsg(returnVerifyTiktokCalendar(999999, "账号不存在"));
            return bean;
        }
        bean.setDistributor(distributor);
        String signStr = distributor.getSecret() + "&" + "client_key=" + distributor.getAppid() + "&timestamp=" + timestamp + "&http_body=" + jsonStr;
        // 签名结果(账号签名异常)
        if (!xLifeSign.equals(Sha256Util.getSHA256(signStr))) {
            log.error("分销{},{},{},{}", "抖音日历票", path, xLifeSign + "==" + Sha256Util.getSHA256(signStr), "签名异常");
            bean.setErrMsg(returnVerifyTiktokCalendar(999999, "签名异常"));
            return bean;
        }
        bean.setSignSuccess(true);
        return bean;
    }

    public String doRequestByPathCalendar(String path, HttpServletRequest request, String jsonStr, DistributorRespDTO distributor) {
        String method = path;
        // 内容体jsonStr
        JSONObject body = JSONUtil.parseObj(jsonStr);
        // 下单前校验
        if ("VerifyOrder".equals(method)) {
            return verifyOrderTiktokCalendar(body, distributor);
            // 创建订单
        } else if ("CreateOrder".equals(method)) {
            return createOrderTiktokCalendar(body, distributor);
            // 同步发放凭证
        } else if ("SyncSendVouchers".equals(method)) {
            return syncSendVouchersTiktokCalendar(body, distributor);
            // 通知取消订单
        } else if ("CancelOrder".equals(method)) {
            return cancelOrderTiktokCalendar(body, distributor, request);
            // 申请退单、退票
        } else if ("RefundOrder".equals(method)) {
            return refundOrderTiktokCalendar(body, distributor,0);
            // 通知退单、退票结果
        } else if ("RefundedOrder".equals(method)) {
            return refundedNotifyTiktokCalendar(body, distributor);
            // 订单状态查询
        } else if ("QueryOrder".equals(method)) {
            return queryNotifyTiktokCalendar(body, distributor);
        } else {
            return returnVerifyTiktokCalendar(999999, "接口不存在");
        }
    }

    String verifyOrderTiktokCalendar(JSONObject body, DistributorRespDTO distributor) {
        try {
            // 产品编号
            Long distributorProductId = body.getLong("sku_out_id");
            DistributorProductRespDTO distributorProduct = otaDistributorApi.getDistributorProductById(distributorProductId).getCheckedData();

//            DistributorProductRespDTO distributorProduct = otaDistributorApi.getDistributorProductByProductAndDistributor(productId, distributor.getId()).getCheckedData();
            // 产品不存在
            if (null == distributorProduct) {
                return returnVerifyTiktokCalendar(2, "产品不存在");
            }
            // 产品已下架
            if (distributorProduct.getIsUse() != 1) {
                return returnVerifyTiktokCalendar(2, "产品已下架");
            }

            int tickets = Integer.parseInt(body.getStr("count"));

            TradeOrderCreateReqDTO order = this.parseOrderTiktokCalendar(body, distributor);
//            //验证分销产品库存
//            StockDistributorDeductReqDTO deductReqDTO = new StockDistributorDeductReqDTO();
//            deductReqDTO.setDistributorProductId(distributorProductId);
//            deductReqDTO.setNum(tickets);
//            deductReqDTO.setUseDate(order.getTravelDate());
//            try {
//                stockApi.deductDistributorStock(deductReqDTO).getCheckedData();
//            } catch (Exception e) {
//                log.error("抖音日历票扣减库存异常：{}", e.getMessage());
//                return returnVerifyTiktokCalendar(1, "库存不足");
//            }
//             //验证销售规则
            ProductSpuRespDTO productSpuRespDTO = productApi.getProduct(distributorProduct.getProductId()).getCheckedData();
            TradeOrderRuleValidate.checkSaleRule(order.getItemlList().get(0), productSpuRespDTO);
//
            // 可以下单
            return returnVerifyTiktokCalendar(0, "预校验订单成功");
        } catch (ServerException e) {
            log.error("抖音日历票:", e);
            String msg = e.getMessage();
            // 您购买的商品已暂时售罄，请选择其他日期或商品
            if (StringUtils.isNotBlank(msg) && msg.contains("证件号不能为空")) {
                return returnVerifyTiktokCalendar(13, msg);
                // 您已持有该门票，请选择其他票种或日期
            } else if (StringUtils.isNotBlank(msg) && (msg.contains("有未检子票") || msg.contains("证件号"))) {
                return returnVerifyTiktokCalendar(3, msg);
                // 当前日期无法预订，请选择其他日期
            } else if (StringUtils.isNotBlank(msg) && msg.contains("提前")) {
                return returnVerifyTiktokCalendar(4, msg);
                // 出行人年龄不在可订范围内
            } else if (StringUtils.isNotBlank(msg) && msg.contains("年龄")) {
                return returnVerifyTiktokCalendar(5, msg);
                // 出行人性别不在可订范围内
            } else if (StringUtils.isNotBlank(msg) && msg.contains("性别")) {
                return returnVerifyTiktokCalendar(6, msg);
                // 您购买的商品已暂时售罄，请选择其他日期或商品
            } else {
                return returnVerifyTiktokCalendar(999999, msg);
            }
        } catch (Exception e) {
            log.error("抖音日历票:", e);
            return returnVerifyTiktokCalendar(999999, "服务异常");
        }
    }

    String createOrderTiktokCalendar(JSONObject body, DistributorRespDTO distributor) {
        Map<String, String> headParam = setHeadMap(distributor);
        RLock lock = null;
        try {
            // 封装下单
            TradeOrderCreateReqDTO order = this.parseOrderTiktokCalendar(body, distributor);
            String key = order.getExternalOrderNo() + distributor.getId();
            lock = redissonClient.getLock(key);
            // 订单创建
            if (lock.tryLock(10, 20, TimeUnit.SECONDS)) {
                return savePayOrderTiktokCalendar(order.getExternalOrderNo(), order, false, distributor, headParam);
            } else {
                return returnCreateOrderTiktokCalendar("", 999999, "重复提交");
            }
        } catch (ServerException ex) {
            log.error("分销{},{},{},{}", "抖音日历票创建订单", "createOrder", body.getStr("order_id"), ex.getMessage());

            return returnCreateOrderTiktokCalendar("", 999999, ex.getMessage());
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "抖音日历票创建订单", "createOrder", body.getStr("order_id"), e.getMessage());
            return returnCreateOrderTiktokCalendar("", 999999, e.getMessage());
        } finally {
            if (lock != null) {
                try {
                    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                } catch (Exception e) {
                    log.error("释放锁失败", e);
                }
            }
        }
    }

    private TradeOrderCreateReqDTO parseOrderTiktokCalendar(JSONObject body, DistributorRespDTO distributor) throws Exception {

        TradeOrderCreateReqDTO order = new TradeOrderCreateReqDTO();
        Long distributorId = distributor.getId();
        List<TradeOrderCreateReqDTO.OrderItem> itemlList = new ArrayList<>();
        List<TradeOrderCreateReqDTO.OrderDetail> detailList = new ArrayList<>();
        // 设置三方订单号(抖音侧订单号)
        order.setExternalOrderNo(body.getStr("order_id"));
        Long distributorProductId = body.getLong("sku_out_id");
        String useDate = body.getStr("book_start_day");
        Date travelDate = DateUtil.parse(useDate, DatePattern.NORM_DATE_PATTERN);
        order.setTravelDate(travelDate);
        TradeOrderCreateReqDTO.OrderProperty orderProperty = OtaConvert.INSTANCE.convert(distributorId.toString(), distributor.getName());
        order.setOrderProperty(orderProperty);

        //2、查询产品
        DistributorProductRespDTO distributorProduct = otaDistributorApi.getDistributorProductById(distributorProductId).getCheckedData();
        ProductSpuRespDTO productSpuRespDTO = productApi.getProduct(distributorProduct.getProductId()).getCheckedData();


        if (null == distributorProduct || distributorProduct.getIsUse() != 1 || null == productSpuRespDTO || productSpuRespDTO.getIsUse() != 1) {
            throw new ServerException(1001, "产品已下架或不存在！");
        }


        // 设置订单门票数
        Integer ticketsCount = body.getInt("count");
        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.OrderUser orderUser = new TradeOrderCreateReqDTO.OrderUser();
        // 设置证件类型(0：身份证，1护照，2人像，99：其他）
        orderUser.setCredentialType(0);
        if (body.containsKey("buyer")) {
            JSONObject contactObject = JSONUtil.parseObj(body.getStr("buyer"));
            // 设置联系人姓名
            orderUser.setContactName(decryptAES(contactObject.getStr("name"), distributor.getSecret()));
            // 设置联系人电话
            orderUser.setMobile(decryptAES(contactObject.getStr("phone"), distributor.getSecret()));
        } else {
            // 设置联系人姓名
            orderUser.setContactName("");
            // 设置联系人电话
            orderUser.setMobile("");
        }
        // 设置证件号
        orderUser.setCredentialNo("");
        // 设置联系人信息
        order.setOrderUser(orderUser);
        // 设置证件号
        orderUser.setCredentialNo("");
        // 设置联系人信息
        order.setOrderUser(orderUser);
        // 单位:元
        BigDecimal ticketPrice = distributorProduct.getSalePrice();

//        StockDayPriceQueryReqDTO queryReqDTO = new StockDayPriceQueryReqDTO();
//        queryReqDTO.setBusinessId(distributorProductId);
//        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) {
//            ticketPrice = stockDayPrice.getData().getPrice();
//        }
        if (ticketPrice != null) {
            BigDecimal orderAmt = ticketPrice.multiply(new BigDecimal(ticketsCount));
            order.setAmount(orderAmt);
            order.setPayAmount(orderAmt);
        }
        TradeOrderCreateReqDTO.OrderDetail orderDetail = OtaConvert.INSTANCE.convert(distributorProduct, ticketsCount, distributorProjectDTOS,ticketPrice);

        //场次
        JSONObject ticketSpecification = body.getJSONObject("ticket_specification");
        if (ticketSpecification.containsKey("ticket_session")) {
            JSONObject ticketSession = ticketSpecification.getJSONObject("ticket_session");
            String planName = ticketSession.getStr("ticket_session_name");
//            RoutePlanRespDTO routePlan = tradeDetailMapper.getRoutePlanByName(productSpuRespDTO.getId(), planName, travelDate);
//            if (routePlan == null) {
//                throw new ServerException(1005, "产品当天不存在{" + planName + "}场次");
//            }
//            order.setRoutePlanId(routePlan.getId());
        } else {
            order.setRoutePlanId(distributorProduct.getRoutePlanId());
        }

        List<TradeOrderCreateReqDTO.Visitor> visitors = new ArrayList<>();
        // 获取出行人信息
        JSONArray vistors = JSONUtil.parseArray(body.getStr("tourists"));
        // 售票规则验证购票方式(1:实名购票，3：无需实名)

        ProductSaleRuleRespDTO saleRule = productSpuRespDTO.getProductSaleRule();
        // 产品是实名购票且出行人信息非空时
        if (saleRule != null && saleRule.getIsLimitType() == 1 && CollUtil.isNotEmpty(vistors)) {
            for (int i = 0; i < vistors.size(); i++) {
                JSONObject item = vistors.getJSONObject(i);
                String idCard = decryptAES(item.getStr("license_id"), distributor.getSecret());

                if (StringUtils.isEmpty(idCard)) {
                    throw new ServerException(1004, "游客身份证为空不能下单");
                }

                TradeOrderCreateReqDTO.Visitor visitor = new TradeOrderCreateReqDTO.Visitor();
                visitor.setCredentialNo(idCard);
                Integer cardType = this.changeIDCardTypeFormSystem(item.getInt("license_type"));
                visitor.setCredentialType(cardType);
                String visitorname = decryptAES(item.getStr("name"), distributor.getSecret());
                visitor.setName(visitorname);
                String phone = decryptAES(item.getStr("phone"), distributor.getSecret());
                visitor.setMobile(phone);
                visitors.add(visitor);
            }
        } else {
            // 非实名制购票
            if (saleRule != null && saleRule.getIsLimitType() == 0) {
                if (CollUtil.isNotEmpty(vistors)) {
                    for (int i = 0; i < vistors.size(); i++) {
                        JSONObject item = vistors.getJSONObject(i);
                        if (i == 0) {
                            String visitorname = decryptAES(item.getStr("name"), distributor.getSecret());
                            String phone = decryptAES(item.getStr("phone"), distributor.getSecret());
                            String idCard = decryptAES(item.getStr("license_id"), distributor.getSecret());
                            // 联系人，取第一个游客信息
                            TradeOrderCreateReqDTO.OrderUser orderUserNew = new TradeOrderCreateReqDTO.OrderUser();
                            // 设置联系人姓名
                            orderUserNew.setContactName(visitorname);
                            // 设置证件号类型
                            orderUserNew.setCredentialType(this.changeIDCardTypeFormSystem(item.getInt("license_type")));
                            // 设置证件号信息
                            orderUserNew.setCredentialNo(idCard);
                            // 设置联系人电话
                            orderUserNew.setMobile(phone);
                            order.setOrderUser(orderUserNew);
                        }
                    }
                }
                visitors = OtaConvert.INSTANCE.convertOtaVisitorList(ticketsCount);
                ;
            }
        }
        if (saleRule != null && saleRule.getIsLimitType() == 1 && orderDetail.getCount() != visitors.size()) {
            throw new ServerException(1005, "本商品为实名制商品，出行人必填！");
        }
        // 设置出行人信息
        orderDetail.setVisitorList(visitors);
        detailList.add(orderDetail);
        //设置订单明细
        orderItem.setDetailList(detailList);
        itemlList.add(orderItem);
        //设置订单项列表
        order.setItemlList(itemlList);
        return order;
    }

    public String savePayOrderTiktokCalendar(String otaOrderNo, TradeOrderCreateReqDTO order, boolean isPay, DistributorRespDTO distributor, Map<String, String> headParam) {
        // 重复验证
        TradeOrderDO orderDB = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(otaOrderNo, order.getOrderProperty().getSourceId());
        // 为空时
        if (orderDB == null) {
            // 创建订单
            CommonResult<TradeOrderRespDTO> result = tradeOrderService.createOrder(order);
            if (result.isError()) {
                return returnCreateOrderTiktokCalendar("", 999999, result.getMsg());
            }
            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 returnCreateOrderTiktokCalendar(tradeOrderRespDTO.getOrderNo(), 999999, payResult.getMsg());
                }
            }
            // 返回订单创建成功
            return returnCreateOrderTiktokCalendar(tradeOrderRespDTO.getOrderNo(), 0, "");
            // 订单已存在
        } else {

            return returnCreateOrderTiktokCalendar(orderDB.getOrderNo(), 0, "");
        }
    }

    @Override
    public void orderConfirm(DistributorRespDTO distributor, String externalOrderNo, int confirmResult) {
        Map<String, String> headParam = setHeadMap(distributor);
        orderConfirm(distributor, externalOrderNo, confirmResult, headParam);
    }

    public void orderConfirm(DistributorRespDTO distributor, String externalOrderNo, int confirmResult, Map<String, String> headParam) {
        // https://open.douyin.com
        String url = distributor.getServerUrl() + "/goodlife/v1/trip/order/confirm/";
        JSONObject paramObject = new JSONObject();
        // 抖音侧订单ID
        paramObject.put("order_id", externalOrderNo);
        // 接单结果，1 接受，2 拒绝
        paramObject.put("confirm_result", confirmResult);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // Map<String, String> headParam = setHeadMap(distributor);
//        String result = SendHttps.sendPost(url, paramObject.toString(), headParam);
        String result = HttpUtil.createPost(url).contentType("application/json")
                .body(paramObject.toString())
                .headerMap(headParam, false)
                .timeout(15000)
                .execute().body();
        log.error("抖音日历票确认接单返回:{}", result);
        if (StringUtils.isBlank(result)) {
            log.error("分销{},{},{},{}", "抖音日历票确认接单", "orderConfirm", externalOrderNo, "");
            // 失败重推
            result = rePush(distributor, paramObject);
            log.error("分销{},{},{},{}", "抖音日历票确认接单", "orderConfirm", externalOrderNo, result);
        } else {
            JSONObject returnObject = JSONUtil.parseObj(result);
            JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("extra"));
            boolean errorCodeFlag = "0".equals(dataObject.getStr("error_code"));
            // 非0表示失败
            if (!errorCodeFlag) {
                log.error("分销{},{},{},{}", "抖音日历票确认接单", "orderConfirm", externalOrderNo, dataObject.getStr("description"));
                // 失败重推
                result = rePush(distributor, paramObject);
                log.error("分销{},{},{},{}", "抖音日历票确认接单", "orderConfirm", externalOrderNo, result);
            }
        }
    }

    /**
     * @param distributor 账号
     * @param paramObject 请求参数
     * @return void
     * @description 失败重推
     */
    public String rePush(DistributorRespDTO distributor, JSONObject paramObject) {
        String result = "";
        int recordCount = 1;
        // 重推6次
        while (recordCount <= 6) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String url = distributor.getServerUrl() + "/goodlife/v1/trip/order/confirm/";
            Map<String, String> headParam = setHeadMap(distributor);
//            result = SendHttps.sendPost(url, paramObject.toString(), headParam);
            result = HttpUtil.createPost(url).contentType("application/json")
                    .body(paramObject.toString())
                    .headerMap(headParam, false)
                    .timeout(15000)
                    .execute().body();
            if (StringUtils.isNotBlank(result)) {
                JSONObject returnObject = JSONUtil.parseObj(result);
                JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("extra"));
                boolean errorCodeFlag = "0".equals(dataObject.getStr("error_code"));
                // 非0表示失败
                if (errorCodeFlag) {
                    return result;
                }
            }
            recordCount++;
        }
        return result;
    }

    String syncSendVouchersTiktokCalendar(JSONObject body, DistributorRespDTO distributor) {
        RLock lock = null;
        try {
            // 要退票的抖音订单号
            String orderId = body.getStr("order_id");

            // 订单信息
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(orderId, distributor.getId().toString());
            // 订单不存在或已过期时 订单未出票成功
            if (order == null || order.getOrderStatus().equals(TradeOrderStatusEnum.CANCELED.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.FAIL.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.CANCELED.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.PROCESS_FAIL.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.EXPIRED.getStatus())) {
                return returnSendVouchersFailed();
            }
            JSONObject ticketRule = body.getJSONObject("ticket_rule");
            ticketRule.getJSONArray("code_sending_info");
            List<Integer> codeSendingInfo = ticketRule.getBeanList("code_sending_info", Integer.class);
            // 订单已出票时
            if (order.getOrderStatus() >= TradeOrderStatusEnum.UNUSED.getStatus()) {
                return retrunSendVouchersSuccessed(order, codeSendingInfo);
            }
            String key = order.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(order.getId());
                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()) {
                    throw new ServerException(1009, payResult.getMsg());
                }

                return retrunSendVouchersSuccessed(order, codeSendingInfo);
            } else {
                JSONObject returnObject = new JSONObject();
                JSONObject dataObject = new JSONObject();
                // 接口错误码
                dataObject.put("error_code", 0);
                // 接口错误描述
                dataObject.put("description", "重复提交");
                // 发码结果:0发码中1成功2失败
                dataObject.put("result", 2);
                returnObject.put("data", dataObject);
                return returnObject.toString();
            }
        } catch (ServerException ex) {
            log.error("分销{},{},{},{}", "抖音日历票发放凭证", "createOrder", body.getStr("order_id"), ex.getMessage());
            JSONObject returnObject = new JSONObject();
            JSONObject dataObject = new JSONObject();
            // 接口错误码
            dataObject.put("error_code", 0);
            // 接口错误描述
            dataObject.put("description", "订单出票异常" + ex.getMessage());
            // 发码结果:0发码中1成功2失败
            dataObject.put("result", 2);
            returnObject.put("data", dataObject);
            return returnObject.toString();
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "抖音日历票发放凭证", "createOrder", body.getStr("order_id"), e.getMessage());
            JSONObject returnObject = new JSONObject();
            JSONObject dataObject = new JSONObject();
            // 接口错误码
            dataObject.put("error_code", 0);
            // 接口错误描述
            dataObject.put("description", "订单出票异常");
            // 发码结果:0发码中1成功2失败
            dataObject.put("result", 2);
            returnObject.put("data", dataObject);
            return returnObject.toString();
        } finally {
            if (null != lock && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    String retrunSendVouchersSuccessed(TradeOrderDO order, List<Integer> codeSendingInfo) {
        JSONObject returnObject = new JSONObject();
        JSONObject dataObject = new JSONObject();
        // 接口错误码
        dataObject.put("error_code", 0);
        // 接口错误描述
        dataObject.put("description", "订单出票成功");
        // 发码结果:0发码中1成功2失败
        dataObject.put("result", 1);
        List<TradeDetailDO> detailList = tradeOrderRepositoryService.queryDetailByOrderId(order.getId());
        if (CollUtil.isNotEmpty(detailList)) {
            JSONArray vouchers = new JSONArray();
            detailList.forEach(detail -> {
                JSONObject entranceObject = new JSONObject();
                // 项目唯一标识,不能重复票号
                entranceObject.put("project_id", detail.getVoucherCode());

                // 空 非实名 非空 实名
                List<TradeVisitorDO> visitorList = tradeOrderRepositoryService.queryVisitorByDetailId(detail.getId());
                TradeVisitorDO visitorDO = visitorList.get(0);
                String certificateNo = visitorDO.getCredentialNo();
                if (StringUtils.isNotBlank(certificateNo)) {
                    //凭证发放方式，多选1=身份 2=券号 3=券码 6=链接URL
                    if (CollUtil.contains(codeSendingInfo, 1)) {
                        JSONArray credentials = new JSONArray();
                        JSONObject credential = new JSONObject();
                        visitorDO.getCredentialType();
                        credential.set("credential_type", changeCertificateType(visitorDO.getCredentialType()));
                        credential.set("credential_no", certificateNo);
                        credentials.add(credential);
                        entranceObject.put("credentials", credentials);
                    }

                    if (CollUtil.contains(codeSendingInfo, 2)) {
                        JSONArray certificateNos = new JSONArray();
                        certificateNos.add(detail.getVoucherCode());
                        entranceObject.put("certificate_nos", certificateNos);
                    }
                    if (CollUtil.contains(codeSendingInfo, 3)) {
                        JSONArray qrcodes = new JSONArray();
                        qrcodes.add(detail.getVoucherCode());
                        entranceObject.put("qrcodes", qrcodes);
                    }

                } else {
                    if (CollUtil.contains(codeSendingInfo, 2)) {
                        JSONArray certificateNos = new JSONArray();
                        certificateNos.add(detail.getVoucherCode());
                        entranceObject.put("certificate_nos", certificateNos);
                    }
                    if (CollUtil.contains(codeSendingInfo, 3)) {
                        JSONArray qrcodes = new JSONArray();
                        qrcodes.add(detail.getVoucherCode());
                        entranceObject.put("qrcodes", qrcodes);
                    }
                }

                JSONObject voucher = new JSONObject();
                voucher.put("entrance", entranceObject);
                vouchers.add(voucher);
            });
            dataObject.put("vouchers", vouchers);
        }
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    String returnSendVouchersFailed() {
        JSONObject returnObject = new JSONObject();
        JSONObject dataObject = new JSONObject();
        // 接口错误码
        dataObject.put("error_code", 0);
        // 接口错误描述
        dataObject.put("description", "订单未出票成功");
        // 发码结果:0发码中1成功2失败
        dataObject.put("result", 2);
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    /**
     * @param body        参数
     * @param distributor 分销商信息
     * @param request     HttpServletRequest
     * @return java.lang.String
     * @description 通知取消订单
     */
    String cancelOrderTiktokCalendar(JSONObject body, DistributorRespDTO distributor, HttpServletRequest request) {
        try {
            // 要退票的抖音订单号
            String orderId = body.getStr("order_id");
            // 订单信息
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(orderId, distributor.getId().toString());
            // 订单不存在或订单无效时
            if (order == null) {
                return returnCancelTiktokCalendar(0, "无效订单");
            }
            CommonResult<?> commonResult = tradeOrderPayService.cancelOrder(order.getId());
            if (commonResult.isSuccess()) {
                return returnCancelTiktokCalendar(0, "取消成功");
            } else {
                return returnCancelTiktokCalendar(2, commonResult.getMsg());
            }

        } catch (Exception ex) {
            log.error("分销{},{},{}", "抖音日历取消订单", "cancelOrder", ex.getMessage());
            return returnCancelTiktokCalendar(2, "订单取消异常");
        }
    }

    String refundOrderTiktokCalendar(JSONObject body, DistributorRespDTO distributor, Integer isForceRefund) {
        // 票号集合
        List<String> ticketNoList = new ArrayList<>();
        // 抖音侧计算的退款手续费金额，分
        int refundFeeAmount = MapUtils.getIntValue(body, "refund_fee_amount", 0);
        RLock lock = null;
        try {
            // 要退票的抖音订单号
            String orderId = body.getStr("order_id");
            // 订单信息
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(orderId, distributor.getId().toString());
            // 订单不存在或订单无效时 同意退款
            if (order == null || order.getOrderStatus() == 0) {
                return returnCancelTiktokCalendar(0, "无效订单");
            }
            Integer count = -1;
            boolean certificatesFlag = body.containsKey("vouchers");
            List<TradeRefundCreateReqDTO.RefundItem> refundItemList = new ArrayList<>();
            BigDecimal refundAmt = BigDecimal.ZERO;
            if (certificatesFlag) {
                // 要退票的票号
                JSONArray certificates = body.getJSONArray("vouchers");
                if (CollUtil.isNotEmpty(certificates)) {
                    for (int i = 0; i < certificates.size(); i++) {
                        JSONObject certificate = certificates.getJSONObject(i);
                        JSONObject entrance = certificate.getJSONObject("entrance");
                        String code = entrance.getStr("project_id");
                        if (StringUtils.isNotBlank(code)) {
                            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);
                            ticketNoList.add(tradeDetail.getVoucherCode());
                        }
                    }
                    count = refundItemList.size();
                }
            }
            // 业务唯一键，用于审核回调
            String bizUniqKey = MapUtil.getStr(body, "biz_uniq_key", "");
            List<TradeDetailDO> detailDOList = tradeOrderRepositoryService.queryDetailByOrderId(order.getId());

            List<TradeDetailDO> refundList = new ArrayList<>();
            if (count > 0) {
                // 计算订单金额
                int refund_count = 0;
                for (TradeDetailDO detailDO : detailDOList) {
                    for (String ticketNo : ticketNoList) {
                        if (StringUtils.equals(detailDO.getVoucherCode(), ticketNo)) {
                            // 验证门票是否可退：0初始状态，1待使用，2已取票，3已核销，4退款中，5已退票，6逾期核销，7已过期
                            if (detailDO.getVoucherStatus() > 0 && detailDO.getVoucherStatus() < 3) {
                                refundList.add(detailDO);
                            } else if (detailDO.getVoucherStatus() == 5) {
                                refund_count += 1;
                            }
                        }
                    }
                }
                if (refundList.size() == 0 && count == refund_count) {
                    return returnRefundTiktokCalendar(0, "订单门票已取消", 1, refundFeeAmount);
                } else if (refundList.size() != count) {
                    return returnRefundTiktokCalendar(0, "订单门票取消失败，申请张数超过可退款张数", 2, refundFeeAmount);
                }

            } else {
                // 退票数缺省时退票所有可退门票
                if (count == -1) {
                    Map<String, Object> ticketMap = tradeOrderRepositoryService.queryDetailCountByOrderId(order.getId());
                    int checkCount = MapUtils.getIntValue(ticketMap, "checkCount");
                    int partCount = MapUtils.getIntValue(ticketMap, "partCount");
                    int outTimeCount = MapUtils.getIntValue(ticketMap, "outTimeCount");
                    int refundCount = MapUtils.getIntValue(ticketMap, "refundCount");
                    // 已使用票数
                    int useCount = checkCount + partCount + outTimeCount;
                    // 订单已退票
                    if (refundCount == detailDOList.size()) {
                        return returnRefundTiktokCalendar(0, "订单已取消", 1, refundFeeAmount);
                    }

                    refundAmt = detailDOList.stream()
                            .filter(detail -> detail.getVoucherStatus() != null
                                    && detail.getVoucherStatus() > 0
                                    && detail.getVoucherStatus() < 4)
                            .map(TradeDetailDO::getActualPrice)
                            .reduce(BigDecimal.ZERO, BigDecimal::add);

                }
            }
//            //如果退款金额等于0，则用可退门票的金额和
//            if (refundAmt.compareTo(BigDecimal.ZERO) == 0) {
//                refundAmt = refundList.stream().map(TradeDetailDO::getActualPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
//            }
            // 该订单无可退门票
            Long effectyCount = tradeOrderRepositoryService.queryEffectyCount(order.getId());
            if (effectyCount == 0) {
                return returnRefundTiktokCalendar(0, "该订单无可退门票", 2, refundFeeAmount);
            }
            // 锁
            lock = redissonClient.getLock(order.getOrderNo() + distributor.getId());
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {

                TradeRefundCreateReqDTO refundCreateReqDTO = TradeRefundConvert.INSTANCE.convertOtaRefund(order.getId(), distributor.getId(), isForceRefund, bizUniqKey);
                refundCreateReqDTO.setRefundAmount(refundAmt);
                if (CollUtil.isNotEmpty(refundItemList)) {
                    refundCreateReqDTO.setItemlList(refundItemList);
                }
                CommonResult<TradeRefundRespDTO> refundRes = tradeRefundService.refund(refundCreateReqDTO);
                if (refundRes.isSuccess()) {
                    return returnRefundTiktokCalendar(0, refundRes.getMsg(), 1, refundFeeAmount);
                } else {
                    return returnRefundTiktokCalendar(1, refundRes.getMsg(), 2, refundFeeAmount);
                }

            } else {
                return returnRefundTiktokCalendar(0, "重复提交", 2, refundFeeAmount);
            }
        } catch (ServerException ex) {
            return returnRefundTiktokCalendar(1, ex.getMessage(), 2, refundFeeAmount);
        } catch (Exception ex) {
            return returnRefundTiktokCalendar(1, "订单取消异常", 2, refundFeeAmount);
        } finally {
            if (null != lock && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    String refundedNotifyTiktokCalendar(JSONObject body, DistributorRespDTO distributor) {
        // 票号集合
        List<String> ticketNoList = new ArrayList<>();
        // 实际退款金额，分
        int refundAmount = MapUtils.getIntValue(body, "refund_amount", 0);
        try {
            // 要退票的抖音订单号
            String orderId = body.getStr("order_id");
            // 订单号
            String orderNo = MapUtil.getStr(body, "order_out_id");
            // 订单信息
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(orderId, distributor.getId().toString());
            // 订单不存在或订单无效时 同意退款
            if (order == null || order.getOrderStatus().equals(TradeOrderStatusEnum.CANCELED.getStatus())
                    || order.getOrderStatus().equals(TradeOrderStatusEnum.FAIL.getStatus())) {
                return returnCancelTiktokCalendar(0, "无效订单");
            }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 returnCancelTiktokCalendar(0, "退款成功");
                } else {
                    return returnCancelTiktokCalendar(2, commonResult.getMsg());
                }
            }else if (order.getOrderStatus() >= TradeOrderStatusEnum.PAID.getStatus()
                    && order.getOrderStatus() < TradeOrderStatusEnum.ALL_REFUND.getStatus()){
                return  this.refundOrderTiktokCalendar(body, distributor,1);
            }else {
                return returnCancelTiktokCalendar(0, "已退款");
            }
        } catch (ServerException ex) {
            return returnCancelTiktokCalendar(1, ex.getMessage());
        } catch (Exception ex) {
            return returnCancelTiktokCalendar(1, "订单退款异常");
        } finally {

        }
    }

    String queryNotifyTiktokCalendar(JSONObject body, DistributorRespDTO distributor) {
        // 订单号
        String orderNo = MapUtil.getStr(body, "order_out_id");
        RLock lock = null;
        try {
            // 要退票的抖音订单号
            String orderId = body.getStr("order_id");
            // 订单信息
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(orderId, distributor.getId().toString());
            // 订单不存在或订单无效时 同意退款
            if (order == null || order.getOrderStatus() == 0 || order.getOrderStatus() == 6) {
                JSONObject returnObject = new JSONObject();
                JSONObject dataObject = new JSONObject();
                // 错误码 1 其他异常 2 三方没有该订单
                dataObject.put("error_code", 1);
                // 错误信息
                dataObject.put("description", "无效订单");
                // 第三方订单ID
                if (StringUtils.isNotBlank(orderNo)) {
                    dataObject.put("order_out_id", orderNo);
                } else if (order != null) {
                    orderNo = order.getOrderNo();
                    dataObject.put("order_out_id", orderNo);
                }
                returnObject.put("data", dataObject);
                return returnObject.toString();
            }
            // 锁
            lock = redissonClient.getLock(order.getOrderNo() + distributor.getId());
            if (lock.tryLock(10, TimeUnit.SECONDS)) {

                JSONObject returnObject = new JSONObject();
                JSONObject dataObject = new JSONObject();
                // 0 成功
                dataObject.set("error_code", 0);
                // 错误信息
                dataObject.set("description", "订单状态查询成功");
                // 第三方订单ID
                dataObject.set("order_out_id", orderNo);

                JSONArray vouchers = new JSONArray();
                int useCount = 0;
                List<TradeDetailDO> detailDOList = tradeOrderRepositoryService.queryDetailByOrderId(order.getId());
                // 第三方凭证总数量
                dataObject.set("total_voucher_quantity", detailDOList.size());
                if (CollUtil.isNotEmpty(detailDOList)) {
                    for (TradeDetailDO detailDO : detailDOList) {
                        JSONObject entranceObject = new JSONObject();
                        // 项目唯一标识,不能重复票号
                        entranceObject.set("project_id", detailDO.getVoucherCode());
                        // 0为未使用，1为已使用
                        entranceObject.set("status", 0);
                        if (Objects.equals(detailDO.getVoucherStatus(), VoucherStatusEnum.VERIFIED.getStatus()) || Objects.equals(detailDO.getVoucherStatus(), VoucherStatusEnum.OVERDUE_VERIFIED.getStatus())) {
                            entranceObject.put("status", 1);
                            useCount += 1;
                        }
                        JSONObject voucher = new JSONObject();
                        voucher.put("entrance", entranceObject);
                        vouchers.add(voucher);
                    }
                }
                // 第三方凭证已使用数量
                dataObject.put("used_voucher_quantity", useCount);
                // 凭证列表
                dataObject.put("vouchers", vouchers);
                returnObject.put("data", dataObject);
                return returnObject.toString();
            } else {
                JSONObject returnObject = new JSONObject();
                JSONObject dataObject = new JSONObject();
                // 错误码 1 其他异常 2 三方没有该订单
                dataObject.put("error_code", 1);
                // 错误信息
                dataObject.put("description", "重复提交");
                // 第三方订单ID
                dataObject.put("order_out_id", orderNo);
                returnObject.put("data", dataObject);
                return returnObject.toString();
            }
        } catch (ServerException ex) {
            JSONObject returnObject = new JSONObject();
            JSONObject dataObject = new JSONObject();
            // 错误码 1 其他异常 2 三方没有该订单
            dataObject.put("error_code", 1);
            // 错误信息
            dataObject.put("description", ex.getMessage());
            // 第三方订单ID
            dataObject.put("order_out_id", orderNo);
            returnObject.put("data", dataObject);
            return returnObject.toString();
        } catch (Exception ex) {
            JSONObject returnObject = new JSONObject();
            JSONObject dataObject = new JSONObject();
            // 错误码 1 其他异常 2 三方没有该订单
            dataObject.put("error_code", 1);
            // 错误信息
            dataObject.put("description", "订单查询异常");
            // 第三方订单ID
            dataObject.put("order_out_id", orderNo);
            returnObject.put("data", dataObject);
            return returnObject.toString();
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }
    }

    @Override
    public boolean consumeNotice(DistributorRespDTO distributor, Map<String, Object> map) {
        String param = "";
        String sendUrl = "";
        try {
            JSONObject requestBody = new JSONObject();
            // 核销的抖音门店id
            // requestBody.put("poi_id", distributor.getOtaIp());
            requestBody.put("verify_token", RandomStringUtils.randomAlphanumeric(32));
            // 抖音订单号
            requestBody.put("order_id", MapUtil.getStr(map, "external_order_no"));
            JSONArray vouchers = new JSONArray();
            JSONObject entranceObject = new JSONObject();
            // 项目唯一标识,不能重复票号
            entranceObject.put("project_id", MapUtil.getStr(map, "voucherCode"));
            JSONObject voucher = new JSONObject();
            voucher.put("entrance", entranceObject);
            vouchers.add(voucher);
            requestBody.put("vouchers", vouchers);

            sendUrl = distributor.getServerUrl() + "/goodlife/v1/trip/certificate/verify/";

            param = requestBody.toString();

            Map<String, String> headParam = setHeadMap(distributor);
//            String sendResult = SendHttps.sendPost(sendUrl, param, headParam);
            String sendResult = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .body(param)
                    .headerMap(headParam, false)
                    .timeout(15000)
                    .execute().body();
            // 返回结果为空时
            if (StringUtils.isBlank(sendResult)) {
                notifyLogService.saveNotifyLog(map, sendUrl, param, sendResult, 0);
            }
            JSONObject returnObject = JSONUtil.parseObj(sendResult);
            JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("data"));
            boolean verifyResultsFlag = StringUtils.equals(dataObject.getStr("error_code"), "0");
            // 验券结果内容为空时
            if (verifyResultsFlag) {
                notifyLogService.saveNotifyLog(map, sendUrl, param, sendResult, 1);
            } else {
                notifyLogService.saveNotifyLog(map, sendUrl, param, sendResult, 0);
            }
            return verifyResultsFlag;
        } catch (Exception ex) {
            // 记录消费通知
            notifyLogService.saveNotifyLog(map, sendUrl, param, ex.getMessage(), 0);
            return false;
        }
    }

    private String checkRefund(TradeDetailDO detailDO) {
        Long productId = detailDO.getProductId();
        ProductSpuRespDTO productSpu = productApi.getProduct(productId).getCheckedData();
        ProductRefundRuleRespDTO refundRule = productSpu.getProductRefundRule();
//        boolean typeAllowRefund = ticketTypeService.allowRefund(typeId);
        if (refundRule.getRefundRule() != 0) {
            //查询退票规则
//            RefundRuleEntity refundRule = refundRuleService.queryByTicketTypeId(ticket.getTicketTypeId());
            if (refundRule != null) {
                //过期后是否可退
                if (refundRule.getExpireOperateType() != 3) {
                    if (detailDO.getValidityEndDate().compareTo(LocalDateTime.now()) < 0) {
                        //已过期不可退
                        return "已过期不可退";
                    }
                }
                //取票后是否可退
//                if (refundRule.getIsRejectRefund() == 0 && ticket.getTakeStatus() == 1) {
//                    //已取票不可退
//                    return "已取票不可退";
//                }
//                //是否可部分退票
//                if (refundRule.getIsPartRefund() == 0 && ticket.getTicketStatus() == 3) {
//                    //已检票不可退
//                    return "已检票不可退";
//                }
            }
            //查询子票
        }
        return "";
    }

    public String returnVerifyTiktokCalendar(int code, String description) {
        JSONObject returnObject = new JSONObject();
        JSONObject dataObject = new JSONObject();
        // 错误码
        dataObject.put("error_code", code);
        // 错误信息
        dataObject.put("description", description);
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    public String returnCreateOrderTiktokCalendar(String orderNo, int code, String description) {
        JSONObject returnObject = new JSONObject();
        JSONObject dataObject = new JSONObject();
        // 接口错误码
        dataObject.put("error_code", code);
        // 接口错误描述
        dataObject.put("description", description);
        // 外部订单 ID，当处理成功时必须返回且不为空
        if (StringUtils.isNotBlank(orderNo)) {
            dataObject.put("order_out_id", orderNo);
        }
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    public String returnCancelTiktokCalendar(int code, String description) {
        JSONObject returnObject = new JSONObject();
        JSONObject dataObject = new JSONObject();
        // 错误码
        dataObject.put("error_code", code);
        // 错误信息
        dataObject.put("description", description);
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    /**
     * @param code              退票结果
     * @param description       拒绝原因
     * @param auditRefundResult 审核退款结果
     * @param refundFeeAmount   退款手续费金额
     * @return java.lang.String
     * @description 退票返回抖音信息
     * @author 杨涛
     * @date 2025/9/27 15:50
     */
    public String returnRefundTiktokCalendar(int code, String description, int auditRefundResult, int refundFeeAmount) {
        JSONObject returnObject = new JSONObject();
        JSONObject dataObject = new JSONObject();
        // 错误码
        dataObject.put("error_code", code);
        // 错误信息
        dataObject.put("description", description);
        // 审核退款结果，当处理成功时必须返回，1 接受，2 拒绝，3 等待审核
        dataObject.put("audit_refund_result", auditRefundResult);
        // 退款手续费金额，审核退款结果为接受时必须返回，分
        dataObject.put("refund_fee_amount", refundFeeAmount);
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    /**
     * @param data base64后的密文
     * @return java.lang.String
     * @description AES解密
     * @author 杨涛
     * @date 2025/9/26 15:10
     */
    public static String decryptAES(String data, String appSecret) throws Exception {
        try {
            // 先用base64解密
            byte[] encrypted1 = decode(data);
            String key = getKey(appSecret);
            String iv = key.substring(16);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, "UTF-8");
            return originalString.trim();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String getKey(String appSecret) {
        return parseSecret(appSecret);
    }

    private static String parseSecret(String secret) {
        secret = fillSecret(secret);
        secret = cutSecret(secret);
        return secret;
    }


    private static String fillSecret(String secret) {
        if (secret.length() >= 32) {
            return secret;
        }
        int rightCnt = (32 - secret.length()) / 2;
        int leftCnt = 32 - secret.length() - rightCnt;

        StringBuilder sb = new StringBuilder("");
        for (int i = 0; i < leftCnt; i++) {
            sb.append('#');
        }
        sb.append(secret);
        for (int i = 0; i < rightCnt; i++) {
            sb.append('#');
        }
        return sb.toString();
    }

    private static String cutSecret(String secret) {
        if (secret.length() <= 32) {
            return secret;
        }

        int rightCnt = (secret.length() - 32) / 2;
        int leftCnt = secret.length() - 32 - rightCnt;

        return secret.substring(leftCnt, 32 + leftCnt);
    }

    /**
     * @param byteArray byte数组
     * @return java.lang.String
     * @description base64编码
     * @author 杨涛
     * @date 2025/5/12 9:04
     */
    public static String encode(byte[] byteArray) {
        return new String(Base64.getEncoder().encode(byteArray));
    }

    /**
     * @param base64EncodedString
     * @return byte[]
     * @description base64解码
     */
    public static byte[] decode(String base64EncodedString) {
        return Base64.getDecoder().decode(base64EncodedString);
    }

    private String handleOtaCertificateType(String type) {
        String otaCertificate = "";
        switch (type) {
            case "0":
                otaCertificate = "3";
                break;
            case "1":
                otaCertificate = "9";
                break;
            case "4":
                otaCertificate = "4";
                break;
            case "5":
                otaCertificate = "5";
                break;
            case "9":
                otaCertificate = "10";
                break;
            default:
                otaCertificate = "3";
                break;
        }

        return otaCertificate;
    }

    private Integer changeCertificateType(Integer type) {
        //1:身份证
        //2:港澳通行证
        //3:台湾通行证
        //4:回乡证
        //5:台胞证
        //6:护照
        //7 外籍护照
        //8 外国人永久居留证
        Integer otaCertificate = null;
        switch (type) {
            case 0:
                otaCertificate = 1;//身份证
                break;
            case 1:
                otaCertificate = 6; //外籍护照
                break;
            case 2:
                otaCertificate = 8; //外国人永居证
                break;

            case 5:
                otaCertificate = 2; //港澳通行证
                break;
            case 6:
                otaCertificate = 3; //台湾通行证
                break;
            case 7:
                otaCertificate = 4; //回乡证
                break;
            case 8:
                otaCertificate = 5; //台胞证
                break;
            case 9:
                otaCertificate = 7; //外籍护照
                break;
            default:
                break;
        }

        return otaCertificate;
    }

    private Integer changeIDCardTypeFormSystem(Integer type) {
        Integer otaCertificate = null;
        switch (type) {
            case 1:
                otaCertificate = CredentialTypeEnum.ID_CARD.getType();
                break;
            case 2:
                otaCertificate = CredentialTypeEnum.GANG_AO_PASS_CARD.getType();
                break;
            case 3:
                otaCertificate = CredentialTypeEnum.TW_PASS_CARD.getType(); ;
                break;
            case 4:
                otaCertificate = CredentialTypeEnum.HUI_XIANG_CARD.getType(); ;
                break;
            case 5:
                otaCertificate = CredentialTypeEnum.TAI_BAO_CARD.getType(); ;
                break;
            case 6:
                otaCertificate = CredentialTypeEnum.PASSPORT.getType(); ;
                break;
            case 7:
                otaCertificate = CredentialTypeEnum.FOREIGN_PASSPORT.getType(); ;
                break;
            case 8:
                otaCertificate = CredentialTypeEnum.FORERVER_LIVE_CARD.getType(); ;
                break;
            default:
                otaCertificate = CredentialTypeEnum.OTHER.getType(); ;
                break;
        }

        return otaCertificate;
    }

    @Override
    public boolean refundNotice(DistributorRespDTO distributor, Map<String, Object> map) {
        return false;
    }

    private String removeHtmlTags(String htmlStr) {
        // 定义HTML标签的正则表达式
        String regex = "<[^>]+>";

        // 使用正则表达式替换HTML标签
        String plainText = htmlStr.replaceAll(regex, "");

        return plainText;
    }


    /**
     * 判断目标日期是否在给定的日期范围内（包含边界）
     *
     * @param targetDate    目标日期（不能为 null）
     * @param saleStartDate 开始日期（可为 null）
     * @param saleEndDate   结束日期（可为 null）
     * @return 是否在范围内
     */
    private boolean isDateInRange(LocalDate targetDate,
                                  LocalDate saleStartDate,
                                  LocalDate saleEndDate) {
        Asserts.isTrue(targetDate != null, "目标日期不能为空");
        boolean afterStart = (saleStartDate == null) || !targetDate.isBefore(saleStartDate);
        boolean beforeEnd = (saleEndDate == null) || !targetDate.isAfter(saleEndDate);

        return afterStart && beforeEnd;
    }

}




