Browse Source

Merge branch 'master' of http://git.izouma.com/xiongzhu/raex_back into dev-meta

sunkean 3 years ago
parent
commit
dbff98a028

+ 6 - 0
src/main/java/com/izouma/nineth/domain/Company.java

@@ -27,4 +27,10 @@ public class Company extends BaseEntity {
     private String bgImg;
 
     private String bgColor;
+
+    private boolean mintActivity;
+
+    private boolean airdrop;
+
+
 }

+ 12 - 3
src/main/java/com/izouma/nineth/domain/UserDetail.java

@@ -1,14 +1,11 @@
 package com.izouma.nineth.domain;
 
-import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
 import javax.persistence.Id;
 
 @Data
@@ -21,6 +18,15 @@ public class UserDetail extends BaseEntityNoID {
     @Id
     private Long userId;
 
+    @ApiModelProperty("昵称")
+    private String nickname;
+
+    @ApiModelProperty("头像")
+    private String pic;
+
+    @ApiModelProperty("背景图")
+    private String bgpic;
+
     @ApiModelProperty("生日")
     private String birthday;
 
@@ -72,4 +78,7 @@ public class UserDetail extends BaseEntityNoID {
     @ApiModelProperty("个人热力值")
     private Long personalheatCount;
 
+    @ApiModelProperty("是否点亮")
+    private boolean light;
+
 }

+ 19 - 0
src/main/java/com/izouma/nineth/service/AirDropService.java

@@ -167,6 +167,25 @@ public class AirDropService {
         return airDropRepo.save(record);
     }
 
+    public void testCreate(AirDrop record) {
+        boolean parallel = sysConfigService.getBoolean("parallel_airdrop");
+        if (record.getTargets().isEmpty()) throw new BusinessException("空投对象不能为空");
+        if (record.getTargets().stream().mapToInt(DropTarget::getNum).sum() > 300)
+            throw new BusinessException("空投数量不能超过300");
+        if (AirDropType.coupon == record.getType()) {
+            Coupon coupon = couponRepo.findById(record.getCouponId()).orElseThrow(new BusinessException("兑换券不存在"));
+        } else {
+            Collection collection = collectionRepo.findById(record.getCollectionId())
+                    .orElseThrow(new BusinessException("藏品不存在"));
+            if (collection.isSalable()) {
+                throw new BusinessException("请先设置藏品为不可购买:" + collection.getName());
+            }
+            if (!record.isIgnoreStockCheck() && collection.getStock() < record.getTargets().stream().mapToInt(DropTarget::getNum).sum()) {
+                throw new BusinessException("藏品库存不足:" + collection.getName());
+            }
+        }
+    }
+
     @Async
     public void asyncDrop(Long collectionId, Long userId, int num, LocalDateTime time) {
         drop(collectionId, userId, num, time);

+ 13 - 4
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -26,7 +26,10 @@ import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.beans.BeanUtils;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.core.env.Environment;
-import org.springframework.data.domain.*;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.data.redis.core.BoundHashOperations;
 import org.springframework.data.redis.core.BoundValueOperations;
@@ -36,14 +39,14 @@ import org.springframework.stereotype.Service;
 import org.springframework.web.bind.annotation.RequestParam;
 
 import javax.annotation.PostConstruct;
-import javax.persistence.criteria.*;
+import javax.persistence.criteria.Join;
+import javax.persistence.criteria.Predicate;
 import javax.transaction.Transactional;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -111,7 +114,13 @@ public class CollectionService {
                         });
             }
         }
