package com.izouma.nineth.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.huifu.adapay.core.exception.BaseAdaPayException;
import com.huifu.adapay.model.AdapayCommon;
import com.huifu.adapay.model.Payment;
import com.izouma.nineth.config.*;
import com.izouma.nineth.domain.Collection;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.event.OrderNotifyEvent;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.utils.JpaUtils;
import com.izouma.nineth.utils.SecurityUtils;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Page;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

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

@Slf4j
@Service
public class MintOrderService {

    private final MintOrderRepo                 mintOrderRepo;
    private final UserRepo                      userRepo;
    private final AssetService                  assetService;
    private final AssetRepo                     assetRepo;
    private final MintActivityRepo              mintActivityRepo;
    private final UserAddressRepo               userAddressRepo;
    private final GeneralProperties             generalProperties;
    private final Environment                   env;
    private final AdapayProperties              adapayProperties;
    private final SnowflakeIdWorker             snowflakeIdWorker;
    private final WxPayProperties               wxPayProperties;
    private final WxPayService                  wxPayService;
    private final MintMaterialRepo              mintMaterialRepo;
    private final MintActivityService           mintActivityService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final RocketMQTemplate              rocketMQTemplate;
    private final CollectionRepo                collectionRepo;
    private final OrderRepo                     orderRepo;
    private final ShowCollectionRepo            showCollectionRepo;
    private final AirDropService                airDropService;
    private final OrderPayService               orderPayService;

    public MintOrderService(MintOrderRepo mintOrderRepo,
                            UserRepo userRepo,
                            AssetService assetService,
                            AssetRepo assetRepo,
                            MintActivityRepo mintActivityRepo,
                            UserAddressRepo userAddressRepo,
                            GeneralProperties generalProperties,
                            Environment env,
                            AdapayProperties adapayProperties,
                            SnowflakeIdWorker snowflakeIdWorker,
                            WxPayProperties wxPayProperties,
                            WxPayService wxPayService,
                            MintMaterialRepo mintMaterialRepo,
                            MintActivityService mintActivityService,
                            RedisTemplate<String, Object> redisTemplate,
                            RocketMQTemplate rocketMQTemplate,
                            CollectionRepo collectionRepo,
                            OrderRepo orderRepo,
                            ShowCollectionRepo showCollectionRepo,
                            AirDropService airDropService,
                            @Lazy OrderPayService orderPayService) {
        this.mintOrderRepo = mintOrderRepo;
        this.userRepo = userRepo;
        this.assetService = assetService;
        this.assetRepo = assetRepo;
        this.mintActivityRepo = mintActivityRepo;
        this.userAddressRepo = userAddressRepo;
        this.generalProperties = generalProperties;
        this.env = env;
        this.adapayProperties = adapayProperties;
        this.snowflakeIdWorker = snowflakeIdWorker;
        this.wxPayProperties = wxPayProperties;
        this.wxPayService = wxPayService;
        this.mintMaterialRepo = mintMaterialRepo;
        this.mintActivityService = mintActivityService;
        this.redisTemplate = redisTemplate;
        this.rocketMQTemplate = rocketMQTemplate;
        this.collectionRepo = collectionRepo;
        this.orderRepo = orderRepo;
        this.showCollectionRepo = showCollectionRepo;
        this.airDropService = airDropService;
        this.orderPayService = orderPayService;
    }

    public Page<MintOrder> all(PageQuery pageQuery) {
        return mintOrderRepo.findAll(JpaUtils.toSpecification(pageQuery, MintOrder.class), JpaUtils.toPageRequest(pageQuery));
    }

