licailing vor 3 Jahren
Ursprung
Commit
c08e5ea4d2

+ 6 - 0
pom.xml

@@ -411,6 +411,12 @@
             <artifactId>druid-spring-boot-starter</artifactId>
             <version>1.1.21</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-spring-boot-starter</artifactId>
+            <version>2.2.0</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 12 - 0
src/main/java/com/izouma/nineth/annotations/Debounce.java

@@ -0,0 +1,12 @@
+package com.izouma.nineth.annotations;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Debounce {
+    String key();
+
+    long delay() default 200L;
+}

+ 22 - 5
src/main/java/com/izouma/nineth/config/GeneralProperties.java

@@ -6,9 +6,26 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 @ConfigurationProperties(prefix = "general")
 @Data
 public class GeneralProperties {
-    private String host;
-    private String contractName;
-    private String name;
-    private String org;
-    private String shortName;
+    private String  host;
+    private String  contractName;
+    private String  name;
+    private String  org;
+    private String  shortName;
+    private String  createOrderGroup;
+    private String  createOrderTopic;
+    private String  updateStockGroup;
+    private String  updateStockTopic;
+    private String  updateSaleGroup;
+    private String  updateSaleTopic;
+    private String  orderNotifyGroup;
+    private String  orderNotifyTopic;
+    private String  mintGroup;
+    private String  mintTopic;
+    private String  updateActivityStockGroup;
+    private String  updateActivityStockTopic;
+    private String  broadcastEventGroup;
+    private String  broadcastEventTopic;
+    private boolean notifyServer;
+    private int     dataCenterId;
+    private int     workerId;
 }

+ 21 - 0
src/main/java/com/izouma/nineth/config/RedisKeys.java

@@ -0,0 +1,21 @@
+package com.izouma.nineth.config;
+
+public class RedisKeys {
+    public static final String COLLECTION = "collection::";
+
+    public static final String CREATE_ORDER = "createOrder::";
+
+    public static final String COLLECTION_STOCK = "collectionStock::";
+
+    public static final String COLLECTION_SALE = "collectionSale::";
+
+    public static final String PAY_RECORD = "payRecord::";
+
+    public static final String ORDER_LOCK = "orderLock::";
+
+    public static final String MINT_ACTIVITY_STOCK = "mintActivityStock::";
+
+    public static final String MINT_ORDER_LOCK = "mintOrderLock::";
+
+    public static final String ACTIVITY_PAY_RECORD = "activityPayRecord::";
+}

+ 51 - 0
src/main/java/com/izouma/nineth/domain/ActivityCollection.java

@@ -0,0 +1,51 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.annotations.Searchable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+@ApiModel("活动收集")
+public class ActivityCollection extends BaseEntity {
+    @ApiModelProperty("活动名称")
+    @Searchable
+    private String name;
+
+    @Column(columnDefinition = "TEXT")
+    @ApiModelProperty("图片")
+    private String pic;
+
+    @ApiModelProperty("藏品名称")
+    private String collectionName;
+
+    @ApiModelProperty("藏品ID")
+    private Long collectionId;
+
+    @ApiModelProperty("藏品数量")
+    private int num;
+
+    @ApiModelProperty("剩余数量")
+    private int stock;
+
+    @ApiModelProperty("发行数量")
+    private int total;
+
+    @ApiModelProperty("领取藏品ID")
+    private Long awardCollectionId;
+
+    @ApiModelProperty("领取藏品名称")
+    private String awardCollectionName;
+
+    private int projectId;
+}

+ 44 - 0
src/main/java/com/izouma/nineth/domain/ActivityMaterial.java

@@ -0,0 +1,44 @@
+package com.izouma.nineth.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.izouma.nineth.converter.FileObjectListConverter;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import java.util.List;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}, ignoreUnknown = true)
+public class ActivityMaterial extends BaseEntity {
+    private Long orderId;
+
+    private Long assetId;
+
+    private Long collectionId;
+
+    private String name;
+
+    @ApiModelProperty("图片")
+    @Convert(converter = FileObjectListConverter.class)
+    @Column(columnDefinition = "TEXT")
+    private List<FileObject> pic;
+
+    @ApiModelProperty("编号")
+    private Integer number;
+
+    @ApiModelProperty("分类")
+    private String category;
+
+}

+ 33 - 0
src/main/java/com/izouma/nineth/domain/ActivityOrder.java

@@ -0,0 +1,33 @@
+package com.izouma.nineth.domain;
+
+
+import com.izouma.nineth.annotations.Searchable;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.Transient;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+public class ActivityOrder extends BaseEntity {
+
+    private Long userId;
+
+    @Searchable
+    private String phone;
+
+    private Long activityCollectionId;
+
+    private String activityCollection;
+
+    @Transient
+    private List<ActivityMaterial> material;
+
+}

