package com.liquidnet.service.order.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.UserPathDto;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.GoblinQueueBizArtworkGenDto;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinNftOrder;
import com.liquidnet.service.goblin.param.GoblinNftOrderPayAgainParam;
import com.liquidnet.service.goblin.param.GoblinNftOrderPayCallbackParam;
import com.liquidnet.service.goblin.param.GoblinNftOrderPayParam;
import com.liquidnet.service.goblin.param.GoblinNftOrderRefundCallbackParam;
import com.liquidnet.service.goblin.service.IGoblinNftOrderService;
import com.liquidnet.service.order.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;

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

import static com.liquidnet.commons.lang.util.DateUtil.DTF_YMD_HMS;

@Service
@Slf4j
public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService {

    @Autowired
    GoblinRedisUtils goblinRedisUtils;
    @Autowired
    GoblinMongoUtils goblinMongoUtils;
    @Autowired
    QueueUtils queueUtils;
    @Autowired
    GoblinOrderUtils goblinOrderUtils;
    @Autowired
    GoblinNftOrderUtils nftOrderUtils;

    @Value("${liquidnet.service.order.url-pay.pay}")
    private String payUrl;
    @Value("${liquidnet.service.order.url-pay.check}")
    private String checkUrl;
    @Value("${liquidnet.service.order.url-pay.nftPayNotify}")
    private String synUrl;

    @Override
    public ResponseDto<GoblinNftPayResultVo> checkOrder(GoblinNftOrderPayParam payParam) {
        String uid = CurrentUtil.getCurrentUid();
        // 加锁
        boolean isLock = nftOrderUtils.setNftOrderBuyLock(uid);
        if (!isLock) {
            return ResponseDto.failure("排队处理中，请勿重复操作～");
        } else {
            try {
                // 认证验证
                boolean numAccount = goblinRedisUtils.getNftNumAccount(uid);
                if (!numAccount) {
//                    return ResponseDto.failure("您还未开通数字账户～");
                }

                // 是否存在此商品信息 是否隐藏
                String skuId = payParam.getSkuId();
                int number = 1;
                GoblinGoodsSkuInfoVo skuVo = goblinRedisUtils.getGoodsSkuInfoVo(skuId);
                if (null == skuVo || (null != skuVo.getSkuAppear() && skuVo.getSkuAppear().equals("1"))) {
                    return ResponseDto.failure("该商品不存在～");
                }
                String spuId = skuVo.getSpuId();

                // 判断是否藏品
                if (!Objects.equals(1, skuVo.getSkuType())) {
                    return ResponseDto.failure("该商品不属于藏品～");
                }
                // 3审核通过 3已上架 0未删除 才可购买
                if (
                        !skuVo.getStatus().equals("3") || !skuVo.getShelvesStatus().equals("3") ||
                                skuVo.getDelFlg().equals("1") || (null != skuVo.getSkuCanbuy() && skuVo.getSkuCanbuy().equals("0"))
                ) {
                    return ResponseDto.failure("该商品当前状态不可购买～");
                }
                // 主动设置的售罄
                if (null != skuVo.getSoldoutStatus() && skuVo.getSoldoutStatus().equals(1)) {
                    return ResponseDto.failure("该商品已售罄～");
                }
                // 判断开售、停售时间
                LocalDateTime saleStartTime = skuVo.getSaleStartTime();
                LocalDateTime saleStopTime = skuVo.getSaleStopTime();
                LocalDateTime nowTime = LocalDateTime.now();
                if (nowTime.isBefore(saleStartTime)) {
                    return ResponseDto.failure("该商品还未开始售卖～");
                } else if (null != saleStopTime && nowTime.isAfter(saleStopTime)) {
                    return ResponseDto.failure("该商品已停售～");
                }
                // 判断数量限购 盲盒暂时没限购但是要记录购买数量 所以也放在这里 这样既能都记录限购数量又不至于判断处理麻烦
                Integer limitCount = skuVo.getBuyLimit();
                // 是否限购都记录一个购买数量 防止中间增加限购 开始没加后面判断库存减了也是有问题的 或者放在最后也行 因为对单用户限流了
                int buyCount = goblinRedisUtils.incrSkuCountByUid(uid, skuId, number);
                if (null != limitCount && limitCount > 0) {
                    if (buyCount > limitCount) {
                        goblinRedisUtils.decrSkuCountByUid(uid, skuId, number);
                        return ResponseDto.failure("您已超出限购数量～");
                    }
                }
                // 权限限购
                /*String mobile = StringUtils.defaultString(((String) CurrentUtil.getTokenClaims().get(CurrentUtil.TOKEN_MOBILE)), "");
                boolean isVip = nftOrderUtils.isVipMember(uid);
                Boolean isAuthBuy = goblinOrderUtils.judgeOrderRose(isVip, skuId, mobile, Integer.parseInt(skuVo.getBuyFactor()));
                if (!isAuthBuy) {
                    return ResponseDto.failure("该商品仅限特定用户购买～");
                }*/
                // 判断优惠券不能一起使用
                /*String platVoucherCode = payParam.getPlatVoucherCode();
                String storeVoucherCode = payParam.getStoreVoucherCode();
                if (!StringUtils.isEmpty(platVoucherCode) && !StringUtils.isEmpty(storeVoucherCode)) {
                    return ResponseDto.failure("平台券与店铺券不可一起使用哦～");
                }*/
                String boxSkuId = "";
                // 购买盲盒回滚抽到的库存 购买藏品回滚购买的库存
                String stockSkuId = skuId;
                if (skuVo.getUnbox().equals("1")) {// 盲盒逻辑
                    GoblinGoodsInfoVo spuInfoVo = goblinRedisUtils.getGoodsInfoVo(spuId);
                    List<String> skuIdList = spuInfoVo.getSkuIdList();
                    if (CollectionUtil.isEmpty(skuIdList)) {
                        return ResponseDto.failure("该商品SPU不存在～");
                    } else {
                        GoblinGoodsSkuInfoVo skuInfoVo = lotteryDraw(skuIdList, number);
                        if (null == skuInfoVo) {
                            goblinRedisUtils.decrSkuCountByUid(uid, skuId, number);
                            return ResponseDto.failure("盲盒库存不足啦～");
                        }
                        boxSkuId = skuInfoVo.getSkuId();
                        stockSkuId = boxSkuId;
                    }
                } else {// 普通藏品逻辑
                    // 判断库存
                    int surplusGeneral = nftOrderUtils.decrSkuStock(skuId, number);
                    if (surplusGeneral < 0) {
                        nftOrderUtils.backSkuCountAndStock(uid, stockSkuId, skuId, number);
                        return ResponseDto.failure("库存不足啦～");
                    }
                }

                /**
                 * 判断优惠券
                 * 因平台券和店铺券不能同时使用所以不需要相互考虑失败退另一种券的问题,各自处理就好
                 * 回滚好库存和用户已购买数量
                 * 无论平台券还是店铺券都只支持代金券1的类型
                 */
                BigDecimal voucherPrice = BigDecimal.ZERO;
                BigDecimal storeVoucherPrice = BigDecimal.ZERO;
                String orderId = IDGenerator.nextSnowId();
                String orderCode = IDGenerator.storeCode(orderId);
                BigDecimal totalPrice = skuVo.getPrice();
                /*if (!StringUtils.isEmpty(platVoucherCode)) {// 平台券
                    GoblinUseResultVo useResultVo = nftOrderUtils.useCoupon(platVoucherCode, "购买NFT商品[" + orderCode + "]", totalPrice, spuId, uid);
                    String typeVoucher = useResultVo.getCouType();
                    if (typeVoucher.equals("-1")) {
                        nftOrderUtils.backSkuCountAndStock(uid, skuId, number);
                        return ResponseDto.failure("当前平台券不允许使用～");
                    } else {
                        voucherPrice = useResultVo.getValue();
                    }
                }
                if (!StringUtils.isEmpty(storeVoucherCode)) {// 店铺券
                    GoblinUseResultVo storeCouponVo = nftOrderUtils.useStoreCoupon(storeVoucherCode, "购买NFT商品[" + orderCode + "]", totalPrice, spuId, uid);
                    String typeVoucher = storeCouponVo.getCouType();
                    if (typeVoucher.equals("-1")) {
                        nftOrderUtils.backSkuCountAndStock(uid, skuId, number);
                        return ResponseDto.failure("当前店铺券不允许使用～");
                    } else {
                        storeVoucherPrice = storeCouponVo.getValue();
                    }
                }*/

                // 下单数据
                GoblinNftOrder nftOrder = order(payParam, skuVo.getStoreId(), uid, spuId, number, orderId, orderCode, totalPrice, voucherPrice, storeVoucherPrice, boxSkuId);
                if (null == nftOrder) {
                    nftOrderUtils.backSkuCountAndStock(uid, stockSkuId, skuId, number);
                    return ResponseDto.failure("下单失败～");
                }

                // 下单唤起支付
                GoblinNftPayResultVo nftPayResultVo = payOrder(nftOrder, uid, payParam);
                if (null == nftPayResultVo) {
                    nftOrderUtils.backSkuCountAndStock(uid, stockSkuId, skuId, number);
                    return ResponseDto.failure("下单失败啦～");
                }
                return ResponseDto.success(nftPayResultVo);
            } catch (Exception e) {
                log.error("NFT下单异常 e:{}", e);
                return ResponseDto.failure("下单失败，请稍后重试！");
            } finally {
                // 执行完毕。释放锁
                nftOrderUtils.delNftOrderBuyLock(uid);
            }
        }
    }

    private GoblinGoodsSkuInfoVo lotteryDraw(List<String> skuIdList, int number) {
        try {
            ArrayList<GoblinGoodsSkuInfoVo> skuInfoVos = ObjectUtil.cloneArrayGoblinGoodsSkuInfoListVo();
            for (String kid : skuIdList) {
                GoblinGoodsSkuInfoVo skuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(kid);
                int skuStock = goblinRedisUtils.getSkuStock(kid);
                if (null != skuInfoVo && skuStock > 0) {// 有库存的加入奖池
                    skuInfoVos.add(skuInfoVo);
                }
            }
            if (CollectionUtil.isEmpty(skuInfoVos)) {
                return null;
            } else {
                // 计算总概率 和 剩余没填概率的平均概率
                int size = 0;
                BigDecimal sumHitRatio = BigDecimal.ZERO;
                for (GoblinGoodsSkuInfoVo skuInfoVo : skuInfoVos) {
                    if (null == skuInfoVo.getHitRatio() || skuInfoVo.getHitRatio().compareTo(BigDecimal.ZERO) <= 0) {
                        size++;
                    } else {
                        sumHitRatio = sumHitRatio.add(skuInfoVo.getHitRatio());
                    }
                }
                /**
                 * 剔除掉没库存的商品再去算未设置概率商品的平均概率 会导致未设置概率的商品的概率增加
                 * 如果不剔除掉没库存的商品去算未设置概率商品的平均概率 会导致总概率小于100 如果随机数还是0-100的话就有可能会中奖溢出
                 * 想保持概率 1.不剔除无库存商品算平均概率 2.随机数最大值为排序后概率的最后一个值
                 */
                BigDecimal avgHitRatio = BigDecimal.ZERO;
                if (size > 0) {// 说明有未设置抽奖概率的
                    avgHitRatio = new BigDecimal(100).subtract(sumHitRatio).divide(new BigDecimal(size), 2, RoundingMode.HALF_UP);
                }
                // 未设置概率的写入概率
                int skuListSize = skuInfoVos.size();
                for (int i = 0; i < skuListSize; i++) {
                    GoblinGoodsSkuInfoVo infoVo = skuInfoVos.get(i);
                    if (null == infoVo.getHitRatio() || infoVo.getHitRatio().compareTo(BigDecimal.ZERO) <= 0) {
                        skuInfoVos.get(i).setHitRatio(avgHitRatio);
                    }
                    // 等于0的最终概率就是设置的值 大于0最终的概率是自己的+上面的
                    if (i > 0) {
                        skuInfoVos.get(i).setHitRatio(skuInfoVos.get(i - 1).getHitRatio().add(skuInfoVos.get(i).getHitRatio()));
                    }
                }
                // 按照概率排序
                List<GoblinGoodsSkuInfoVo> listSort = skuInfoVos.stream().sorted(Comparator.comparing(GoblinGoodsSkuInfoVo::getHitRatio)).collect(Collectors.toList());
                List<BigDecimal> hitRatioList = listSort.stream().map(GoblinGoodsSkuInfoVo::getHitRatio).collect(Collectors.toList());
                // 根据区块值来获取抽取到的物品索引
                double nextDouble = Math.random();
                BigDecimal nextDoubleNew = BigDecimal.valueOf(nextDouble);
                nextDoubleNew = nextDoubleNew.multiply(hitRatioList.get(hitRatioList.size() - 1)).setScale(4, RoundingMode.HALF_UP);

                hitRatioList.add(nextDoubleNew);
                Collections.sort(hitRatioList);

                int index = hitRatioList.indexOf(nextDoubleNew);

                GoblinGoodsSkuInfoVo goodsSkuInfoVo = listSort.get(index);
                // 判断库存
                int surplusGeneral = nftOrderUtils.decrSkuStock(goodsSkuInfoVo.getSkuId(), number);
                if (surplusGeneral < 0) {
                    nftOrderUtils.incrSkuStock(goodsSkuInfoVo.getSkuId(), number);
                    return lotteryDraw(skuIdList, number);
                } else {
                    return goodsSkuInfoVo;
                }
            }
        } catch (Exception e) {
            log.error("NFT下单-抽盲盒异常 e:{}", e);
            return null;
        }
    }

    private GoblinNftOrder order(
            GoblinNftOrderPayParam payParam, String storeId, String uid, String spuId, int number,
            String orderId, String orderCode,
            BigDecimal totalPrice, BigDecimal voucherPrice, BigDecimal storeVoucherPrice, String boxSkuId
    ) {
        try {
            LocalDateTime now = LocalDateTime.now();

            Map token = CurrentUtil.getTokenClaims();
            String headerCliSource = CurrentUtil.getHeaderCliSource(), headerCliVersion = CurrentUtil.getHeaderCliVersion();
            String source = headerCliSource == null ? "" : headerCliSource;
            String version = headerCliVersion == null ? "" : headerCliVersion;

            GoblinStoreInfoVo storeInfoVo = goblinRedisUtils.getStoreInfoVo(storeId);
            String storeName = storeInfoVo.getStoreName();

            // 生成订单
            GoblinNftOrder nftOrder = GoblinNftOrder.getNew();
            nftOrder.setOrderId(orderId);
            nftOrder.setSpuId(spuId);
            nftOrder.setSkuId(payParam.getSkuId());
            nftOrder.setBoxSkuId(boxSkuId);
            nftOrder.setNum(number);
            nftOrder.setStoreId(storeId);
            nftOrder.setStoreName(storeName);
            nftOrder.setOrderCode(orderCode);
            nftOrder.setUserId(uid);
            nftOrder.setUserName(StringUtils.defaultString(((String) token.get(CurrentUtil.TOKEN_NICKNAME)), ""));
            nftOrder.setUserMobile(StringUtils.defaultString(((String) token.get(CurrentUtil.TOKEN_MOBILE)), ""));
            nftOrder.setPriceTotal(totalPrice);
            nftOrder.setPriceCoupon(voucherPrice);
            nftOrder.setStorePriceCoupon(storeVoucherPrice);
            nftOrder.setPriceRedEnvelope(BigDecimal.ZERO);
            nftOrder.setPriceVoucher(voucherPrice.add(storeVoucherPrice));
            BigDecimal priceActual = totalPrice.subtract(voucherPrice).subtract(storeVoucherPrice);
            nftOrder.setPriceActual(priceActual.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : priceActual);
            /*nftOrder.setUcouponId(payParam.getPlatVoucherCode());
            nftOrder.setStoreCouponId(payParam.getStoreVoucherCode());*/
            nftOrder.setUcouponId("");
            nftOrder.setStoreCouponId("");
            nftOrder.setRedEnvelopeCode("");
            nftOrder.setStatus(GoblinStatusConst.NftStatus.ORDER_STATUS_1.getValue());
            nftOrder.setSource(source);
            nftOrder.setOrderType(GoblinStatusConst.NftStatus.ORDER_TYPE_1.getValue());
            nftOrder.setPayType(payParam.getPayType());
            nftOrder.setDeviceFrom(payParam.getDeviceFrom());
            nftOrder.setVersion(version);
            nftOrder.setPayCountdownMinute(5);
            nftOrder.setIpAddress(CurrentUtil.getCliIpAddr());
            nftOrder.setCreatedAt(now);

            return nftOrder;
        } catch (Exception e) {
            log.error("NFT下单-整合订单数据异常 e:{}", e);
            return null;
        }
    }

    private GoblinNftPayResultVo payOrder(GoblinNftOrder nftOrder, String uid, GoblinNftOrderPayParam payParam) {
        try {
            // 是否免费
            boolean isFree = false;
            GoblinNftPayResultVo NftPayResultVo = GoblinNftPayResultVo.getNew();
            NftPayResultVo.setOrderId(nftOrder.getOrderId());
            String payCode;
            if (nftOrder.getPriceActual().compareTo(BigDecimal.valueOf(0)) > 0) {// 调用支付
                LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString();
                httpData.add("type", "NFT");
                httpData.add("price", nftOrder.getPriceActual().toString());
                // 测试0.01
                // httpData.add("price","0.01");
                httpData.add("name", nftOrder.getStoreName());
                GoblinGoodsSkuInfoVo skuVo = goblinRedisUtils.getGoodsSkuInfoVo(nftOrder.getSkuId());
                httpData.add("detail", skuVo.getName());
                httpData.add("orderCode", nftOrder.getOrderCode());
                httpData.add("orderId", nftOrder.getOrderId());
                httpData.add("clientIp", CurrentUtil.getCliIpAddr());
                httpData.add("notifyUrl", synUrl);
                httpData.add("createDate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                httpData.add("expireTime", "5");
                httpData.add("payType", nftOrder.getPayType());
                httpData.add("deviceFrom", nftOrder.getDeviceFrom());
                if (nftOrder.getDeviceFrom().equals("js") || nftOrder.getDeviceFrom().equals("applet")) {
                    httpData.add("openId", payParam.getOpenId());
                }
                String showUrl = payParam.getShowUrl().concat(nftOrder.getOrderCode());
                String returnUrl = payParam.getReturnUrl().concat(nftOrder.getOrderCode());
                httpData.add("showUrl", showUrl);
                httpData.add("returnUrl", returnUrl);

                LinkedMultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString();
                header.add("Accept", "application/json;charset=UTF-8");
                String returnData = HttpUtil.post(payUrl, httpData, header);
                log.info("调用 DRAGON 结果 = " + returnData);
                ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
                });
                GoblinPayInnerResultVo dtoData = dto.getData();

                payCode = dtoData.getCode();
                NftPayResultVo.setCode(payCode);
                NftPayResultVo.setOrderCode(dtoData.getOrderCode());
                NftPayResultVo.setPayData(dtoData.getPayData());
                NftPayResultVo.setPayType(nftOrder.getPayType());
                NftPayResultVo.setPrice(nftOrder.getPriceActual());
                NftPayResultVo.setShowUrl(showUrl);
                NftPayResultVo.setReturnUrl(returnUrl);
            } else {
                isFree = true;
                nftOrder.setPayType("FREE");
                payCode = "FREE_PAY_CODE";
                NftPayResultVo.setPrice(BigDecimal.valueOf(0));
                NftPayResultVo.setPayType(nftOrder.getPayType());
            }

            LinkedList<String> sqls = CollectionUtil.linkedListString();
            sqls.add(SqlMapping.get("goblin_nft_order.insert"));
            LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();

            nftOrder.setPayCode(payCode);
            sqlDataOrder.add(new Object[]{
                    nftOrder.getOrderId(), nftOrder.getSpuId(), nftOrder.getSkuId(), nftOrder.getBoxSkuId(),
                    nftOrder.getNum(), nftOrder.getStoreId(), nftOrder.getStoreName(), nftOrder.getOrderCode(), nftOrder.getUserId(), nftOrder.getUserName(), nftOrder.getUserMobile(), nftOrder.getPriceTotal(), nftOrder.getPriceCoupon(),
                    nftOrder.getStorePriceCoupon(), nftOrder.getPriceRedEnvelope(), nftOrder.getPriceVoucher(), nftOrder.getPriceActual(), nftOrder.getUcouponId(), nftOrder.getStoreCouponId(), nftOrder.getRedEnvelopeCode(), nftOrder.getStatus(), nftOrder.getSource(),
                    nftOrder.getOrderType(), nftOrder.getPayType(), nftOrder.getDeviceFrom(), nftOrder.getVersion(), nftOrder.getPayCountdownMinute(), nftOrder.getIpAddress(), nftOrder.getCreatedAt(), nftOrder.getPayCode()
            });

            // 订单vo
            GoblinNftOrderVo orderVo = GoblinNftOrderVo.getNew().copy(nftOrder);
            orderVo.setCreatedAt(nftOrder.getCreatedAt());

            // 待支付发送队列
            queueUtils.sendMsgByRedisGoblinStock(orderVo.getOrderId(), nftOrder.getCreatedAt(), "NFT");

            // redis 订单详情
            nftOrderUtils.setNftOrder(orderVo);
            nftOrderUtils.setNftOrderIdOfCode(nftOrder.getOrderCode(), nftOrder.getOrderId());
            // redis 订单列表
            // nftOrderUtils.addNftOrderList(uid, orderVo.getOrderId());

            // mongo
            goblinMongoUtils.setGoblinNftOrderVo(orderVo);

            // 执行sql
            String sqlData = SqlMapping.gets(sqls, sqlDataOrder);
            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_NFT_ORDER.getKey(), sqlData);

            log.info(UserPathDto.setData("NFT下单(唤起支付)", nftOrder, NftPayResultVo));
            if (isFree) {// 免费直接回调
                GoblinNftOrderPayCallbackParam NftOrderPayCallbackParam = GoblinNftOrderPayCallbackParam.getNew();
                NftOrderPayCallbackParam.setCode(payCode);
                NftOrderPayCallbackParam.setOrderCode(nftOrder.getOrderCode());
                NftOrderPayCallbackParam.setPaymentAt(DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss));
                NftOrderPayCallbackParam.setPrice(nftOrder.getPriceActual());
                NftOrderPayCallbackParam.setPaymentId("FREE_PAYMENT_ID");
                NftOrderPayCallbackParam.setPaymentType(null);
                NftOrderPayCallbackParam.setStatus(1);
                syncOrder(NftOrderPayCallbackParam);
            }

            return NftPayResultVo;
        } catch (Exception e) {
            log.error("NFT下单-生成订单唤起支付异常 e:{}", e);
            return null;
        }
    }

    @Override
    public ResponseDto<GoblinNftPayResultVo> payAgain(GoblinNftOrderPayAgainParam param) {
        return ResponseDto.failure("暂不支持此接口");
        /*String uid = CurrentUtil.getCurrentUid();
        LocalDateTime now = LocalDateTime.now();
        //检查订单时间 是否关闭
        GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(param.getOrderId());

        if (storeOrderVo == null) {
            return ResponseDto.failure("订单不存在");
        }

        if (!storeOrderVo.getUserId().equals(uid)) {
            return ResponseDto.failure(ErrorMapping.get("20003"));
        }

        if (storeOrderVo.getStatus() != GoblinStatusConst.Status.ORDER_STATUS_0.getValue()) {
            return ResponseDto.failure("订单无法支付");//订单
        }
        MultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString();
        header.add("Accept", "application/json;charset=UTF-8");
        String returnCheckData = HttpUtil.get(checkUrl + "?code=" + storeOrderVo.getPayCode(), null, header);
        ResponseDto<GoblinPayInnerResultVo> checkVo = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
        });
        if (checkVo.getData().getStatus() == 1) {
            return ResponseDto.failure("订单已支付");
        }
        BigDecimal price = BigDecimal.ZERO;
        String[] orderIds = goblinRedisUtils.getMasterCode(storeOrderVo.getMasterOrderCode());
        for (String orderId : orderIds) {
            GoblinStoreOrderVo orderVo = goblinRedisUtils.getGoblinOrder(orderId);
            price = price.add(orderVo.getPriceActual());
        }
        GoblinNftPayResultVo NftPayResultVo = GoblinNftPayResultVo.getNew();
        if (!storeOrderVo.getPayType().equals("FREE")) {
            // 调用支付
            LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString();
            httpData.add("type", "PRODUCT");
            httpData.add("price", price.toString());
            httpData.add("name", goblinRedisUtils.getStoreInfoVo(storeOrderVo.getStoreId()).getStoreName());
            httpData.add("detail", "查找最初订单");
            httpData.add("orderCode", storeOrderVo.getMasterOrderCode());
            httpData.add("clientIp", storeOrderVo.getIpAddress());
            httpData.add("notifyUrl", synUrl);
            httpData.add("createDate", storeOrderVo.getCreatedAt());
            httpData.add("expireTime", storeOrderVo.getPayCountdownMinute().toString());
            httpData.add("payType", param.getPayType());
            httpData.add("deviceFrom", param.getDeviceFrom());

            if (param.getDeviceFrom().equals("js") || param.getDeviceFrom().equals("applet")) {
                httpData.add("openId", param.getOpenId());
            }
            httpData.add("showUrl", param.getShowUrl() + storeOrderVo.getMasterOrderCode());
            httpData.add("returnUrl", param.getReturnUrl() + storeOrderVo.getMasterOrderCode());
            String returnData = HttpUtil.post(payUrl, httpData);
            log.debug("调用 DRAGON 结果 = " + returnData);
            ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
            });
            NftPayResultVo.setCode(dto.getData().getCode());
            NftPayResultVo.setOrderCode(storeOrderVo.getMasterOrderCode());
            NftPayResultVo.setPayData(dto.getData().getPayData());
            NftPayResultVo.setOrderId(storeOrderVo.getMasterOrderCode());
            NftPayResultVo.setPayType(param.getPayType());
            NftPayResultVo.setPrice(price);
            NftPayResultVo.setShowUrl(param.getShowUrl() + storeOrderVo.getMasterOrderCode());
            NftPayResultVo.setReturnUrl(param.getReturnUrl() + storeOrderVo.getMasterOrderCode());
            //redis
            storeOrderVo.setPayType(param.getPayType());
            storeOrderVo.setDeviceFrom(param.getDeviceFrom());
            storeOrderVo.setPayCode(NftPayResultVo.getCode());
            goblinRedisUtils.setGoblinOrder(storeOrderVo.getOrderId(), storeOrderVo);
            //mongo
            goblinMongoUtils.updateGoblinStoreOrderVo(storeOrderVo.getOrderId(), storeOrderVo);
            //mysql
            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_ORDER_AGAIN.getKey(),
                    SqlMapping.get("goblin_order.pay.again", param.getPayType(), param.getDeviceFrom(), NftPayResultVo.getCode(), param.getOrderId(), now, now));
            log.info(UserPathDto.setData("再次支付[新支付方式]", param, NftPayResultVo));
        }
        return ResponseDto.success(NftPayResultVo);*/
    }

    @Override
    public ResponseDto<Integer> checkOrderResult(String orderId) {
        String uid = CurrentUtil.getCurrentUid();
        GoblinNftOrderVo nftOrder = nftOrderUtils.getNftOrder(orderId);
        if (null == nftOrder || !nftOrder.getUserId().equals(uid)) {
            return ResponseDto.failure("订单不存在");
        } else {
            String returnCheckData = HttpUtil.get(checkUrl + "?code=" + nftOrder.getPayCode(), null);
            ResponseDto<GoblinNftOrderPayCallbackParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<GoblinNftOrderPayCallbackParam>>() {
            });
            if (syncOrderDtoParam.getData().getStatus() == 1) {
                //处理订单
                syncOrder(syncOrderDtoParam.getData());
                return ResponseDto.success(1);
            } else {
                return ResponseDto.success(0);
            }
        }
    }

    @Override
    public String syncOrder(GoblinNftOrderPayCallbackParam syncOrderParam) {
        log.info("NFT支付回调 参数: " + syncOrderParam.toString());

        String orderCode = syncOrderParam.getOrderCode();

        String orderId = nftOrderUtils.getNftOrderIdOfCode(orderCode);
        if (orderId.isEmpty()) {
            log.error("Nft订单不存在1 param:[orderCode:{}]", orderCode);
            return "fail";
        }
        GoblinNftOrderVo orderVo = nftOrderUtils.getNftOrder(orderId);
        if (null == orderVo) {
            log.error("Nft订单不存在2 param:[orderCode:{}]", orderCode);
            return "fail";
        }
        if (!orderVo.getPayCode().equals(syncOrderParam.getCode())) {
            log.error("Nft订单payCode不匹配 param:[orderCode:{}, payCode:{}, code:{}]", orderCode, orderVo.getPayCode(), syncOrderParam.getCode());
            return "fail";
        }
        if (
                !Objects.equals(orderVo.getStatus(), GoblinStatusConst.NftStatus.ORDER_STATUS_1.getValue())
                        &&
                        !Objects.equals(orderVo.getStatus(), GoblinStatusConst.NftStatus.ORDER_STATUS_3.getValue())
        ) {
            log.error("Nft订单当前状态不能支付回调 param:[orderCode:{}, status:{}]", orderCode, orderVo.getStatus());
            return "fail";
        }

        if (Objects.equals(1, syncOrderParam.getStatus())) {
            String uid = orderVo.getUserId();
            LocalDateTime now = LocalDateTime.now();
            if (Objects.equals(orderVo.getStatus(), GoblinStatusConst.NftStatus.ORDER_STATUS_3.getValue())) {// 超时支付
                log.info("Nft订单超时支付 param:[orderCode:{}]", orderCode);
                // 处理为退款中
                orderVo.setStatus(GoblinStatusConst.NftStatus.ORDER_STATUS_4.getValue());
                orderVo.setUpdatedAt(now);
                LinkedList<String> sqls = CollectionUtil.linkedListString();
                LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();
                sqls.add(SqlMapping.get("goblin_nft_order.update.refund"));
                sqlDataOrder.add(new Object[]{
                        orderVo.getStatus(), now, orderId, now, now
                });
                nftOrderUtils.setNftOrder(orderVo);
                // mongo
                goblinMongoUtils.updateGoblinNftOrderVo(orderVo);
                // mysql
                queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_NFT_ORDER.getKey(),
                        SqlMapping.gets(sqls, sqlDataOrder));
                // 退款
                nftOrderUtils.refundOrderSku(orderId, syncOrderParam.getPaymentId(), syncOrderParam.getPaymentType());
            } else {// 正常流程
                orderVo.setPaymentType(syncOrderParam.getPaymentType());
                orderVo.setPaymentId(syncOrderParam.getPaymentId());
                orderVo.setPayCode(syncOrderParam.getCode());
                LocalDateTime payTime = LocalDateTime.parse(syncOrderParam.getPaymentAt(), DTF_YMD_HMS);
                orderVo.setPayTime(payTime);
                orderVo.setStatus(GoblinStatusConst.NftStatus.ORDER_STATUS_2.getValue());
                orderVo.setUpdatedAt(now);

                LinkedList<String> sqls = CollectionUtil.linkedListString();
                LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();
                sqls.add(SqlMapping.get("goblin_nft_order.update.pay"));
                sqlDataOrder.add(new Object[]{
                        orderVo.getPaymentType(), orderVo.getPaymentId(), orderVo.getPayCode(),
                        orderVo.getPayTime(), orderVo.getStatus(), orderVo.getUpdatedAt(),
                        orderId, now, now
                });

                // redis
                nftOrderUtils.setNftOrder(orderVo);
                // mongo
                goblinMongoUtils.updateGoblinNftOrderVo(orderVo);
                // mysql
                queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_NFT_ORDER.getKey(),
                        SqlMapping.gets(sqls, sqlDataOrder));

                // 通知生成藏品
                GoblinQueueBizArtworkGenDto artworkGenDto = GoblinQueueBizArtworkGenDto.getNew();
                artworkGenDto.setOrderId(orderId);
                artworkGenDto.setUid(uid);
                artworkGenDto.setSkuId(orderVo.getSkuId());
                artworkGenDto.setSource(1);
                queueUtils.sendMsgByRedis(MQConst.GoblinQueue.BIZ_ARTWORK_GEN.getKey(), artworkGenDto.toJson());

                // 增加销量
                goblinRedisUtils.incrSkuSaleCount(orderVo.getSpuId(), orderVo.getSkuId(), orderVo.getNum());

                // 加积分
                goblinOrderUtils.doTask(uid, orderVo.getPriceActual());

            }
            // 写入用户订单列表 因取消的订单不展示 所以放在这里
            nftOrderUtils.addNftOrderList(uid, orderVo.getOrderId());
        }

        return "success";
    }

    @Override
    public String refundSyncOrder(GoblinNftOrderRefundCallbackParam refundCallbackParam) {
        LocalDateTime now = LocalDateTime.now();
        log.info("NftRefundCallback订单退款回调参数: [RefundCallbackParam={}]", refundCallbackParam);
        GoblinNftOrderRefundVo nftOrderRefundVo = goblinMongoUtils.getGoblinNftOrderRefundVo(refundCallbackParam.getOrderRefundCode());
        if (nftOrderRefundVo == null) {
            log.info("NftRefundCallback: 退款订单查询失败，编号{}", refundCallbackParam.getOrderRefundCode());
            return "fail";
        }
        if (nftOrderRefundVo.getStatus() == GoblinStatusConst.NftStatus.ORDER_REFUND_STATUS_2.getValue()) {
            log.info("NftRefundCallback: 退款订单已完成，编号{}", refundCallbackParam.getOrderRefundCode());
            return "success";
        }
        Integer status = refundCallbackParam.getStatus();
        if (1 == status) { // 退款成功
            String orderId = nftOrderRefundVo.getOrderId();
            GoblinNftOrderVo nftOrder = nftOrderUtils.getNftOrder(orderId);
            nftOrder.setStatus(GoblinStatusConst.NftStatus.ORDER_STATUS_5.getValue());
            nftOrder.setUpdatedAt(now);
            // 取消订单已经回滚了库存和购买数量 这里不需要处理了
            /*if (StringUtil.isNotEmpty(nftOrder.getUcouponId())) {
                goblinOrderUtils.backCoupon(nftOrder.getUcouponId(), nftOrder.getUserId());
            }

            if (StringUtil.isNotEmpty(nftOrder.getStoreCouponId())) {
                List<BackCouponParam> params = ObjectUtil.getBackCouponParam();
                BackCouponParam backCouponParam = BackCouponParam.getNew();
                backCouponParam.setuCouponIds(nftOrder.getStoreCouponId());
                backCouponParam.setUid(nftOrder.getUserId());
                params.add(backCouponParam);
                goblinOrderUtils.backStoreCoupon(params);
            }*/
            nftOrderRefundVo.setStatus(GoblinStatusConst.NftStatus.ORDER_REFUND_STATUS_2.getValue());
            nftOrderRefundVo.setRefundAt(now);
            nftOrderRefundVo.setUpdatedAt(now);
            //redis
            nftOrderUtils.setNftOrder(nftOrder);
            nftOrderUtils.setBackOrderVo(nftOrderRefundVo);
            //mongo
            goblinMongoUtils.updateGoblinNftOrderVo(nftOrder);
            goblinMongoUtils.updateGoblinNftOrderRefundVo(nftOrderRefundVo);
            //mysql
            LinkedList<String> sqls = CollectionUtil.linkedListString();
            LinkedList<Object[]> sqlsOrder = CollectionUtil.linkedListObjectArr();
            LinkedList<Object[]> sqlsBackOrder = CollectionUtil.linkedListObjectArr();
            sqls.add(SqlMapping.get("goblin_nft_order.update.refund"));
            sqls.add(SqlMapping.get("goblin_nft_order_refund.refund"));
            sqlsOrder.add(new Object[]{
                    nftOrder.getStatus(), now,
                    orderId, now, now
            });
            sqlsBackOrder.add(new Object[]{
                    nftOrderRefundVo.getStatus(), now, now, nftOrderRefundVo.getOrderRefundId(), now, now
            });
            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_NFT_ORDER.getKey(),
                    SqlMapping.gets(sqls, sqlsOrder, sqlsBackOrder));
            //减积分
            goblinOrderUtils.desTask(nftOrder.getUserId(), nftOrderRefundVo.getPrice());
        } else if (0 == status) {
            log.info("NftRefundCallback: 支付平台返回状态为失败，编号{}", refundCallbackParam.getOrderRefundCode());
            return "fail";
        }
        return "success";
    }
}