    @Transactional
    public void create(Long userId, List<Long> assetIds) {
        User user = userRepo.findByIdAndDelFalse(userId).orElseThrow(new BusinessException("用户不存在"));
        User blackHole = userRepo.findByIdAndDelFalse(1435297L).orElseThrow(new BusinessException("无法铸造"));
        if (assetIds.size() != 3) {
            throw new BusinessException("数量不正确，请重新选择");
        }
        List<Asset> assets = assetRepo.findAllByIdInAndUserId(assetIds, userId);
        assets = assets.stream()
                .filter(asset -> asset.getName().contains("尼尔斯") && AssetStatus.NORMAL.equals(asset.getStatus()))
                .collect(Collectors.toList());
        if (assets.size() != 3) {
            throw new BusinessException("有藏品不符合，请重新选择");
        }


        // 铸造订单
        MintOrder order = mintOrderRepo.save(MintOrder.builder()
                .userId(userId)
                .phone(user.getPhone())
                .consume(true)
                .status(MintOrderStatus.AIR_DROP)
                .build());

        // 铸造资产
        List<MintMaterial> materials = assets.stream().map(asset -> {
            MintMaterial material = new MintMaterial();
            material.setAssetId(asset.getId());
            material.setCollectionId(asset.getCollectionId());
            material.setName(asset.getName());
            material.setNumber(asset.getNumber());
            material.setPic(asset.getPic());
            material.setCategory(asset.getCategory());
            material.setOrderId(order.getId());
            return material;
        }).collect(Collectors.toList());

        mintMaterialRepo.saveAll(materials);
        // 改为转赠
        assets.forEach(asset -> assetService.transfer(asset, asset.getPrice(), blackHole, TransferReason.GIFT, null));

    }

    /**
     * 订单
     *
     * @param id 编号
     */
    public void finish(Long id) {
        MintOrder mintOrder = mintOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
        mintOrder.setStatus(MintOrderStatus.FINISH);
        mintOrderRepo.save(mintOrder);
    }

    /**
     * 发货
     *
     * @param id        编号
     * @param courierId 快递单号
     */
    public void dispatch(Long id, String courierId) {
        MintOrder mintOrder = mintOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
        mintOrder.setStatus(MintOrderStatus.RECEIVE);
        mintOrder.setCourierId(courierId);
        mintOrderRepo.save(mintOrder);
    }