-
+        if (query.containsKey("distinct")) {
+            query.remove("distinct");
+            specification = specification.and((Specification<Collection>) (root, criteriaQuery, criteriaBuilder) -> {
+                criteriaQuery.groupBy(root.get("name"));
+                return criteriaBuilder.and();
+            });
+        }
         if (query.containsKey("rarityLabel")) {
             String rarityLabel = String.valueOf(query.get("rarityLabel"));
             query.remove("rarityLabel");

+ 188 - 47
src/main/java/com/izouma/nineth/service/MintOrderService.java

@@ -122,50 +122,6 @@ public class MintOrderService {
         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(Constants.BLACK_HOLE_USER_ID)
-                .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));
-
-    }
-
     /**
      * 订单
      *
@@ -304,9 +260,9 @@ public class MintOrderService {
                             }
 //                            asset.setConsignment(false);
                         }
-                        Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
-                                .orElseThrow(new BusinessException("无展示记录"));
-                        collectionRepo.delete(collection);
+                        collectionRepo.findById(asset.getPublicCollectionId())
+                                .ifPresent(collectionRepo::delete);
+
                         // 如果展厅有此藏品
                         showCollectionRepo.deleteAllByCollectionId(asset.getPublicCollectionId());
                     }
@@ -370,6 +326,191 @@ public class MintOrderService {
         }
     }
 
+    @RedisLock(value = "'mintOrderLock'+#{userId}", expire = 30, message = "操作频率过高,请稍后再试")
+    public String testCreate(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("很遗憾,铸造活动已无库存"));
+        Exception ex = null;
+        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());
+            assets.stream().forEach(asset -> {
+                if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
+                    throw new BusinessException("所选藏品:" + asset.getName() +
+                            (asset.getNumber() == null ? "" : ("#" + asset.getNumber())) + " 已锁定");
+                }
+            });
+//            if (assets.stream().anyMatch(a -> a.isPublicShow() || a.isConsignment())) {
+//                throw new BusinessException("请先下架所选藏品");
+//            }
+            if (assets.stream().anyMatch(a -> a.getStatus() != AssetStatus.NORMAL)) {
+                throw new BusinessException("所选藏品不符和规则,请重新选择");
+            }
+            if (!mintActivity.isAudit()) {
+                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(a -> {
+                    if (a.getPrivileges().stream().noneMatch(p ->
+                            "铸造".equals(p.getName())
+                                    && (!p.isOnce() || !p.isOpened()))) {
+                        throw new BusinessException(a.getName() + (a.getNumber() == null ? "" : (" #" + a.getNumber()
+                                .toString())) + " 无铸造特权或特权已使用");
+                    }
+                });
+//                assets.forEach(asset -> {
+//                    asset.getPrivileges()
+//                            .stream()
+//                            .filter(p -> !p.isOnce() || !p.isOpened())
+//                            .findFirst()
+//                            .ifPresent(p -> {
+//                                privilegeIds.put(asset.getId(), p.getId());
+//                                p.setOpened(true);
+//                                p.setOpenTime(LocalDateTime.now());
+//                                p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
+//                            });
+//                    assetRepo.save(asset);
+//                });
+            } else {
+                // 转让的用户
+                userRepo.findByIdAndDelFalse(Constants.BLACK_HOLE_USER_ID).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);
+                        }
+//                        collectionRepo.findById(asset.getPublicCollectionId())
+//                                .ifPresent(collectionRepo::delete);
+//                        // 如果展厅有此藏品
+//                        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 = 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))
+                    .companyId(mintActivity.getCompanyId())
+                    .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());
+
+
+            airDropService.testCreate(AirDrop.builder()
+                    .name("铸造活动[" + mintActivity.getName() + "]空投")
+                    .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)
+                    .companyId(mintActivity.getCompanyId())
+                    .build());
+        } catch (Exception e) {
+            ex = e;
+            e.printStackTrace();
+        }
+        mintActivityService.increaseStock(mintActivityId, 1);
+        if (ex == null) {
+            return "测试通过";
+        } else {
+            if (StringUtils.isEmpty(ex.getMessage())) {
+                return ex.toString();
+            }
+            return ex.getMessage();
+        }
+    }
+
     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) {

+ 3 - 1
src/main/java/com/izouma/nineth/service/TagService.java

@@ -3,11 +3,13 @@ package com.izouma.nineth.service;
 import com.izouma.nineth.domain.Tag;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.repo.TagRepo;
+import com.izouma.nineth.utils.DateTimeUtils;
 import com.izouma.nineth.utils.JpaUtils;
 import lombok.AllArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDate;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -38,7 +40,7 @@ public class TagService {
 
     public Tag create(String name, String remark, boolean addForExist, String addMode, String pattern, Long companyId) {
         Tag tag = tagRepo.saveAndFlush(Tag.builder()
-                .name(name)
+                .name(DateTimeUtils.format(LocalDate.now(), "MM/dd") + ": " + name)
                 .remark(remark)
                 .companyId(companyId)
                 .build());

+ 803 - 0
src/main/java/com/izouma/nineth/service/TestMintOrderService.java

@@ -0,0 +1,803 @@
+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.annotations.RedisLock;
+import com.izouma.nineth.config.*;
+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 TestMintOrderService {
+
+    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 TestMintOrderService(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));
+    }
+
+    /**
+     * 订单
+     *
+     * @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
+    @RedisLock(value = "'mintOrderLock'+#{userId}", expire = 30, message = "操作频率过高,请稍后再试")
+    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());
+            assets.stream().forEach(asset -> {
+                if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
+                    throw new BusinessException("所选藏品:" + asset.getName() +
+                            (asset.getNumber() == null ? "" : ("#" + asset.getNumber())) + " 已锁定");
+                }
+            });
+//            if (assets.stream().anyMatch(a -> a.isPublicShow() || a.isConsignment())) {
+//                throw new BusinessException("请先下架所选藏品");
+//            }
+            if (assets.stream().anyMatch(a -> a.getStatus() != AssetStatus.NORMAL)) {
+                throw new BusinessException("所选藏品不符和规则,请重新选择");
+            }
+            if (!mintActivity.isAudit()) {
+                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(a -> {
+                    if (a.getPrivileges().stream().noneMatch(p ->
+                            "铸造".equals(p.getName())
+                                    && (!p.isOnce() || !p.isOpened()))) {
+                        throw new BusinessException(a.getName() + (a.getNumber() == null ? "" : (" #" + a.getNumber()
+                                .toString())) + " 无铸造特权或特权已使用");
+                    }
+                });
+                assets.forEach(asset -> {
+                    asset.getPrivileges()
+                            .stream()
+                            .filter(p -> !p.isOnce() || !p.isOpened())
+                            .findFirst()
+                            .ifPresent(p -> {
+                                privilegeIds.put(asset.getId(), p.getId());
+                                p.setOpened(true);
+                                p.setOpenTime(LocalDateTime.now());
+                                p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
+                            });
+                    assetRepo.save(asset);
+                });
+            } else {
+                // 转让的用户
+                userRepo.findByIdAndDelFalse(Constants.BLACK_HOLE_USER_ID).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);
+                        }
+                        collectionRepo.findById(asset.getPublicCollectionId())
+                                .ifPresent(collectionRepo::delete);
+
+                        // 如果展厅有此藏品
+                        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))
+                    .companyId(mintActivity.getCompanyId())
+                    .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;
+        }
+    }
+
+    @RedisLock(value = "'mintOrderLock'+#{userId}", expire = 30, message = "操作频率过高,请稍后再试")
+    public String testCreate(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("很遗憾,铸造活动已无库存"));
+        Exception ex = null;
+        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());
+            assets.stream().forEach(asset -> {
+                if (asset.getLockTo() != null && asset.getLockTo().isAfter(LocalDateTime.now())) {
+                    throw new BusinessException("所选藏品:" + asset.getName() +
+                            (asset.getNumber() == null ? "" : ("#" + asset.getNumber())) + " 已锁定");
+                }
+            });
+//            if (assets.stream().anyMatch(a -> a.isPublicShow() || a.isConsignment())) {
+//                throw new BusinessException("请先下架所选藏品");
+//            }
+            if (assets.stream().anyMatch(a -> a.getStatus() != AssetStatus.NORMAL)) {
+                throw new BusinessException("所选藏品不符和规则,请重新选择");
+            }
+            if (!mintActivity.isAudit()) {
+                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(a -> {
+                    if (a.getPrivileges().stream().noneMatch(p ->
+                            "铸造".equals(p.getName())
+                                    && (!p.isOnce() || !p.isOpened()))) {
+                        throw new BusinessException(a.getName() + (a.getNumber() == null ? "" : (" #" + a.getNumber()
+                                .toString())) + " 无铸造特权或特权已使用");
+                    }
+                });
+//                assets.forEach(asset -> {
+//                    asset.getPrivileges()
+//                            .stream()
+//                            .filter(p -> !p.isOnce() || !p.isOpened())
+//                            .findFirst()
+//                            .ifPresent(p -> {
+//                                privilegeIds.put(asset.getId(), p.getId());
+//                                p.setOpened(true);
+//                                p.setOpenTime(LocalDateTime.now());
+//                                p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
+//                            });
+//                    assetRepo.save(asset);
+//                });
+            } else {
+                // 转让的用户
+                userRepo.findByIdAndDelFalse(Constants.BLACK_HOLE_USER_ID).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);
+                        }
+//                        collectionRepo.findById(asset.getPublicCollectionId())
+//                                .ifPresent(collectionRepo::delete);
+//                        // 如果展厅有此藏品
+//                        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))
+                    .companyId(mintActivity.getCompanyId())
+                    .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);
+            }
+        } catch (Exception e) {
+            ex = e;
+            // 错了加库存
+            mintActivityService.increaseStock(mintActivityId, 1);
+        }
+        if (ex == null) {
+            mintActivityService.increaseStock(mintActivityId, 1);
+            return "测试通过";
+        } else {
+            return ex.getMessage();
+        }
+    }
+
+    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)
+                            .companyId(mintActivity.getCompanyId())
+                            .build());
+                    mintOrder.setStatus(MintOrderStatus.FINISH);
+                    mintOrderRepo.save(mintOrder);
+                }
+            } else {
+                mintOrder.setStatus(MintOrderStatus.DELIVERY);
+            }
+
+            if (mintOrder.isConsume()) {
+                User newOwner = userRepo.findByIdAndDelFalse(Constants.BLACK_HOLE_USER_ID)
+                        .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);
+    }
+}

+ 2 - 0
src/main/java/com/izouma/nineth/service/WithdrawApplyScheduleService.java

@@ -5,6 +5,7 @@ import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.utils.DateTimeUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
@@ -15,6 +16,7 @@ import java.util.concurrent.TimeUnit;
 @Service
 @AllArgsConstructor
 @Slf4j
+@ConditionalOnProperty(name = "general.workerId", havingValue = "1")
 public class WithdrawApplyScheduleService {
 
     private SysConfigService     sysConfigService;

+ 4 - 0
src/main/java/com/izouma/nineth/utils/DateTimeUtils.java

@@ -71,4 +71,8 @@ public class DateTimeUtils {
         }
         return true;
     }
+
+    public static void main(String[] args) {
+        System.out.println(isWorkDay(LocalDate.now().plusDays(2)));
+    }
 }

+ 7 - 0
src/main/java/com/izouma/nineth/web/MintOrderController.java

@@ -96,6 +96,13 @@ public class MintOrderController extends BaseController {
         return mintOrderService.create(SecurityUtils.getAuthenticatedUser(), assetIds, mintActivityId, addressId);
     }
 
+    @PostMapping("/testCreate")
+    public String testCreate(@RequestParam String assets, @RequestParam Long mintActivityId, Long addressId) {
+        LongArrayConverter lc = new LongArrayConverter();
+        List<Long> assetIds = lc.convertToEntityAttribute(assets);
+        return mintOrderService.testCreate(SecurityUtils.getAuthenticatedUser(), assetIds, mintActivityId, addressId);
+    }
+
     @PreAuthorize("hasAnyRole('ADMIN','SAAS')")
     @ApiOperation("导出")
     @PostMapping("/excelPhone")

+ 62 - 11
src/main/java/com/izouma/nineth/web/UserDetailController.java

@@ -7,8 +7,6 @@ import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.UserDetailRepo;
 import com.izouma.nineth.service.ContentAuditService;
 import com.izouma.nineth.service.UserDetailService;
-import com.izouma.nineth.utils.ObjUtils;
-import com.izouma.nineth.utils.SecurityUtils;
 import com.izouma.nineth.utils.excel.ExcelUtils;
 import lombok.AllArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
@@ -31,23 +29,26 @@ public class UserDetailController extends BaseController {
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public UserDetail save(@RequestBody UserDetail record) {
-        Long currentUserId = SecurityUtils.getAuthenticatedUser().getId();
-        if (!Objects.equals(currentUserId, record.getUserId())) {
-            throw new BusinessException("当前修改用户与登录用户不一致");
-        }
+
         if (StringUtils.isNotBlank(record.getAutograph())) {
             if (!contentAuditService.auditText(record.getAutograph())) {
                 throw new BusinessException("简介包含非法内容");
             }
         }
-        if (record.getUserId() != null) {
-            UserDetail orig = userDetailRepo.findById(record.getUserId()).orElseThrow(new BusinessException("无记录"));
-            ObjUtils.merge(orig, record);
-            return userDetailRepo.save(orig);
-        }
         return userDetailRepo.save(record);
     }
 
+    @PostMapping("/meta/save")
+    public MetaRestResult<UserDetail> metaSave(@RequestBody UserDetail record) {
+        UserDetail userDetail;
+        try {
+            userDetail = save(record);
+        } catch (Exception e) {
+            return MetaRestResult.returnError(e.getMessage());
+        }
+        return MetaRestResult.returnSuccess(userDetail);
+    }
+
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/all")
     public Page<UserDetail> all(@RequestBody PageQuery pageQuery) {
@@ -67,6 +68,56 @@ public class UserDetailController extends BaseController {
         return userDetail;
     }
 
+    @GetMapping("/light/{userId}")
+    public boolean light(@PathVariable Long userId) {
+        UserDetail userDetail = userDetailRepo.findById(userId).orElse(null);
+        if (Objects.isNull(userDetail)) {
+            return false;
+        }
+        if(StringUtils.isNotBlank(userDetail.getAutograph())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getBgpic())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getBirthday())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getBlood())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getCompany())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getConstellation())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getHome())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getMail())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getNickname())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getOccupation())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getPic())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getSchool())) {
+            return true;
+        }
+        if(StringUtils.isNotBlank(userDetail.getSex())) {
+            return true;
+        }
+        return false;
+    }
+
+
+
     @GetMapping("/{userId}/metaQuery")
     public MetaRestResult<UserDetail> metaQuery(@PathVariable Long userId) {
         return MetaRestResult.returnSuccess(get(userId));

+ 1 - 1
src/main/resources/application.yaml

@@ -157,7 +157,7 @@ aliyun:
 general:
   host: https://test.raex.vip
   contract-name: raex_new
-  name: OASISMETA
+  name: 绿洲宇宙
   org: 华储艺术品中心(深圳)有限公司
   short-name: 华储
   create-order-group: create-order-group-dev

BIN
src/main/resources/static/img/bg.jpg


+ 2 - 2
src/main/resources/static/pc.html

@@ -29,7 +29,7 @@
       }
 
       .phone-box {
-        width: 50%;
+        padding-right: 7vw;
         display: flex;
         align-items: center;
         justify-content: center;
@@ -80,7 +80,7 @@
     </style>
   </head>
 
-  <body style="background-image: url(img/bg.png)">
+  <body style="background-image: url(img/bg.jpg)">
     <div class="phone-box">
       <div class="phone">
         <img src="img/box.png" alt="" class="bg" />

+ 5 - 9
src/main/resources/templates/Privacy.ftlh

@@ -63,7 +63,7 @@
       您可以拒绝提供,如果拒绝提供您将可能无法获得相关服务,但不影响其他功能与服务的正常使用。
     </p>
     <h6>2、资讯浏览和咨询服务</h6>
-    <p class="text-content">(1)为保障推荐内容的质量并向您推荐可能感兴趣的信息或为您提供更适合您的顾问咨询服务,我们可能会收集必要的日志信息。</p>
+    <p class="text-content">(1)为保障内容的质量并向您提供更好的服务,我们可能会收集必要的日志信息。</p>
     <p class="text-content">(2)用于信息展示和咨询服务的日志信息包括:
       您操作、使用的行为信息:点击、关注、收藏、搜索、浏览、分享
       ,您主动提供的信息:反馈<span class="strong-text">GPS信息、WLAN接入点</span> 、蓝牙和基站等传感器信息</p>
@@ -82,10 +82,7 @@
     <p class="text-content">
       (1)您主动关注您感兴趣的账号、话题并与之进行互动,进行浏览、评论、收藏、点赞或分享内容时,我们会收集您关注的账号,并向您展示您关注账号发布内容。
     </p>
-    <p class="text-content">
-      <span class="strong-text">(2)您知悉并同意,对于您在使用产品及/或服务的过程中提供的您的联系方式(例如:联系电话),我们在运营中可能会向其中的一种或多种发送多类通知,用于用户消息告知、身份验证、安全验证、用户使用体验调研等用途;此外,我们也可能会向在前述过程中收集的手机号码通过短信、电话的方式,为您提供您可能感兴趣的服务、功能或活动等商业性信息的用途,但请您放心,如您不愿接受这些信息,您可以直接与我们联系进行退订。</span>
-    </p>
-    <p class="text-content">(3)为了实现口令分享功能,我们需要访问您的剪切板,读取其中包含的口令、分享码、链接,以实现跳转、分享、活动联动等功能或服务。</p>
+    <p class="text-content">(2)为了实现口令分享功能,我们需要访问您的剪切板,读取其中包含的口令、分享码、链接,以实现跳转、分享、活动联动等功能或服务。</p>
     <h6>5、开展营销活动</h6>
     <p class="text-content">
       当您选择参加${properties.name}举办的有关营销活动时,根据活动可能需要您提供<span class="strong-text">姓名、通信地址、联系方式、身份证号、银行账号信息。这些信息可能包括个人敏感信息,如果您拒绝提供这些信息,</span>可能影响您参加相关活动或领取奖品,但不会影响其他功能。
@@ -126,9 +123,8 @@
     <p class="text-content">我们可能会设置认证与保障安全性的cookie 或匿名标识符,使我们确认您是否安全登录服务,或者是否遇到盗用、欺诈等不法行为。这些技术还会帮助我们改进服务效率,提升登录和响应速度。</p>
     <h5>(二)帮助您获得更轻松的访问体验</h5>
     <p class="text-content">我们可能会设置认证与保障安全性的cookie 或匿名标识符,使我们确认您是否安全登录服务,或者是否遇到盗用、欺诈等不法行为。这些技术还会帮助我们改进服务效率,提升登录和响应速度。</p>
-    <h5>(三)为您推荐、展示、推送您可能感兴趣的内容或账号</h5>
-    <p class="text-content">我们可能会利用Cookie和同类技术了解您的偏好和使用习惯,进行咨询或数据分析,以改善产品服务、推荐用户感兴趣的信息或功能。</p>
-    <p class="text-content">在${properties.name}的分享页中,我们可能会使用cookie对浏览活动进行记录,用于向您推荐信息和排查崩溃、延迟的相关异常情况以及探索更好的服务方式。</p>
+    <p class="text-content">我们可能会利用Cookie和同类技术了解您的偏好和使用习惯,进行咨询或数据分析,以改善产品服务。</p>
+    <p class="text-content">在${properties.name}的分享页中,我们可能会使用cookie对浏览活动进行记录,用于排查崩溃、延迟的相关异常情况以及探索更好的服务方式。</p>
     <p class="text-content">大多数浏览器均为用户提供了清除浏览器缓存数据的功能,您可以在浏览器设置功能中进行相应的数据清除操作。如您进行清除,可能因为这些修改,您可能无法使用依赖于Cookie由公司提供的服务或相应功能。</p>
     <h4>三、我们如何存储个人信息</h4>
     <h5>(一)信息存储的地点</h5>
@@ -262,7 +258,7 @@
       本《隐私政策》适用于由${properties.name}运营主体提供的所有服务,除本《隐私政策》另有规定外,本《隐私政策》所用词语与《${properties.name}用户协议》所定义的词语具有相同的涵义。<span class="strong-text">本《隐私政策》不适用于:</span>
     </p>
     <p class="text-content">
-      1.其他第三方产品或服务,可能包括在个性化推荐中向您显示的产品或网站和广告内容或者“${properties.name}”服务中链接到的其他产品或网站;
+      1.其他第三方产品或服务
     </p>
     <p class="text-content">2.为“${properties.name}”服务进行广告宣传的其他第三方</p>
     <p class="text-content">

+ 44 - 24
src/main/vue/src/views/Admin.vue

@@ -217,26 +217,26 @@ export default {
                                 root: false,
                                 active: true
                             },
-                            // {
-                            //     id: '3410339',
-                            //     name: '空投',
-                            //     path: '/airDropList',
-                            //     icon: '',
-                            //     sort: 43,
-                            //     parent: '3410316',
-                            //     root: false,
-                            //     active: true
-                            // },
-                            // {
-                            //     id: '3410345',
-                            //     name: '兑换券',
-                            //     path: '/couponList',
-                            //     icon: '',
-                            //     sort: 44,
-                            //     parent: '3410316',
-                            //     root: false,
-                            //     active: true
-                            // },
+                            {
+                                id: '3410339',
+                                name: '空投',
+                                path: '/airDropList',
+                                icon: '',
+                                sort: 43,
+                                parent: '3410316',
+                                root: false,
+                                active: true
+                            },
+                            {
+                                id: '3410345',
+                                name: '兑换券',
+                                path: '/couponList',
+                                icon: '',
+                                sort: 44,
+                                parent: '3410316',
+                                root: false,
+                                active: true
+                            },
                             {
                                 id: '3410399',
                                 name: '铸造者',
@@ -420,10 +420,30 @@ export default {
                         }
                     });
                 }
-                setId('', menus);
-                this.rawMenus = menus;
-                this.menus = menus;
-                this.findActiveMenu();
+                this.$http.get(`/company/get/${this.companyId}`).then(res => {
+                    function delKey(name, obj) {
+                        if (obj && obj instanceof Array) {
+                            for (let i = 0; i < obj.length; i++) {
+                                if (obj[i].name === name) {
+                                    obj.splice(i, 1);
+                                } else if (obj[i].children instanceof Array) {
+                                    delKey(name, obj[i].children);
+                                }
+                            }
+                        }
+                    }
+                    if (!res.airdrop) {
+                        delKey('空投', menus);
+                    }
+                    if (!res.mintActivity) {
+                        delKey('铸造管理', menus);
+                    }
+                    console.log(res);
+                    setId('', menus);
+                    this.rawMenus = menus;
+                    this.menus = menus;
+                    this.findActiveMenu();
+                });
             } else {
                 this.$http.get('/menu/userMenu').then(res => {
                     this.rawMenus = res;

+ 5 - 1
src/main/vue/src/views/CompanyEdit.vue

@@ -13,7 +13,7 @@
                     :model="formData"
                     :rules="rules"
                     ref="form"
-                    label-width="66px"
+                    label-width="80px"
                     label-position="right"
                     size="small"
                     style="max-width: 500px;"
@@ -30,6 +30,10 @@
                     <el-form-item prop="disabled" label="禁用">
                         <el-switch v-model="formData.disabled"></el-switch>
                     </el-form-item>
+                    <el-form-item label="功能权限">
+                        <el-checkbox v-model="formData.airdrop">空投</el-checkbox>
+                        <el-checkbox v-model="formData.mintActivity">铸造活动</el-checkbox>
+                    </el-form-item>
                     <el-form-item class="form-submit">
                         <el-button @click="onSave" :loading="saving" type="primary">
                             保存

+ 7 - 5
src/main/vue/src/views/MintActivityList.vue

@@ -75,10 +75,10 @@
                     <el-tag type="success" v-else>自动</el-tag>
                 </template>
             </el-table-column>
-            <el-table-column label="操作" align="center" fixed="right" width="120">
+            <el-table-column label="操作" align="center" fixed="right" width="150">
                 <template slot-scope="{ row }">
                     <el-button @click="editRow(row)" type="primary" size="mini" plain>详情</el-button>
-                    <!-- <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button> -->
+                    <el-button @click="testLink(row)" size="mini" plain>测试</el-button>
                 </template>
             </el-table-column>
         </el-table>
@@ -156,7 +156,7 @@ export default {
             this.$axios
                 .get('/mintActivity/excel', {
                     responseType: 'blob',
-                    params: { size: 10000 ,query: { del: false }},
+                    params: { size: 10000, query: { del: false } }
                 })
                 .then(res => {
                     console.log(res);
@@ -198,9 +198,11 @@ export default {
                         this.$message.error(e.error);
                     }
                 });
+        },
+        testLink(row) {
+            this.$alert(location.origin + `/9th/chooseProduct?activityId=${row.id}&test=true`);
         }
     }
 };
 </script>
-<style lang="less" scoped>
-</style>
+<style lang="less" scoped></style>

+ 1 - 1
src/test/java/com/izouma/nineth/CommonTest.java

@@ -778,6 +778,6 @@ public class CommonTest {
 
     @Test
     public void testrandomize() {
-        System.out.println(randomize(6, 1000));
+        System.out.println(DateTimeUtils.format(LocalDate.now(), "yy/MM/dd: "));
     }
 }

+ 0 - 6
src/test/java/com/izouma/nineth/service/MintOrderServiceTest.java

@@ -43,12 +43,6 @@ class MintOrderServiceTest extends ApplicationTests {
     @Autowired
     private RocketMQTemplate rocketMQTemplate;
 
-    @Test
-    void exchange() throws InterruptedException {
-        mintOrderService.create(9972L, Arrays.asList(134607L, 132435L, 132433L));
-        Thread.sleep(1000);
-    }
-
     @Test
     public void test1() {
         User user = userRepo.findByIdAndDelFalse(9972L).orElse(null);