+ 24 - 0
src/main/java/com/izouma/nineth/repo/ActivityCollectionRepo.java

@@ -0,0 +1,24 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.ActivityCollection;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface ActivityCollectionRepo extends JpaRepository<ActivityCollection, Long>, JpaSpecificationExecutor<ActivityCollection> {
+    @Query("update ActivityCollection t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    @Query("select c.stock from ActivityCollection c where c.id = ?1")
+    Integer getStock(Long id);
+
+    @Query("update ActivityCollection c set c.stock = ?2 where c.id = ?1")
+    @Transactional
+    @Modifying
+    int updateStock(Long id, int stock);
+}

+ 16 - 0
src/main/java/com/izouma/nineth/repo/ActivityMaterialRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.ActivityMaterial;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface ActivityMaterialRepo extends JpaRepository<ActivityMaterial, Long>, JpaSpecificationExecutor<ActivityMaterial> {
+    @Query("update ActivityMaterial t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 16 - 0
src/main/java/com/izouma/nineth/repo/ActivityOrderRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.ActivityOrder;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface ActivityOrderRepo extends JpaRepository<ActivityOrder, Long>, JpaSpecificationExecutor<ActivityOrder> {
+    @Query("update ActivityOrder t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 2 - 0
src/main/java/com/izouma/nineth/repo/AssetRepo.java

@@ -45,4 +45,6 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
     Asset findFirstByTokenIdAndCreatedAtAfterOrderByCreatedAt(String tokenId, LocalDateTime time);
 
     List<Asset> findByTxHashIsNullAndTokenIdNotNullAndCreatedAtBefore(LocalDateTime time);
+
+    List<Asset> findAllByCollectionIdAndUserIdAndStatus(Long collectionId, Long userId, AssetStatus status);
 }

+ 5 - 0
src/main/java/com/izouma/nineth/repo/CollectionRepo.java

@@ -95,4 +95,9 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
 
     @Query("select c.currentNumber from Collection c where c.id = ?1")
     Optional<Integer> getCurrentNumber(Long id);
+
+    @Query("select c.stock from Collection c where c.id = ?1")
+    Integer getStock(Long id);
+    List<Collection> findAllByIdIn(java.util.Collection<Long> ids);
+
 }

+ 57 - 0
src/main/java/com/izouma/nineth/service/ActivityCollectionService.java

@@ -0,0 +1,57 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.annotations.Debounce;
+import com.izouma.nineth.config.GeneralProperties;
+import com.izouma.nineth.config.RedisKeys;
+import com.izouma.nineth.domain.ActivityCollection;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.repo.ActivityCollectionRepo;
+import com.izouma.nineth.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.springframework.data.domain.Page;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class ActivityCollectionService {
+
+    private ActivityCollectionRepo        activityCollectionRepo;
+    private RedisTemplate<String, Object> redisTemplate;
+    private CacheService                  cacheService;
+    private RocketMQTemplate              rocketMQTemplate;
+    private GeneralProperties             generalProperties;
+
+    public Page<ActivityCollection> all(PageQuery pageQuery) {
+        return activityCollectionRepo.findAll(JpaUtils.toSpecification(pageQuery, ActivityCollection.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+    public synchronized Long increaseStock(Long id, int number) {
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.MINT_ACTIVITY_STOCK + id);
+        if (ops.get() == null) {
+            Boolean success = ops.setIfAbsent(Optional.ofNullable(activityCollectionRepo.getStock(id))
+                    .orElse(0), 7, TimeUnit.DAYS);
+            log.info("创建redis铸造活动库存:{}", success);
+        }
+        Long stock = ops.increment(number);
+        rocketMQTemplate.convertAndSend(generalProperties.getUpdateActivityStockTopic(), id);
+        return stock;
+    }
+
+    @Debounce(key = "#id", delay = 500)
+    public void syncStock(Long id) {
+        Integer stock = (Integer) redisTemplate.opsForValue().get(RedisKeys.MINT_ACTIVITY_STOCK + id);
+        if (stock != null) {
+            log.info("同步铸造活动库存信息{}", id);
+            activityCollectionRepo.updateStock(id, stock);
+            cacheService.clearActivityCollection(id);
+        }
+    }
+}

+ 20 - 0
src/main/java/com/izouma/nineth/service/ActivityMaterialService.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.domain.ActivityMaterial;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.repo.ActivityMaterialRepo;
+import com.izouma.nineth.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class ActivityMaterialService {
+
+    private ActivityMaterialRepo activityMaterialRepo;
+
+    public Page<ActivityMaterial> all(PageQuery pageQuery) {
+        return activityMaterialRepo.findAll(JpaUtils.toSpecification(pageQuery, ActivityMaterial.class), JpaUtils.toPageRequest(pageQuery));
+    }
+}

+ 133 - 0
src/main/java/com/izouma/nineth/service/ActivityOrderService.java

@@ -0,0 +1,133 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.domain.*;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.AssetStatus;
+import com.izouma.nineth.enums.OrderStatus;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.*;
+import com.izouma.nineth.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class ActivityOrderService {
+
+    private ActivityOrderRepo         activityOrderRepo;
+    private ActivityCollectionRepo    activityCollectionRepo;
+    private ActivityCollectionService activityCollectionService;
+    private AssetRepo                 assetRepo;
+    private OrderRepo                 orderRepo;
+    private CollectionRepo            collectionRepo;
+    private ActivityMaterialRepo      activityMaterialRepo;
+    private CollectionService         collectionService;
+    private AssetService              assetService;
+    private UserRepo                  userRepo;
+
+    public Page<ActivityOrder> all(PageQuery pageQuery) {
+        return activityOrderRepo.findAll(JpaUtils.toSpecification(pageQuery, ActivityOrder.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+
+    public Asset create(User user, Long mintActivityId) {
+        try {
+            ActivityCollection activity = activityCollectionRepo.findById(mintActivityId)
+                    .orElseThrow(new BusinessException("活动不存在"));
+
+            //库存
+            int stock = Optional.ofNullable(activityCollectionService.increaseStock(mintActivityId, -1))
+                    .map(Math::toIntExact)
+                    .orElseThrow(new BusinessException("很遗憾,活动已无库存"));
+            if (stock < 0) {
+                throw new BusinessException("活动已无库存");
+            }
+
+            //数量
+            List<Asset> assets = assetRepo.findAllByCollectionIdAndUserIdAndStatus(activity.getCollectionId(), user.getId(),
+                    AssetStatus.NORMAL);
+            if (assets.size() < activity.getNum()) {
+                throw new BusinessException("数量不足,无法领取");
+            }
+
+            //兑换后的
+            Collection award = collectionRepo.findById(activity.getAwardCollectionId())
+                    .orElseThrow(new BusinessException("无藏品"));
+
+            int awardStock = Optional.ofNullable(collectionService.decreaseStock(activity.getAwardCollectionId(), 1))
+                    .map(Math::toIntExact)
+                    .orElseThrow(new BusinessException("兑换藏品无库存"));
+            if (awardStock < 0) {
+                throw new BusinessException("兑换藏品无库存");
+            }
+
+            //指定账户
+            User newOwner = userRepo.findByIdAndDelFalse(1590945L).orElseThrow(new BusinessException("无法兑换"));
+
+            List<Asset> consumption = assets.stream().limit(activity.getNum()).collect(Collectors.toList());
+            //消耗资产
+            consumption.forEach(asset -> {
+                if (asset.isPublicShow()) {
+
+                    if (asset.getPublicCollectionId() != null) {
+                        List<Order> orders = orderRepo.findByCollectionId(asset.getPublicCollectionId());
+                        if (orders.stream().anyMatch(o -> o.getStatus() != OrderStatus.CANCELLED)) {
+                            throw new BusinessException("已有订单不可兑换");
+                        }
+                        Collection collection = collectionRepo.findById(asset.getPublicCollectionId())
+                                .orElseThrow(new BusinessException("无展示记录"));
+//                        List<Long> collectionIds = collectionRepo.findAllByAssetId(collection.getAssetId());
+//                        if (CollectionUtils.isNotEmpty(collectionIds)) {
+//                            log.info("删除collection {}", collectionIds);
+//                            collectionRepo.deleteAllByIdIn(collectionIds);
+//                        } else {
+                            log.info("删除collection {}", collection.getId());
+                            collectionRepo.delete(collection);
+//                        }
+                    }
+                }
+            });
+
+            //收集记录
+            ActivityOrder order = activityOrderRepo.save(ActivityOrder.builder()
+                    .userId(user.getId())
+                    .phone(user.getPhone())
+                    .activityCollection(activity.getCollectionName())
+                    .activityCollectionId(mintActivityId)
+                    .build());
+
+            //兑换详情
+            consumption.forEach(asset -> {
+                activityMaterialRepo.save(
+                        ActivityMaterial.builder()
+                                .orderId(order.getId())
+                                .assetId(asset.getId())
+                                .category(asset.getCategory())
+                                .collectionId(asset.getCollectionId())
+                                .name(asset.getName())
+                                .number(asset.getNumber())
+                                .pic(asset.getPic())
+                                .build());
+                // 转赠
+                assetService.transfer(asset, asset.getPrice(), newOwner, "转赠", null);
+            });
+
+            //发放新的
+            return assetService.createAsset(award, user, order.getId(), null, "兑换",
+                    award.getTotal() > 1 ? collectionService.getNextNumber(award.getId()) : null);
+
+        } catch (Exception e) {
+            // 错了加库存
+            activityCollectionService.increaseStock(mintActivityId, 1);
+            throw e;
+        }
+    }
+}

+ 4 - 0
src/main/java/com/izouma/nineth/service/CacheService.java

@@ -20,4 +20,8 @@ public class CacheService {
     @CacheEvict(value = "user", key = "#username")
     public void clearUser(String username) {
     }
+
+    @CacheEvict(value = "activityCollection", key = "#id")
+    public void clearActivityCollection(Long id) {
+    }
 }

+ 35 - 7
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -1,6 +1,8 @@
 package com.izouma.nineth.service;
 
 import com.alibaba.fastjson.JSON;
+import com.izouma.nineth.config.GeneralProperties;
+import com.izouma.nineth.config.RedisKeys;
 import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.CollectionDTO;
@@ -13,16 +15,20 @@ import com.izouma.nineth.repo.*;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.RandomUtils;
 import org.apache.commons.lang3.Range;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.beans.BeanUtils;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.TaskScheduler;
 import org.springframework.stereotype.Service;
 
@@ -33,19 +39,25 @@ import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service
 @AllArgsConstructor
 public class CollectionService {
 
-    private CollectionRepo   collectionRepo;
-    private LikeRepo         likeRepo;
-    private BlindBoxItemRepo blindBoxItemRepo;
-    private AppointmentRepo  appointmentRepo;
-    private UserRepo         userRepo;
-    private TaskScheduler    taskScheduler;
-    private CacheService     cacheService;
+    private CollectionRepo                collectionRepo;
+    private LikeRepo                      likeRepo;
+    private BlindBoxItemRepo              blindBoxItemRepo;
+    private AppointmentRepo               appointmentRepo;
+    private UserRepo                      userRepo;
+    private TaskScheduler                 taskScheduler;
+    private CacheService                  cacheService;
+    private RedisTemplate<String, Object> redisTemplate;
+    private RocketMQTemplate              rocketMQTemplate;
+    private GeneralProperties             generalProperties;
+
 
     private final Map<Long, ScheduledFuture<?>> tasks = new HashMap<>();
 
@@ -335,4 +347,20 @@ public class CollectionService {
         collection.setStock(collection.getStock() + number);
         collection.setTotal(collection.getTotal() + number);
     }
+
+    public synchronized Long increaseStock(Long id, int number) {
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.COLLECTION_STOCK + id);
+        if (ops.get() == null) {
+            Boolean success = ops.setIfAbsent(Optional.ofNullable(collectionRepo.getStock(id))
+                    .orElse(0), 7, TimeUnit.DAYS);
+            log.info("创建redis库存:{}", success);
+        }
+        Long stock = ops.increment(number);
+        rocketMQTemplate.convertAndSend(generalProperties.getUpdateStockTopic(), id);
+        return stock;
+    }
+
+    public synchronized Long decreaseStock(Long id, int number) {
+        return increaseStock(id, -number);
+    }
 }

+ 29 - 0
src/main/resources/application.yaml

@@ -141,6 +141,20 @@ general:
   name: 第九空间
   org: 广州麦塔沃司信息技术有限公司
   short-name: 麦塔沃司
+  create-order-group: create-order-group-dev
+  create-order-topic: create-order-topic-dev
+  update-stock-group: update-stock-group-dev
+  update-stock-topic: update-stock-topic-dev
+  update-sale-group: update-sale-group-dev
+  update-sale-topic: update-sale-topic-dev
+  order-notify-group: order-notify-group-dev
+  order-notify-topic: order-notify-topic-dev
+  mint-group: mint-group-dev
+  mint-topic: mint-topic-dev
+  update-activity-stock-group: update-activity-stock-group-dev
+  update-activity-stock-topic: update-activity-stock-topic-dev
+  broadcast-event-group: broadcast-event-group-dev
+  broadcast-event-topic: broadcast-event-topic-dev
 mychain:
   rest:
     bizid: a00e36c5
@@ -216,6 +230,21 @@ spring:
     database: 1
 general:
   host: https://modern.9space.vip
+  create-order-group: create-order-group
+  create-order-topic: create-order-topic
+  update-stock-group: update-stock-group
+  update-stock-topic: update-stock-topic
+  update-sale-group: update-sale-group
+  update-sale-topic: update-sale-topic
+  order-notify-group: order-notify-group
+  order-notify-topic: order-notify-topic
+  mint-group: mint-group
+  mint-topic: mint-topic
+  update-activity-stock-group: update-activity-stock-group
+  update-activity-stock-topic: update-activity-stock-topic
+  broadcast-event-group: broadcast-event-group
+  broadcast-event-topic: broadcast-event-topic
+  notify-server: true
 wx:
   pay:
     notify-url: https://modern.9space.vip/notify/order/weixin