    /**
     * @param user           用户
     * @param assetId        资产
     * @param mintActivityId 铸造活动
     * @param addressId      地址
     */
//    @Transactional
    public MintOrder create(User user, List<Long> assetId, Long mintActivityId, Long addressId) {
        // 参加的活动
        MintActivity mintActivity = mintActivityRepo.findByIdAndDelFalse(mintActivityId)
                .orElseThrow(new BusinessException("无此铸造活动"));

        if (mintActivity.isScheduleSale()) {
            if (mintActivity.getStartTime().isAfter(LocalDateTime.now())) {
                throw new BusinessException("当前还未开售");
            }
        }
        if (!mintActivity.isOnShelf()) {
            throw new BusinessException("活动已下架");
        }

        UserAddress userAddress = null;
        if (addressId != null) {
            userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
        }

        int stock = Optional.ofNullable(mintActivityService.decreaseStock(mintActivityId, 1))
                .map(Math::toIntExact)
                .orElseThrow(new BusinessException("很遗憾，铸造活动已无库存"));
        try {
            if (stock < 0) {
                throw new BusinessException("铸造活动已无库存");
            }

            if (mintActivity.getNum() > 0) {
                if (assetId.size() != mintActivity.getNum()) {
                    throw new BusinessException("数量不正确，请重新选择");
                }
            }


            List<Asset> assets = assetRepo.findAllByIdInAndUserId(assetId, user.getId());
//            if (assets.stream().anyMatch(a -> a.isPublicShow() || a.isConsignment())) {
//                throw new BusinessException("请先下架所选藏品");
//            }

            if (!mintActivity.isAudit()) {
                if (assets.stream().anyMatch(a -> a.getStatus() != AssetStatus.NORMAL)) {
                    throw new BusinessException("所选藏品不符和规则，请重新选择");
                }
                if (!mintActivityService.matchRule(new ArrayList<>(assets), mintActivity.getRule())) {
                    throw new BusinessException("所选藏品不符和规则，请重新选择");
                }
            } else {
                // 资产产品是否符合铸造活动的名称
                assets = assets.stream()
                        .filter(asset -> asset.getName()
                                .contains(mintActivity.getCollectionName()) && AssetStatus.NORMAL.equals(asset.getStatus()))
                        .collect(Collectors.toList());
                if (mintActivity.getNum() > 0 && (assets.size() != mintActivity.getNum())) {
                    throw new BusinessException("有藏品不符合，请重新选择");
                }
            }


            Map<Long, Long> privilegeIds = new HashMap<>();
            // 铸造特权
            if (!mintActivity.isConsume()) {
                assets.forEach(asset -> {
                    List<Privilege> privileges = asset.getPrivileges()
                            .stream()
                            .filter(p -> p.getName().equals("铸造"))
                            .collect(Collectors.toList());
                    if (privileges.size() == 0) {
                        throw new BusinessException("无铸造特权");
                    } else {
                        boolean flag = false;
                        for (Privilege privilege : privileges) {
                            // 打开多次 或者 可打开一次但未使用
                            if (!privilege.isOnce() || (privilege.isOnce() && !privilege
                                    .isOpened())) {
                                flag = true;
                                privilegeIds.put(asset.getId(), privilege.getId());
                                break;
                            }
                        }
                        if (!flag) {
                            throw new BusinessException("铸造特权已使用");
                        }
                    }
                });
                assets.forEach(asset -> {
                    asset.getPrivileges()
                            .stream()
                            .filter(p -> p.getId().equals(privilegeIds.get(asset.getId())))
                            .forEach(p -> {
                                p.setOpened(true);
                                p.setOpenTime(LocalDateTime.now());
                                p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
                            });
                    assetRepo.save(asset);
                });
            } else {
                // 转让的用户
                userRepo.findByIdAndDelFalse(1435297L).orElseThrow(new BusinessException("无法铸造"));

                // 消耗改为转赠
                assets.forEach(asset -> {
                    if (!asset.getUserId().equals(user.getId())) {
                        throw new BusinessException("此藏品不属于你");
                    }
                    // 取消公开展示
                    if (asset.isPublicShow()) {
                        if (asset.isConsignment()) {
                            if (asset.getPublicCollectionId() != null) {
                                List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
                                if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
                                    throw new BusinessException("已有订单不可取消寄售");
                                }
                            }
//                            asset.setConsignment(false);
                        }
                        Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
                                .orElseThrow(new BusinessException("无展示记录"));
                        collectionRepo.delete(collection);
                        // 如果展厅有此藏品
                        showCollectionRepo.deleteAllByCollectionId(asset.getPublicCollectionId());
                    }

                });

                // 统一处理
                assets.forEach(asset -> {
                    if (asset.isPublicShow()) {
                        asset.setPublicShow(false);
                        asset.setPublicCollectionId(null);
                        if (asset.isConsignment()) {
                            asset.setConsignment(false);
                        }
                    }
                    asset.setStatus(AssetStatus.MINTING);
                    assetRepo.save(asset);
                });
            }


            // 铸造订单
            MintOrder mintOrder = mintOrderRepo.save(MintOrder.builder()
                    .userId(user.getId())
                    .phone(user.getPhone())
                    .consume(mintActivity.isConsume())
                    .status(MintOrderStatus.NOT_PAID)
                    .airDrop(mintActivity.isAirDrop())
                    .gasPrice(mintActivity.getGasPrice())
                    .mintActivityId(mintActivityId)
                    .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
                    .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
                    .address(Optional.ofNullable(userAddress).map(UserAddress::getDetail).orElse(null))
                    .build());

            // 铸造资产
            List<MintMaterial> materials = assets.stream().map(asset -> {
                MintMaterial material = new MintMaterial();
                material.setAssetId(asset.getId());
                material.setCollectionId(asset.getCollectionId());
                material.setName(asset.getName());
                material.setPrivilegeId(privilegeIds.get(asset.getId()));
                material.setNumber(asset.getNumber());
                material.setPic(asset.getPic());
                material.setCategory(asset.getCategory());
                material.setOrderId(mintOrder.getId());
                return material;
            }).collect(Collectors.toList());

            mintMaterialRepo.saveAll(materials);

            if (mintOrder.getGasPrice().compareTo(BigDecimal.ZERO) == 0) {
                this.mintNotify(mintOrder.getId(), PayMethod.WEIXIN, null);
            }
            return mintOrder;
        } catch (Exception e) {
            // 错了加库存
            mintActivityService.increaseStock(mintActivityId, 1);
            throw e;
        }
    }

    public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException {
        MintOrder order = mintOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }

        WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
        request.setBody("铸造GAS费");
        request.setOutTradeNo(String.valueOf(new SnowflakeIdWorker(1, 1).nextId()));
        request.setTotalFee(order.getGasPrice().multiply(BigDecimal.valueOf(100)).intValue());
        if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
            // 测试环境设为1分
//                    request.setTotalFee(1);
        }
        request.setSpbillCreateIp("180.102.110.170");
        request.setNotifyUrl(wxPayProperties.getNotifyUrl());
        request.setTradeType(tradeType);
        request.setOpenid(openId);
        request.setSignType("MD5");
        JSONObject body = new JSONObject();
        body.put("action", "payMintOrder");
        body.put("userId", order.getUserId());
        body.put("orderId", order.getId());
        request.setAttach(body.toJSONString());

        if (WxPayConstants.TradeType.MWEB.equals(tradeType)) {
            WxPayMwebOrderResult result = wxPayService.createOrder(request);
            return result.getMwebUrl() + "&redirect_url=" + new URLCodec().encode(wxPayProperties.getReturnUrl());
        } else if (WxPayConstants.TradeType.JSAPI.equals(tradeType)) {
            return wxPayService.<WxPayMpOrderResult>createOrder(request);
        }
        throw new BusinessException("不支持此付款方式");
    }

    public Object payAdapay(Long id, String payChannel, String openId) throws BaseAdaPayException {
        List<String> aliChannels = Arrays.asList("alipay", "alipay_qr", "alipay_wap");
        List<String> wxChannels = Arrays.asList("wx_pub", "wx_lite");
        if (!aliChannels.contains(payChannel) && !wxChannels.contains(payChannel)) {
            throw new BusinessException("不支持此渠道");
        }
        MintOrder order = mintOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }

        Map<String, Object> paymentParams = new HashMap<>();
        paymentParams.put("order_no", String.valueOf(snowflakeIdWorker.nextId()));
        paymentParams.put("pay_amt", order.getGasPrice().setScale(2, RoundingMode.HALF_UP).toPlainString());
        paymentParams.put("app_id", adapayProperties.getAppId());
        paymentParams.put("pay_channel", payChannel);
        paymentParams.put("goods_title", "铸造GAS费");
        paymentParams.put("goods_desc", "铸造GAS费");
        paymentParams.put("time_expire", DateTimeFormatter.ofPattern("yyyyMMddHHmmss")
                .format(LocalDateTime.now().plusMinutes(5)));
        paymentParams.put("notify_url", adapayProperties.getNotifyUrl() + "/mintOrder/" + order.getId());

        Map<String, Object> expend = new HashMap<>();
        paymentParams.put("expend", expend);
        if ("wx_pub".equals(payChannel)) {
            if (StringUtils.isBlank(openId)) {
                throw new BusinessException("缺少openId");
            }
            expend.put("open_id", openId);
            expend.put("limit_pay", "1");
        }

        Map<String, Object> response;
        if ("wx_lite".equals(payChannel)) {
            paymentParams.put("adapay_func_code", "wxpay.createOrder");
            paymentParams.put("callback_url", generalProperties.getHost() + "/9th/orders");
            response = AdapayCommon.requestAdapayUits(paymentParams);
            log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
        } else {
            response = Payment.create(paymentParams);
            log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
            AdapayService.checkSuccess(response);

            // adapay 同步
            BoundSetOperations<String, Object> ops = redisTemplate.boundSetOps(RedisKeys.ACTIVITY_PAY_RECORD + order.getId());
            ops.add(MapUtils.getString(response, "id"));
            ops.expire(7, TimeUnit.DAYS);
        }

        switch (payChannel) {
            case "alipay_wap":
            case "alipay":
                return MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info");
            case "alipay_qr":
                return MapUtils.getString(MapUtils.getMap(response, "expend"), "qrcode_url");
            case "wx_pub":
                JSONObject payParams = JSON.parseObject(MapUtils.getString(MapUtils.getMap(response, "expend"), "pay_info"));
                payParams.put("timestamp", payParams.get("timeStamp"));
                payParams.remove("timeStamp");
                return payParams;
            default:
                return MapUtils.getMap(response, "expend");
        }
    }

    @Transactional
    public void mintNotify(Long orderId, PayMethod payMethod, String transactionId) {
        log.info("铸造订单回调 orderId: {}, payMethod: {}, transactionId: {}", orderId, payMethod, transactionId);

        if (!getOrderLock(orderId)) {
            log.info("铸造订单回调失败 orderId: {} redis锁定, 重新发送到队列", orderId);
            rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
                    new OrderNotifyEvent(orderId, payMethod, transactionId, System.currentTimeMillis(),
                            OrderNotifyEvent.TYPE_MINT_ORDER));
            return;
        }

        try {
            MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
            if (order.getStatus() == MintOrderStatus.NOT_PAID) {
                MintActivity activity = mintActivityRepo.findById(order.getMintActivityId())
                        .orElseThrow(new BusinessException("无活动"));
                //活动是否需要审核
                if (activity.isAudit()) {
                    order.setStatus(MintOrderStatus.PENDING);
                    order.setTransactionId(transactionId);
                    order.setPayMethod(payMethod);
                    order.setPayAt(LocalDateTime.now());
                    mintOrderRepo.save(order);
                } else {
                    this.notify(order, payMethod, transactionId, true);
                }
            }
        } catch (Exception e) {
            log.info("铸造订单回调出错 orderId={}", orderId, e);
        }

        releaseOrderLock(orderId);
    }

    public void orderAudit(Long orderId, boolean pass) {
        MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (!MintOrderStatus.PENDING.equals(order.getStatus())) {
            throw new BusinessException("状态错误");
        }

        if (pass) {
            this.notify(order, null, null, false);
            return;
        }
        this.cancel(order, true);
    }


    @Transactional
    public void notify(MintOrder mintOrder, PayMethod payMethod, String transactionId, boolean saveTransactionId) {
        Long orderId = mintOrder.getId();

        try {
//            MintOrder mintOrder = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
            List<MintMaterial> materials = mintMaterialRepo.findAllByOrderIdAndDelFalse(orderId);
            List<Asset> assets = assetRepo.findAllById(materials
                    .stream()
                    .map(MintMaterial::getAssetId)
                    .collect(Collectors.toList()));

            if (saveTransactionId) {
                mintOrder.setPayMethod(payMethod);
                mintOrder.setTransactionId(transactionId);
                mintOrder.setPayAt(LocalDateTime.now());
            }

            if (mintOrder.isAirDrop()) {
                mintOrder.setStatus(MintOrderStatus.AIR_DROP);
                MintActivity mintActivity = mintActivityRepo.findById(mintOrder.getMintActivityId()).orElse(null);
                if (ObjectUtils.isNotEmpty(mintActivity) && mintActivity.isAutoDrop()) {
                    User user = userRepo.findById(mintOrder.getUserId()).orElseThrow(new BusinessException("无用户"));
                    airDropService.create(AirDrop.builder()
                            .name("铸造活动[" + mintActivity.getName() + "]空投")
                            .remark(mintOrder.getId().toString())
                            .type(AirDropType.asset)
                            .userIds(Collections.singletonList(mintOrder.getUserId()))
                            .collectionId(mintActivity.getAirDropCollectionId())
                            .targets(Collections.singletonList(new DropTarget(user.getId(), user.getPhone(), user.getNickname(), 1)))
                            .auto(true)
                            .build());
                    mintOrder.setStatus(MintOrderStatus.FINISH);
                    mintOrderRepo.save(mintOrder);
                }
            } else {
                mintOrder.setStatus(MintOrderStatus.DELIVERY);
            }

            if (mintOrder.isConsume()) {
                User newOwner = userRepo.findByIdAndDelFalse(1435297L).orElseThrow(new BusinessException("无法铸造"));
                assets.forEach(asset -> assetService.transfer(asset, asset.getPrice(), newOwner, TransferReason.GIFT, null));
            }
            mintOrderRepo.save(mintOrder);

        } catch (Exception e) {
            if (e instanceof BusinessException) {
                log.error("铸造订单回调出错 orderId: {} {}", orderId, e.getMessage());
            } else {
                log.error("铸造订单回调出错 orderId: " + orderId, e);
            }
        }
    }

    public void cancel(MintOrder order, boolean refund) {
        if (!getOrderLock(order.getId())) {
            log.error("订单取消失败 {}, redis锁了", order.getId());
            return;
        }

        try {
            // 审核中或未支付订单可以取消
            if (!(MintOrderStatus.PENDING == order.getStatus() || MintOrderStatus.NOT_PAID == order.getStatus() || refund)) {
                throw new BusinessException("此状态无法取消");
            }

            List<MintMaterial> materials = mintMaterialRepo.findAllByOrderIdAndDelFalse(order.getId());
            List<Asset> assets = assetRepo.findAllById(materials.stream()
                    .map(MintMaterial::getAssetId)
                    .collect(Collectors.toList()));

            if (order.isConsume()) {
                assets.forEach(asset -> {
                    asset.setStatus(AssetStatus.NORMAL);
                    assetRepo.save(asset);
                });
            } else {
                Map<Long, Long> privilegeIds = materials.stream()
                        .collect(Collectors.toMap(MintMaterial::getAssetId, MintMaterial::getPrivilegeId));
                assets.forEach(asset -> {
                    asset.getPrivileges()
                            .stream()
                            .filter(p -> p.getId().equals(privilegeIds.get(asset.getId())))
                            .forEach(p -> {
                                p.setOpened(false);
                                p.setOpenTime(null);
                                p.setOpenedBy(null);
                            });
                    assetRepo.save(asset);
                });
            }

            log.info("set normal mintOrder {}", order.getId());

            order.setStatus(MintOrderStatus.CANCELLED);
            order.setCancelTime(LocalDateTime.now());
            mintOrderRepo.save(order);

            // 加库存
            mintActivityService.increaseStock(order.getMintActivityId(), 1);
            log.info("取消订单{}", order.getId());

            if (order.getPayMethod() != null && StringUtils.isNotBlank(order.getTransactionId())) {
                log.info("退款铸造订单{}", order.getId());
                PayMethod payMethod = order.getPayMethod();
                if (PayMethod.ALIPAY == payMethod) {
                    if (StringUtils.length(order.getTransactionId()) == 28) {
                        payMethod = PayMethod.HMPAY;
                    } else if (StringUtils.length(order.getTransactionId()) == 30) {
                        payMethod = PayMethod.SANDPAY;
                    }
                }
                try {
                    switch (payMethod) {
                        case HMPAY:
                            orderPayService.refund(order.getId().toString(), order.getTransactionId(),
                                    order.getGasPrice(), Constants.PayChannel.HM);
                            log.info("退款成功");
                            break;
                        case SANDPAY:
                            orderPayService.refund(order.getId().toString(), order.getTransactionId(),
                                    order.getGasPrice(), Constants.PayChannel.SAND);
                            log.info("退款成功");
                            break;
                        case PAYEASE:
                            orderPayService.refund(order.getTransactionId(), order.getTransactionId(),
                                    order.getGasPrice(), Constants.PayChannel.PE);
                    }
                } catch (Exception e) {
                    log.error("铸造订单退款失败 {} ", order.getId(), e);
                }
            }
        } catch (Exception e) {
            log.error("订单取消错误 orderId: " + order.getId(), e);
        }
        releaseOrderLock(order.getId());
    }

    public boolean getOrderLock(Long orderId) {
        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.MINT_ORDER_LOCK + orderId);
        Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.DAYS);
        return Boolean.TRUE.equals(flag);
    }

    public void releaseOrderLock(Long orderId) {
        redisTemplate.delete(RedisKeys.MINT_ORDER_LOCK + orderId);
    }
}
