Explorar o código

Merge remote-tracking branch 'origin/dev' into oasis

# Conflicts:
#	src/main/java/com/izouma/nineth/service/CollectionService.java
wangqifan %!s(int64=3) %!d(string=hai) anos
pai
achega
8f09c0b6f7
Modificáronse 98 ficheiros con 3688 adicións e 261 borrados
  1. 28 3
      src/main/java/com/izouma/nineth/aspect/AssetSaveAspect.java
  2. 18 9
      src/main/java/com/izouma/nineth/config/Constants.java
  3. 1 0
      src/main/java/com/izouma/nineth/config/RedisKeys.java
  4. 8 0
      src/main/java/com/izouma/nineth/config/SchedulingConfig.java
  5. 34 0
      src/main/java/com/izouma/nineth/converter/BuildingPosListConverter.java
  6. 29 0
      src/main/java/com/izouma/nineth/converter/CoordinateConverter.java
  7. 31 0
      src/main/java/com/izouma/nineth/converter/RarityConverter.java
  8. 11 0
      src/main/java/com/izouma/nineth/domain/Asset.java
  9. 3 0
      src/main/java/com/izouma/nineth/domain/Banner.java
  10. 8 2
      src/main/java/com/izouma/nineth/domain/BlindBoxItem.java
  11. 10 0
      src/main/java/com/izouma/nineth/domain/Collection.java
  12. 27 0
      src/main/java/com/izouma/nineth/domain/Company.java
  13. 27 0
      src/main/java/com/izouma/nineth/domain/MetaBonusScene.java
  14. 28 0
      src/main/java/com/izouma/nineth/domain/MetaItem.java
  15. 39 0
      src/main/java/com/izouma/nineth/domain/MetaPlayerInfo.java
  16. 32 0
      src/main/java/com/izouma/nineth/domain/MetaPlayerOfflineInfo.java
  17. 32 0
      src/main/java/com/izouma/nineth/domain/MetaPlayerWear.java
  18. 34 0
      src/main/java/com/izouma/nineth/domain/MetaPlayerWearCount.java
  19. 47 0
      src/main/java/com/izouma/nineth/domain/MetaSpatialInfo.java
  20. 25 0
      src/main/java/com/izouma/nineth/domain/MetaUser.java
  21. 27 0
      src/main/java/com/izouma/nineth/domain/OnOff.java
  22. 3 0
      src/main/java/com/izouma/nineth/domain/Order.java
  23. 8 1
      src/main/java/com/izouma/nineth/domain/RarityLabel.java
  24. 43 0
      src/main/java/com/izouma/nineth/domain/SpaceObjectsInfo.java
  25. 24 0
      src/main/java/com/izouma/nineth/dto/BuildingDTO.java
  26. 28 0
      src/main/java/com/izouma/nineth/dto/BuildingPosDTO.java
  27. 20 0
      src/main/java/com/izouma/nineth/dto/CoordinateDTO.java
  28. 27 0
      src/main/java/com/izouma/nineth/dto/MetaPlayerRole.java
  29. 18 0
      src/main/java/com/izouma/nineth/dto/MetaPlayerRoleClassify.java
  30. 51 0
      src/main/java/com/izouma/nineth/dto/MetaRestResult.java
  31. 22 0
      src/main/java/com/izouma/nineth/enums/MetaBonusSceneEnum.java
  32. 17 0
      src/main/java/com/izouma/nineth/enums/MetaItemEnum.java
  33. 21 0
      src/main/java/com/izouma/nineth/enums/MetaPlayerWearEnum.java
  34. 25 0
      src/main/java/com/izouma/nineth/enums/MetaRegionEnum.java
  35. 19 0
      src/main/java/com/izouma/nineth/enums/OnOffEnum.java
  36. 15 0
      src/main/java/com/izouma/nineth/enums/Rarity.java
  37. 6 0
      src/main/java/com/izouma/nineth/enums/UserHoldTypeEnum.java
  38. 3 0
      src/main/java/com/izouma/nineth/repo/AssetRepo.java
  39. 1 1
      src/main/java/com/izouma/nineth/repo/BlindBoxItemRepo.java
  40. 16 0
      src/main/java/com/izouma/nineth/repo/MetaBonusSceneRepo.java
  41. 14 0
      src/main/java/com/izouma/nineth/repo/MetaItemRepo.java
  42. 12 0
      src/main/java/com/izouma/nineth/repo/MetaPlayerOfflineInfoRepo.java
  43. 22 0
      src/main/java/com/izouma/nineth/repo/MetaPlayerWearRepo.java
  44. 26 0
      src/main/java/com/izouma/nineth/repo/MetaSpatialInfoRepo.java
  45. 10 0
      src/main/java/com/izouma/nineth/repo/MetaUserRepo.java
  46. 16 0
      src/main/java/com/izouma/nineth/repo/OnOffRepo.java
  47. 8 0
      src/main/java/com/izouma/nineth/repo/PurchaseLevelRepo.java
  48. 16 0
      src/main/java/com/izouma/nineth/repo/SpaceObjectsInfoRepo.java
  49. 11 1
      src/main/java/com/izouma/nineth/security/WebSecurityConfig.java
  50. 32 36
      src/main/java/com/izouma/nineth/service/AirDropService.java
  51. 2 0
      src/main/java/com/izouma/nineth/service/AssetMintService.java
  52. 43 16
      src/main/java/com/izouma/nineth/service/AssetService.java
  53. 1 1
      src/main/java/com/izouma/nineth/service/AuctionOrderService.java
  54. 71 50
      src/main/java/com/izouma/nineth/service/CollectionService.java
  55. 0 40
      src/main/java/com/izouma/nineth/service/GiftOrderService.java
  56. 165 0
      src/main/java/com/izouma/nineth/service/MetaPlayerInfoService.java
  57. 20 0
      src/main/java/com/izouma/nineth/service/MetaPlayerWearService.java
  58. 20 0
      src/main/java/com/izouma/nineth/service/MetaSpatialInfoService.java
  59. 20 0
      src/main/java/com/izouma/nineth/service/OnOffService.java
  60. 142 0
      src/main/java/com/izouma/nineth/service/OrderPayService.java
  61. 1 40
      src/main/java/com/izouma/nineth/service/OrderService.java
  62. 8 0
      src/main/java/com/izouma/nineth/service/SysConfigService.java
  63. 4 4
      src/main/java/com/izouma/nineth/service/UserService.java
  64. 91 0
      src/main/java/com/izouma/nineth/web/AlipayNotifyController.java
  65. 11 13
      src/main/java/com/izouma/nineth/web/AssetController.java
  66. 11 0
      src/main/java/com/izouma/nineth/web/AuctionOrderController.java
  67. 27 4
      src/main/java/com/izouma/nineth/web/CollectionController.java
  68. 12 0
      src/main/java/com/izouma/nineth/web/GiftOrderController.java
  69. 43 0
      src/main/java/com/izouma/nineth/web/MetaBonusSceneController.java
  70. 91 0
      src/main/java/com/izouma/nineth/web/MetaPlayerInfoController.java
  71. 97 0
      src/main/java/com/izouma/nineth/web/MetaPlayerWearController.java
  72. 87 0
      src/main/java/com/izouma/nineth/web/MetaSpatialInfoController.java
  73. 86 0
      src/main/java/com/izouma/nineth/web/OnOffController.java
  74. 30 0
      src/main/java/com/izouma/nineth/web/OrderPayControllerV2.java
  75. 3 2
      src/main/java/com/izouma/nineth/web/RarityLabelController.java
  76. 35 0
      src/main/java/com/izouma/nineth/web/RechargeOrderController.java
  77. 17 0
      src/main/java/com/izouma/nineth/web/UserHoldCountController.java
  78. 8 4
      src/main/resources/application.yaml
  79. 1 0
      src/main/resources/genjson/MetaPlayerWear.json
  80. 1 0
      src/main/resources/genjson/MetaSpatialInfo.json
  81. 1 0
      src/main/resources/genjson/OnOff.json
  82. 198 0
      src/main/resources/static/wx_alipay_bridge.html
  83. 1 1
      src/main/vue/src/components/CollectionCoupon.vue
  84. 94 0
      src/main/vue/src/components/ColorSelect.vue
  85. 49 1
      src/main/vue/src/router.js
  86. 22 0
      src/main/vue/src/views/BannerEdit.vue
  87. 17 4
      src/main/vue/src/views/BlindBoxEdit.vue
  88. 9 2
      src/main/vue/src/views/CollectionEdit.vue
  89. 178 0
      src/main/vue/src/views/MetaPlayerWearEdit.vue
  90. 192 0
      src/main/vue/src/views/MetaPlayerWearList.vue
  91. 192 0
      src/main/vue/src/views/MetaSpatialInfoEdit.vue
  92. 206 0
      src/main/vue/src/views/MetaSpatialInfoList.vue
  93. 125 0
      src/main/vue/src/views/OnOffEdit.vue
  94. 188 0
      src/main/vue/src/views/OnOffList.vue
  95. 10 9
      src/main/vue/src/views/RarityLabelEdit.vue
  96. 14 9
      src/main/vue/src/views/RarityLabelList.vue
  97. 8 6
      src/test/java/com/izouma/nineth/RedisTest.java
  98. 3 2
      src/test/java/com/izouma/nineth/service/CollectionServiceTest.java

+ 28 - 3
src/main/java/com/izouma/nineth/aspect/AssetSaveAspect.java

@@ -7,15 +7,31 @@ import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.After;
 import org.aspectj.lang.annotation.Aspect;
+import org.springframework.scheduling.TaskScheduler;
 import org.springframework.stereotype.Component;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
 @Aspect
 @Component
 @Slf4j
-@AllArgsConstructor
 public class AssetSaveAspect {
 
-    private UserAssetSummaryService userAssetSummaryService;
+    private final UserAssetSummaryService  userAssetSummaryService;
+    private final ScheduledExecutorService executorService;
+    private final Map<Long, Future>        futureMap;
+
+    public AssetSaveAspect(UserAssetSummaryService userAssetSummaryService, ScheduledExecutorService executorService) {
+        this.userAssetSummaryService = userAssetSummaryService;
+        this.executorService = executorService;
+        this.futureMap = new HashMap<>();
+    }
 
     @After("execution(* com.izouma.nineth.repo.AssetRepo.save(..)) || execution(* com.izouma.nineth.repo.AssetRepo.saveAndFlush(..))")
     public void redisLock(JoinPoint joinPoint) {
@@ -23,7 +39,16 @@ public class AssetSaveAspect {
         try {
             Asset asset = (Asset) joinPoint.getArgs()[0];
             log.info("recalculate user asset summary {}", asset.getUserId());
-            userAssetSummaryService.calculateNum(asset.getUserId());
+            synchronized (AssetSaveAspect.class) {
+                Optional.ofNullable(futureMap.get(asset.getUserId()))
+                        .ifPresent(f -> {
+                            log.info("debouncing previous task {}", asset.getUserId());
+                            f.cancel(false);
+                        });
+                futureMap.put(asset.getUserId(), executorService.schedule(() -> {
+                    userAssetSummaryService.calculateNum(asset.getUserId());
+                }, 500, TimeUnit.MILLISECONDS));
+            }
         } catch (Exception e) {
             log.error("AssetSaveAspect error", e);
         }

+ 18 - 9
src/main/java/com/izouma/nineth/config/Constants.java

@@ -21,6 +21,8 @@ public interface Constants {
 
     Long BLACK_HOLE_USER_ID = 1435297L;
 
+    String ALIPAY_URL_SCHEME = "alipays://platformapi/startapp?saId=10000007&qrcode=";
+
     interface PayChannel {
         String SAND = "sandPay";
         String HM   = "hmPay";
@@ -39,21 +41,28 @@ public interface Constants {
         String GIFT     = "gift";
         String MINT     = "mintOrder";
         String RECHARGE = "recharge";
-
-        String AUCTION = "auctionOrder";
+        String AUCTION  = "auctionOrder";
     }
 
     interface Rarity {
-        String SSR    = "SSR";
-        String SR     = "SR";
-        String U     = "U";
-        String R     = "R";
+        String SSR = "SSR";
+        String SR  = "SR";
+        String U   = "U";
+        String R   = "R";
 
         String SSR_LIKE = "%SSR #%";
-        String SR_LIKE = "%SR #%";
-        String U_LIKE = "%U #%";
-        String R_LIKE = "%R #%";
+        String SR_LIKE  = "%SR #%";
+        String U_LIKE   = "%U #%";
+        String R_LIKE   = "%R #%";
 
         String ACTIVITY_RANK_ID = "activity_rank_id";
     }
+
+    interface MetaRestCode {
+
+        int success = 200;
+
+        int err = 400;
+
+    }
 }

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

@@ -47,4 +47,5 @@ public class RedisKeys {
 
     public static final String AUCTION_ORDER_LOCK = "auctionOrderLock::";
 
+    public static final String DRAW_BLIND_BOX = "drawBlindBox::";
 }

+ 8 - 0
src/main/java/com/izouma/nineth/config/SchedulingConfig.java

@@ -5,6 +5,9 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.TaskScheduler;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
 @Configuration
 public class SchedulingConfig {
     @Bean
@@ -16,4 +19,9 @@ public class SchedulingConfig {
         taskScheduler.setThreadNamePrefix("Scheduler-");
         return taskScheduler;
     }
+
+    @Bean
+    public ScheduledExecutorService ScheduledExecutorService() {
+        return Executors.newScheduledThreadPool(10);
+    }
 }

+ 34 - 0
src/main/java/com/izouma/nineth/converter/BuildingPosListConverter.java

@@ -0,0 +1,34 @@
+package com.izouma.nineth.converter;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.izouma.nineth.dto.BuildingPosDTO;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+import java.util.List;
+
+@Converter
+public class BuildingPosListConverter implements AttributeConverter<List<BuildingPosDTO>, String> {
+
+    @Override
+    public String convertToDatabaseColumn(List<BuildingPosDTO> buildingPosDTOList) {
+        if (buildingPosDTOList == null) {
+            return null;
+        }
+        return JSON.toJSONString(buildingPosDTOList);
+    }
+
+    @Override
+    public List<BuildingPosDTO> convertToEntityAttribute(String s) {
+        if (StringUtils.isEmpty(s)) {
+            return null;
+        }
+        try {
+            return JSONArray.parseArray(s, BuildingPosDTO.class);
+        } catch (Exception ignored) {
+        }
+        return null;
+    }
+}

+ 29 - 0
src/main/java/com/izouma/nineth/converter/CoordinateConverter.java

@@ -0,0 +1,29 @@
+package com.izouma.nineth.converter;
+
+
+import com.alibaba.fastjson.JSON;
+import com.izouma.nineth.dto.CoordinateDTO;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+
+@Converter
+public class CoordinateConverter implements AttributeConverter<CoordinateDTO, String> {
+
+    @Override
+    public String convertToDatabaseColumn(CoordinateDTO fileObject) {
+        if (fileObject != null)
+            return JSON.toJSONString(fileObject);
+        return null;
+    }
+
+    @Override
+    public CoordinateDTO convertToEntityAttribute(String s) {
+        if (StringUtils.isNotEmpty(s)) {
+            return JSON.parseObject(s, CoordinateDTO.class);
+        }
+        return null;
+    }
+}
+

+ 31 - 0
src/main/java/com/izouma/nineth/converter/RarityConverter.java

@@ -0,0 +1,31 @@
+package com.izouma.nineth.converter;
+
+import com.alibaba.fastjson.JSON;
+import com.izouma.nineth.enums.Rarity;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+import java.util.List;
+
+@Converter
+public class RarityConverter implements AttributeConverter<List<Rarity>, String> {
+    @Override
+    public String convertToDatabaseColumn(List<Rarity> rarity) {
+        if (rarity != null) {
+            return JSON.toJSONString(rarity);
+        }
+        return null;
+    }
+
+    @Override
+    public List<Rarity> convertToEntityAttribute(String s) {
+        if (StringUtils.isNotEmpty(s)) {
+            try {
+                return JSON.parseArray(s, Rarity.class);
+            } catch (Exception ignored) {
+            }
+        }
+        return null;
+    }
+}

+ 11 - 0
src/main/java/com/izouma/nineth/domain/Asset.java

@@ -235,6 +235,13 @@ public class Asset extends CollectionBaseEntity {
 
     private LocalDateTime lockTo;
 
+    @Column(columnDefinition = "bigint default 1 not null")
+    private Long companyId;
+
+    @ApiModelProperty("bit 0: 蚂蚁链, bit 1: 华储链")
+    @Column(columnDefinition = "int default 3 not null")
+    private int chainFlag;
+
     public static Asset create(Collection collection, User user) {
         return Asset.builder()
                 .userId(user.getId())
@@ -264,6 +271,8 @@ public class Asset extends CollectionBaseEntity {
                 .tags(new HashSet<>(collection.getTags()))
                 .prefixName(collection.getPrefixName())
                 .empower(collection.getEmpower())
+                .companyId(collection.getCompanyId())
+                .chainFlag(collection.getChainFlag())
                 .build();
     }
 
@@ -294,6 +303,8 @@ public class Asset extends CollectionBaseEntity {
                 .oldHoldDays(holdDays)
                 .source(AssetSource.OFFICIAL)
                 .opened(false)
+                .companyId(item.getCollection().getCompanyId())
+                .chainFlag(item.getCollection().getChainFlag())
                 .build();
     }
 }

+ 3 - 0
src/main/java/com/izouma/nineth/domain/Banner.java

@@ -48,4 +48,7 @@ public class Banner extends BaseEntity {
 
     @ApiModelProperty("配置id")
     private Long settingId;
+
+    @ApiModelProperty("背景颜色")
+    private String backgroundColor;
 }

+ 8 - 2
src/main/java/com/izouma/nineth/domain/BlindBoxItem.java

@@ -93,10 +93,10 @@ public class BlindBoxItem extends BaseEntity {
     private boolean canResale;
 
     @ApiModelProperty("版税比例")
-    private int royalties;
+    private double royalties;
 
     @ApiModelProperty("手续费比例")
-    private int serviceCharge;
+    private double serviceCharge;
 
     @ApiModelProperty("分类")
     private String category;
@@ -106,4 +106,10 @@ public class BlindBoxItem extends BaseEntity {
 
 //    @ApiModelProperty("持有几天")
 //    private Integer holdDays;
+
+    @Column(columnDefinition = "bigint default 1 not null")
+    private Long companyId;
+
+    @Transient
+    private Collection collection;
 }

+ 10 - 0
src/main/java/com/izouma/nineth/domain/Collection.java

@@ -50,6 +50,7 @@ import java.util.Set;
         @Index(columnList = "onShelf,del,source"),
         @Index(columnList = "onShelf,del,source,salable"),
         @Index(columnList = "onShelf,del,source,salable,price"),
+        @Index(columnList = "prefixName"),
 })
 @DynamicUpdate
 @AllArgsConstructor
@@ -275,9 +276,18 @@ public class Collection extends CollectionBaseEntity {
     private BigInteger hcGasUsed;
     private String     hcTokenId;
 
+    @ApiModelProperty("系列名称")
+    @Column(length = 100)
     private String prefixName;
 
     @ApiModelProperty("赋能列表")
     @Column(columnDefinition = "TEXT")
     private String empower;
+
+    @Column(columnDefinition = "bigint default 1 not null")
+    private Long companyId = 1L;
+
+    @ApiModelProperty("bit 0: 蚂蚁链, bit 1: 华储链")
+    @Column(columnDefinition = "int default 3 not null")
+    private int chainFlag;
 }

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

@@ -0,0 +1,27 @@
+package com.izouma.nineth.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class Company extends BaseEntity {
+
+    private String name;
+
+    private String description;
+
+    private String logo;
+
+    private Long adminUserId;
+
+    private boolean disabled;
+
+}

+ 27 - 0
src/main/java/com/izouma/nineth/domain/MetaBonusScene.java

@@ -0,0 +1,27 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.enums.MetaBonusSceneEnum;
+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.EnumType;
+import javax.persistence.Enumerated;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙彩蛋")
+public class MetaBonusScene extends BaseEntity {
+
+    @ApiModelProperty("玩家id")
+    private Long userId;
+
+    @ApiModelProperty("彩蛋类型")
+    @Enumerated(EnumType.STRING)
+    private MetaBonusSceneEnum type;
+}

+ 28 - 0
src/main/java/com/izouma/nineth/domain/MetaItem.java

@@ -0,0 +1,28 @@
+package com.izouma.nineth.domain;
+
+
+import com.izouma.nineth.enums.MetaItemEnum;
+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.EnumType;
+import javax.persistence.Enumerated;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙物品")
+public class MetaItem extends BaseEntity {
+
+    @ApiModelProperty("物品名称")
+    private String name;
+
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty("物品类型")
+    private MetaItemEnum type;
+}

+ 39 - 0
src/main/java/com/izouma/nineth/domain/MetaPlayerInfo.java

@@ -0,0 +1,39 @@
+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 java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("元宇宙玩家信息")
+public class MetaPlayerInfo {
+
+    @ApiModelProperty("头像")
+    private String avatar;
+
+    @ApiModelProperty("昵称")
+    private String nickname;
+
+    @ApiModelProperty("勋章等级")
+    private int level;
+
+    @ApiModelProperty("境界")
+    private String realm;
+
+    @ApiModelProperty("头衔")
+    private String title;
+
+    @ApiModelProperty("玩家上次离线的服饰信息")
+    private List<MetaPlayerWear> metaPlayerWearList;
+
+    @ApiModelProperty("玩家上次离线的皮肤编号,默认为0")
+    private int skinId = 0;
+
+}

+ 32 - 0
src/main/java/com/izouma/nineth/domain/MetaPlayerOfflineInfo.java

@@ -0,0 +1,32 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.converter.LongArrayConverter;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙玩家离线信息")
+public class MetaPlayerOfflineInfo extends BaseEntity {
+
+    private Long userId;
+
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = LongArrayConverter.class)
+    @ApiModelProperty("离线时服饰id集合")
+    private List<Long> clothesId;
+
+    @ApiModelProperty("离线时皮肤编号")
+    private int skinId;
+
+}

+ 32 - 0
src/main/java/com/izouma/nineth/domain/MetaPlayerWear.java

@@ -0,0 +1,32 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.converter.FileObjectConverter;
+import com.izouma.nineth.enums.MetaPlayerWearEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙玩家服饰")
+public class MetaPlayerWear extends BaseEntity {
+
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    @ApiModelProperty("模型")
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = FileObjectConverter.class)
+    private FileObject model;
+
+    @ApiModelProperty("类型")
+    @Enumerated(EnumType.STRING)
+    private MetaPlayerWearEnum type;
+
+}

+ 34 - 0
src/main/java/com/izouma/nineth/domain/MetaPlayerWearCount.java

@@ -0,0 +1,34 @@
+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 java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("元宇宙服饰相关")
+public class MetaPlayerWearCount {
+
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    @ApiModelProperty("头饰")
+    private List<MetaPlayerWear> headdressList;
+
+    @ApiModelProperty("衣服")
+    private List<MetaPlayerWear> clothesList;
+
+    @ApiModelProperty("裤子")
+    private List<MetaPlayerWear> trousersList;
+
+    @ApiModelProperty("鞋子")
+    private List<MetaPlayerWear> shoesList;
+
+    @ApiModelProperty("动作")
+    private List<MetaPlayerWear> actionList;
+}

+ 47 - 0
src/main/java/com/izouma/nineth/domain/MetaSpatialInfo.java

@@ -0,0 +1,47 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.converter.CoordinateConverter;
+import com.izouma.nineth.dto.CoordinateDTO;
+import com.izouma.nineth.enums.MetaRegionEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙空间信息")
+public class MetaSpatialInfo extends BaseEntity {
+
+    @ApiModelProperty("所属区域")
+    @Enumerated(EnumType.STRING)
+    private MetaRegionEnum region;
+
+    @ApiModelProperty("目前状态:true在售/false未售")
+    private boolean sale;
+
+    @ApiModelProperty("所属用户id")
+    private Long userId;
+
+    @ApiModelProperty("空间大小")
+    private int size;
+
+    @ApiModelProperty("区域内坐标")
+    @Convert(converter = CoordinateConverter.class)
+    private CoordinateDTO coordinate;
+
+    @ApiModelProperty("藏品id")
+    private Long collectionId;
+
+    @ApiModelProperty("hash值")
+    private String hcTxHash;
+
+}

+ 25 - 0
src/main/java/com/izouma/nineth/domain/MetaUser.java

@@ -0,0 +1,25 @@
+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.Id;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙内测用户")
+public class MetaUser extends BaseEntityNoID {
+
+    @ApiModelProperty("用户手机号")
+    @Id
+    private Long phone;
+
+    @ApiModelProperty("是否允许登陆")
+    private boolean allowLogin;
+}

+ 27 - 0
src/main/java/com/izouma/nineth/domain/OnOff.java

@@ -0,0 +1,27 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.enums.OnOffEnum;
+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.EnumType;
+import javax.persistence.Enumerated;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙开关")
+public class OnOff extends BaseEntity {
+
+    @ApiModelProperty("状态:开/关")
+    @Enumerated(EnumType.STRING)
+    private OnOffEnum status;
+
+    @ApiModelProperty("备注")
+    private String remark;
+}

+ 3 - 0
src/main/java/com/izouma/nineth/domain/Order.java

@@ -219,4 +219,7 @@ public class Order extends BaseEntityNoID {
 
     @ApiModelProperty("卖家id")
     private Long sellerId;
+
+    @Column(columnDefinition = "bigint default 1 not null")
+    private Long companyId;
 }

+ 8 - 1
src/main/java/com/izouma/nineth/domain/RarityLabel.java

@@ -1,12 +1,17 @@
 package com.izouma.nineth.domain;
 
 
+import com.izouma.nineth.converter.RarityConverter;
+import com.izouma.nineth.enums.Rarity;
 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
@@ -17,5 +22,7 @@ public class RarityLabel extends BaseEntity{
 
     private String name;
 
-    private String label;
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = RarityConverter.class)
+    private List<Rarity> label;
 }

+ 43 - 0
src/main/java/com/izouma/nineth/domain/SpaceObjectsInfo.java

@@ -0,0 +1,43 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.converter.BuildingPosListConverter;
+import com.izouma.nineth.dto.BuildingDTO;
+import com.izouma.nineth.dto.BuildingPosDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import javax.persistence.Transient;
+import java.util.List;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("空间物品信息")
+public class SpaceObjectsInfo extends BaseEntity{
+
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    @ApiModelProperty("空间id")
+    private Long spaceId;
+
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = BuildingPosListConverter.class)
+    @ApiModelProperty("空间中已编辑物品信息")
+    private List<BuildingPosDTO> buildingPosList;
+
+    @ApiModelProperty("空间中剩余物品信息统计")
+    @Transient
+    private List<BuildingDTO> buildingList;
+
+    @ApiModelProperty("是否个人空间")
+    @Transient
+    private boolean personal;
+}

+ 24 - 0
src/main/java/com/izouma/nineth/dto/BuildingDTO.java

@@ -0,0 +1,24 @@
+package com.izouma.nineth.dto;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("空间中物品信息统计")
+public class BuildingDTO {
+
+    @ApiModelProperty("物品id")
+    private Long itmeId;
+
+    @ApiModelProperty("物品名称")
+    private String name;
+
+    @ApiModelProperty("物品数量")
+    private int count;
+}

+ 28 - 0
src/main/java/com/izouma/nineth/dto/BuildingPosDTO.java

@@ -0,0 +1,28 @@
+package com.izouma.nineth.dto;
+
+
+import com.izouma.nineth.converter.CoordinateConverter;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Convert;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("空间中已编辑物品信息")
+public class BuildingPosDTO {
+
+    @ApiModelProperty("物品id")
+    private Long itemId;
+
+    @ApiModelProperty("位置信息")
+    @Convert(converter = CoordinateConverter.class)
+    private CoordinateDTO coordinate;
+
+    @ApiModelProperty("旋转值")
+    private int rotateCount;
+}

+ 20 - 0
src/main/java/com/izouma/nineth/dto/CoordinateDTO.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.dto;
+
+
+import io.swagger.annotations.ApiModel;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("元宇宙坐标")
+public class CoordinateDTO {
+
+    private String x;
+
+    private String y;
+
+    private String z;
+}

+ 27 - 0
src/main/java/com/izouma/nineth/dto/MetaPlayerRole.java

@@ -0,0 +1,27 @@
+package com.izouma.nineth.dto;
+
+import com.izouma.nineth.enums.UserHoldTypeEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class MetaPlayerRole {
+
+    private Long id;
+
+    private String name;
+
+    private boolean hold;
+
+    private String address;
+
+    private UserHoldTypeEnum type;
+
+    public MetaPlayerRole(Long id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+}

+ 18 - 0
src/main/java/com/izouma/nineth/dto/MetaPlayerRoleClassify.java

@@ -0,0 +1,18 @@
+package com.izouma.nineth.dto;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class MetaPlayerRoleClassify {
+
+    private String classify;
+
+    private List<MetaPlayerRole> metaPlayerRoleList;
+}

+ 51 - 0
src/main/java/com/izouma/nineth/dto/MetaRestResult.java

@@ -0,0 +1,51 @@
+package com.izouma.nineth.dto;
+
+import com.izouma.nineth.config.Constants;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("元宇宙通用返回类")
+public class MetaRestResult<T> {
+
+    @ApiModelProperty("返回消息")
+    private String message;
+
+    @ApiModelProperty("返回状态")
+    private int code;
+
+    @ApiModelProperty("返回数据")
+    private T data;
+
+    public static <T> MetaRestResult<T> returnSuccess(T data) {
+        String successMsg = "请求成功";
+        return returnSuccess(successMsg, data);
+    }
+
+    public static <T> MetaRestResult<T> returnSuccess(String successMessage, T data) {
+        MetaRestResult<T> result = returnResult(data);
+        result.setMessage(successMessage);
+        result.setCode(Constants.MetaRestCode.success);
+        return result;
+    }
+
+    public static <T> MetaRestResult<T> returnError(String errorMessage) {
+        MetaRestResult<T> result = returnResult(null);
+        result.setMessage(errorMessage);
+        result.setCode(Constants.MetaRestCode.err);
+        return result;
+    }
+
+    private static <T> MetaRestResult<T> returnResult(T data) {
+        MetaRestResult<T> result = new MetaRestResult<>();
+        result.setData(data);
+        return result;
+    }
+
+
+}

+ 22 - 0
src/main/java/com/izouma/nineth/enums/MetaBonusSceneEnum.java

@@ -0,0 +1,22 @@
+package com.izouma.nineth.enums;
+
+
+public enum MetaBonusSceneEnum {
+
+    THIRTY("30人"),
+
+    HUNDRED("100人"),
+
+    THREE_HUNDRED("300人");
+
+    private final String description;
+
+    MetaBonusSceneEnum(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}
+

+ 17 - 0
src/main/java/com/izouma/nineth/enums/MetaItemEnum.java

@@ -0,0 +1,17 @@
+package com.izouma.nineth.enums;
+
+
+public enum MetaItemEnum {
+
+    BUILDING("建筑");
+
+    private final String description;
+
+    MetaItemEnum(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 21 - 0
src/main/java/com/izouma/nineth/enums/MetaPlayerWearEnum.java

@@ -0,0 +1,21 @@
+package com.izouma.nineth.enums;
+
+
+public enum MetaPlayerWearEnum {
+
+    HEADDRESS("头饰"),
+    CLOTHES("衣服"),
+    TROUSERS("裤子"),
+    SHOES("鞋子"),
+    ACTION("动作");
+
+    private final String description;
+
+    MetaPlayerWearEnum(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 25 - 0
src/main/java/com/izouma/nineth/enums/MetaRegionEnum.java

@@ -0,0 +1,25 @@
+package com.izouma.nineth.enums;
+
+
+public enum MetaRegionEnum {
+
+    ONE("1区"),
+    TWO("2区"),
+    THREE("3区"),
+    FOUR("4区"),
+    FIVE("5区"),
+    SIX("6区"),
+    SEVEN("7区"),
+    EIGHT("8区"),
+    NINE("9区");
+
+    private final String description;
+
+    MetaRegionEnum(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 19 - 0
src/main/java/com/izouma/nineth/enums/OnOffEnum.java

@@ -0,0 +1,19 @@
+package com.izouma.nineth.enums;
+
+
+public enum OnOffEnum {
+
+    ON("开"),
+
+    OFF("关");
+
+    private final String description;
+
+    OnOffEnum(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 15 - 0
src/main/java/com/izouma/nineth/enums/Rarity.java

@@ -0,0 +1,15 @@
+package com.izouma.nineth.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Rarity {
+
+    private String name;
+    private String value;
+
+}

+ 6 - 0
src/main/java/com/izouma/nineth/enums/UserHoldTypeEnum.java

@@ -0,0 +1,6 @@
+package com.izouma.nineth.enums;
+
+public enum UserHoldTypeEnum {
+
+    ASSET;
+}

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

@@ -111,4 +111,7 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
     int totalElements();
 
     List<Asset> findAllByUserIdAndStatusIn(Long userId, List<AssetStatus> status);
+
+    @Query("select a from Asset a where a.status in (com.izouma.nineth.enums.AssetStatus.NORMAL,com.izouma.nineth.enums.AssetStatus.TRADING,com.izouma.nineth.enums.AssetStatus.GIFTING,com.izouma.nineth.enums.AssetStatus.MINTING,com.izouma.nineth.enums.AssetStatus.AUCTIONING) and a.userId = ?1 and a.name like ?2")
+    List<Asset> findAllByUserIdAndNameLike(Long userId, String name);
 }

+ 1 - 1
src/main/java/com/izouma/nineth/repo/BlindBoxItemRepo.java

@@ -49,6 +49,6 @@ public interface BlindBoxItemRepo extends JpaRepository<BlindBoxItem, Long>, Jpa
     @Transactional
     void increaseStockAndDecreaseSale(Long id, int num);
 
-    @Query(nativeQuery = true, value = "select sum(stock) stock ,sum(total) total from blind_box_item where blind_box_id = ?1 and name like ?2 AND NAME NOT LIKE ?3")
+    @Query(nativeQuery = true, value = "select sum(total - sale) stock ,sum(total) total from blind_box_item where blind_box_id = ?1 and name like ?2 AND NAME NOT LIKE ?3")
     Map<String, BigDecimal> getBlindBoxRare(Long blindBoxId, String rare, String not);
 }

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

@@ -0,0 +1,16 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.MetaBonusScene;
+import com.izouma.nineth.enums.MetaBonusSceneEnum;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+public interface MetaBonusSceneRepo extends JpaRepository<MetaBonusScene, Long>, JpaSpecificationExecutor<MetaBonusScene> {
+
+    List<MetaBonusScene> findAllByUserId(Long userId);
+
+    MetaBonusScene findByUserIdAndType(Long userId, MetaBonusSceneEnum type);
+
+}

+ 14 - 0
src/main/java/com/izouma/nineth/repo/MetaItemRepo.java

@@ -0,0 +1,14 @@
+package com.izouma.nineth.repo;
+
+
+import com.izouma.nineth.domain.MetaItem;
+import com.izouma.nineth.enums.MetaItemEnum;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import java.util.List;
+
+public interface MetaItemRepo extends JpaRepository<MetaItem, Long>, JpaSpecificationExecutor<MetaItem> {
+
+    List<MetaItem> findAllByType(MetaItemEnum type);
+}

+ 12 - 0
src/main/java/com/izouma/nineth/repo/MetaPlayerOfflineInfoRepo.java

@@ -0,0 +1,12 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.MetaPlayerOfflineInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+
+public interface MetaPlayerOfflineInfoRepo extends JpaRepository<MetaPlayerOfflineInfo, Long>, JpaSpecificationExecutor<MetaPlayerOfflineInfo> {
+
+    @Query(value = "select * from meta_player_offline_info a where a.user_id = ?1 order by a.created_at desc limit 1",nativeQuery = true)
+    MetaPlayerOfflineInfo findLastByUserId(Long userId);
+}

+ 22 - 0
src/main/java/com/izouma/nineth/repo/MetaPlayerWearRepo.java

@@ -0,0 +1,22 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.MetaPlayerWear;
+import com.izouma.nineth.enums.MetaPlayerWearEnum;
+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;
+import java.util.List;
+
+public interface MetaPlayerWearRepo extends JpaRepository<MetaPlayerWear, Long>, JpaSpecificationExecutor<MetaPlayerWear> {
+    @Query("update MetaPlayerWear t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    List<MetaPlayerWear> findAllByUserIdAndType(Long userId, MetaPlayerWearEnum type);
+
+    List<MetaPlayerWear> findAllByIdIn(List<Long> ids);
+}

+ 26 - 0
src/main/java/com/izouma/nineth/repo/MetaSpatialInfoRepo.java

@@ -0,0 +1,26 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.MetaSpatialInfo;
+import com.izouma.nineth.dto.CoordinateDTO;
+import com.izouma.nineth.enums.MetaRegionEnum;
+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;
+import java.util.List;
+
+public interface MetaSpatialInfoRepo extends JpaRepository<MetaSpatialInfo, Long>, JpaSpecificationExecutor<MetaSpatialInfo> {
+
+    @Query("update MetaSpatialInfo t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    MetaSpatialInfo findByRegionAndCoordinateAndDel(MetaRegionEnum region, CoordinateDTO coordinate, boolean del);
+
+    List<MetaSpatialInfo> findAllByUserIdAndDel(Long userId, boolean del);
+
+    int countBySaleAndDel(boolean sale, boolean del);
+}

+ 10 - 0
src/main/java/com/izouma/nineth/repo/MetaUserRepo.java

@@ -0,0 +1,10 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.MetaUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface MetaUserRepo extends JpaRepository<MetaUser, Long>, JpaSpecificationExecutor<MetaUser> {
+
+    MetaUser findByPhone(Long phone);
+}

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

@@ -0,0 +1,16 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.OnOff;
+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 OnOffRepo extends JpaRepository<OnOff, Long>, JpaSpecificationExecutor<OnOff> {
+    @Query("update OnOff t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 8 - 0
src/main/java/com/izouma/nineth/repo/PurchaseLevelRepo.java

@@ -13,4 +13,12 @@ public interface PurchaseLevelRepo extends JpaRepository<PurchaseLevel, Long>, J
     @Modifying
     @Transactional
     void softDelete(Long id);
+
+    @Query(value = "select * from purchase_level a where a.start_level <= ?1 and a.end_level >= ?1 and a.del = 0",nativeQuery = true)
+    PurchaseLevel findByLevel(int level);
+
+    @Query(value = "select max(start_level) from purchase_level",nativeQuery = true)
+    int findMaxStartLevel();
+
+    PurchaseLevel findByStartLevel(int startLevel);
 }

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

@@ -0,0 +1,16 @@
+package com.izouma.nineth.repo;
+
+
+import com.izouma.nineth.domain.SpaceObjectsInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface SpaceObjectsInfoRepo extends JpaRepository<SpaceObjectsInfo, Long> {
+
+    SpaceObjectsInfo findBySpaceId(Long spaceId);
+
+    List<SpaceObjectsInfo> findAllByUserId(Long userId);
+
+    List<SpaceObjectsInfo> findAllByUserIdAndIdNot(Long userId, Long id);
+}

+ 11 - 1
src/main/java/com/izouma/nineth/security/WebSecurityConfig.java

@@ -101,7 +101,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/user/code2openId").permitAll()
                 .antMatchers("/blindBoxItem/all").permitAll()
                 .antMatchers("/collection/recommend").permitAll()
-                .antMatchers("/order/**/status").permitAll()
+                .antMatchers("/order/**/status", "/giftOrder/*/status", "/mintOrder/*/status", "/rechargeOrder/*/status", "/auctionOrder/*/status").permitAll()
                 .antMatchers("/mintOrder/**/status").permitAll()
                 .antMatchers("/activity/all").permitAll()
                 .antMatchers("/activity/get/*").permitAll()
@@ -138,6 +138,16 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/user/faceAuthNotify/*").permitAll()
                 .antMatchers("/blindBoxItem/rare/*").permitAll()
                 .antMatchers("/user/synchronizationData").permitAll()
+                .antMatchers("/rarityLabel/label/*").permitAll()
+                .antMatchers("/collection/count/*").permitAll()
+                .antMatchers("/asset/*/metaPlayerRole").permitAll()
+                .antMatchers("/metaPlayerInfo/**").permitAll()
+                .antMatchers("/metaSpatialInfo/**").permitAll()
+                .antMatchers("/onOff/**").permitAll()
+                .antMatchers("/metaBonusScene/**").permitAll()
+                .antMatchers("/alipay/notify").permitAll()
+                .antMatchers("/metaPlayerWear/**").permitAll()
+                .antMatchers("/userHold/app/top").permitAll()
                 // all other requests need to be authenticated
                 .anyRequest().authenticated().and()
                 // make sure we use stateless session; session won't be used to

+ 32 - 36
src/main/java/com/izouma/nineth/service/AirDropService.java

@@ -20,6 +20,7 @@ import org.springframework.stereotype.Service;
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @Service
 @Slf4j
@@ -47,6 +48,8 @@ public class AirDropService {
     private AssetRepo               assetRepo;
     @Autowired
     private CollectionPrivilegeRepo collectionPrivilegeRepo;
+    @Autowired
+    private SysConfigService        sysConfigService;
 
     public Page<AirDrop> all(PageQuery pageQuery) {
         return airDropRepo
@@ -54,6 +57,7 @@ public class AirDropService {
     }
 
     public AirDrop create(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");
@@ -82,10 +86,14 @@ public class AirDropService {
             List<User> users = userRepo.findByIdInAndDelFalse(record.getTargets().stream()
                     .map(DropTarget::getUserId).collect(Collectors.toList()));
 
-            for (DropTarget target : record.getTargets()) {
+            Stream<DropTarget> stream = record.getTargets().stream();
+            if (parallel) {
+                stream = stream.parallel();
+            }
+            stream.forEach(target -> {
                 User user = users.stream().filter(u -> u.getId().equals(target.getUserId()))
                         .findFirst().orElse(null);
-                if (user == null) continue;
+                if (user == null) return;
                 try {
                     for (int i = 0; i < target.getNum(); i++) {
                         if (collection.getType() == CollectionType.BLIND_BOX) {
@@ -149,9 +157,10 @@ public class AirDropService {
                         collectionService.increaseSale(collection.getId(), 1);
                     }
                 } catch (Exception e) {
+                    e.printStackTrace();
                     log.error("空投出错", e);
                 }
-            }
+            });
         }
         return airDropRepo.save(record);
     }
@@ -161,6 +170,14 @@ public class AirDropService {
         drop(collectionId, userId, num, time);
     }
 
+    /**
+     * 空投(交易历史显示为购买)
+     *
+     * @param collectionId 藏品id
+     * @param userId       用户id
+     * @param num          空投数量
+     * @param time         购买时间
+     */
     public void drop(Long collectionId, Long userId, int num, LocalDateTime time) {
         Collection collection = collectionRepo.findById(collectionId)
                 .orElseThrow(new BusinessException("藏品不存在"));
@@ -183,8 +200,18 @@ public class AirDropService {
                 assetRepo.save(asset);
                 for (TokenHistory tokenHistory : tokenHistoryRepo
                         .findByTokenIdOrderByCreatedAtDesc(asset.getTokenId())) {
-                    tokenHistory.setCreatedAt(asset.getCreatedAt());
-                    tokenHistoryRepo.save(tokenHistory);
+                    new Thread(() -> {
+                        try {
+                            Thread.sleep(1000);
+                        } catch (InterruptedException e) {
+                            throw new RuntimeException(e);
+                        }
+                        tokenHistoryRepo.findById(tokenHistory.getId())
+                                .ifPresent(t -> {
+                                    t.setCreatedAt(time.plusSeconds((long) (Math.random() * 120)));
+                                    tokenHistoryRepo.save(t);
+                                });
+                    }).start();
                 }
                 log.info("空投成功{}/{} collectionId={}, userId={}", i + 1, num, collectionId, userId);
             }
@@ -192,35 +219,4 @@ public class AirDropService {
             log.error("空投出错", e);
         }
     }
-
-    public void drop(List<Collection> collections, String phone, LocalDateTime time) {
-//        List<Collection> collections = collectionRepo.findAllById(collectionId);
-        User user = userRepo.findByPhoneAndDelFalse(phone).orElseThrow(new BusinessException("用户不存在"));
-        try {
-            for (Collection collection : collections) {
-                Asset asset;
-                if (collection.getType() == CollectionType.BLIND_BOX) {
-                    BlindBoxItem winItem = collectionService.draw(user.getId(), collection.getId());
-                    asset = assetService.createAsset(winItem, user, 0L, collection.getPrice(), "出售",
-                            collectionService.getNextNumber(winItem), collection.getHoldDays(), false);
-                } else {
-                    asset = assetService.createAsset(collection, user, 0L, collection.getPrice(), "出售",
-                            collectionService.getNextNumber(collection), false);
-                }
-                assetRepo.flush();
-                tokenHistoryRepo.flush();
-
-                asset.setCreatedAt(time.plusSeconds((long) (Math.random() * 120)));
-                assetRepo.save(asset);
-
-                for (TokenHistory tokenHistory : tokenHistoryRepo
-                        .findByTokenIdOrderByCreatedAtDesc(asset.getTokenId())) {
-                    tokenHistory.setCreatedAt(asset.getCreatedAt());
-                    tokenHistoryRepo.save(tokenHistory);
-                }
-            }
-        } catch (Exception e) {
-            log.error("空投出错", e);
-        }
-    }
 }

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

@@ -41,6 +41,7 @@ public class AssetMintService {
     @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 1000))
     public void mint(Long assetId) {
         Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("asset不存在"));
+        if ((asset.getChainFlag() & 0b01) != 0b01) return;
         User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
         if (StringUtils.isEmpty(user.getNftAccount())) {
             NFTAccount account = nftService.createAccount(user.getUsername() + "_");
@@ -72,6 +73,7 @@ public class AssetMintService {
     @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 1000))
     public void hcMint(Long assetId) {
         Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("asset不存在"));
+        if ((asset.getChainFlag() & 0b10) != 0b10) return;
         User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
         if (StringUtils.isEmpty(user.getHcChainAddress())) {
             user.setHcChainAddress(hcChainService.createAccount(user.getId()));

+ 43 - 16
src/main/java/com/izouma/nineth/service/AssetService.java

@@ -41,7 +41,6 @@ import java.util.*;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 @Service
@@ -200,6 +199,7 @@ public class AssetService {
                 .orElseThrow(new BusinessException("盲盒不存在"));
         Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
                 .orElseThrow(new BusinessException("藏品不存在"));
+        winItem.setCollection(collection);
         Asset asset = Asset.create(winItem, user, holdDays);
         asset.setTokenId(TokenUtils.genTokenId());
         asset.setNumber(number);
@@ -287,6 +287,7 @@ public class AssetService {
                 .number(asset.getNumber())
                 .tags(new HashSet<>())
                 .prefixName(asset.getPrefixName())
+                .companyId(asset.getCompanyId())
                 .build();
         if (asset.getTags() != null) {
             collection.getTags().addAll(asset.getTags());
@@ -399,6 +400,7 @@ public class AssetService {
                 .number(asset.getNumber())
                 .tags(new HashSet<>())
                 .prefixName(asset.getPrefixName())
+                .companyId(asset.getCompanyId())
                 .build();
         if (asset.getTags() != null) {
             collection.getTags().addAll(asset.getTags());
@@ -870,8 +872,8 @@ public class AssetService {
         userRepo.addDestroyPoint(userId, 1);
     }
     public double getRoyalties(Long minterId, double royalties, Long userId) {
-        if (royalties == 3) {
-            return 3;
+        if (royalties == 2) {
+            return 2;
         }
         LongArrayConverter converter = new LongArrayConverter();
         String discountMinter = sysConfigService.getString("discount_minter");
@@ -881,7 +883,7 @@ public class AssetService {
             List<Long> collectionIds = converter.convertToEntityAttribute(discountCollection);
             Long assetId = assetRepo.findDiscount(userId, collectionIds);
             if (ObjectUtils.isNotEmpty(assetId)) {
-                return 3;
+                return 2;
             }
         }
         return royalties;
@@ -949,18 +951,43 @@ public class AssetService {
                 .build());
     }
 
-    @Async
-    public void giveBonus() {
-        List<TokenHistory> list = tokenHistoryRepo.findByOperationAndCreatedAtBefore("销毁", LocalDateTime.of(2022, 7, 23, 21, 0, 0));
-        list.parallelStream().forEach(tokenHistory -> {
-            String name = assetRepo.findFirstByTokenId(tokenHistory.getTokenId()).getName();
-            if (Pattern.matches(".*僵尸动物园SSR #.*", name)) {
-                log.info("SSR奖励 {}", name);
-                userBalanceService.modifyBalance(tokenHistory.getFromUserId(), new BigDecimal("1000"), BalanceType.BONUS, null, false, null);
-            } else if (Pattern.matches(".*僵尸动物园SR #.*", name)) {
-                log.info("SR奖励 {}", name);
-                userBalanceService.modifyBalance(tokenHistory.getFromUserId(), new BigDecimal("25"), BalanceType.BONUS, null, false, null);
-            }
+    public List<MetaPlayerRoleClassify> metaPlayerRole(Long userId) {
+        List<MetaPlayerRoleClassify> metaPlayerRoleClassifies = new ArrayList<>();
+        metaPlayerRoleClassifies.add(buildMetaPlayerRoleClassify(userId, "艾弗森", 1L));
+        metaPlayerRoleClassifies.add(buildMetaPlayerRoleClassify(userId, "开拓猿", 2L));
+        metaPlayerRoleClassifies.add(buildMetaPlayerRoleClassify(userId, "朋克", 3L));
+        metaPlayerRoleClassifies.add(buildMetaPlayerRoleClassify(userId, "MUGEN", 4L));
+        return metaPlayerRoleClassifies;
+
+    }
+
+    private MetaPlayerRoleClassify buildMetaPlayerRoleClassify(Long userId, String name, Long id) {
+        MetaPlayerRoleClassify metaPlayerRoleClassify = new MetaPlayerRoleClassify();
+        metaPlayerRoleClassify.setClassify(name);
+        List<MetaPlayerRole> metaPlayerRoleList;
+        if("MUGEN".equals(name)) {
+            metaPlayerRoleList = Arrays.asList(new MetaPlayerRole(id, "无限集团"),
+                    new MetaPlayerRole(id + 1, "苍茫雷道"),
+                    new MetaPlayerRole(id + 2, "炼炉天族"),
+                    new MetaPlayerRole(id + 3, "未央宗"),
+                    new MetaPlayerRole(id + 4, "八级门"),
+                    new MetaPlayerRole(id + 5, "人族"),
+                    new MetaPlayerRole(id + 6, "凤鸣寺"),
+                    new MetaPlayerRole(id + 7, "九州岛"));
+        } else {
+            metaPlayerRoleList = Collections.singletonList(new MetaPlayerRole(id, name));
+        }
+        metaPlayerRoleClassify.setMetaPlayerRoleList(build(userId, metaPlayerRoleList));
+        return metaPlayerRoleClassify;
+    }
+
+    private List<MetaPlayerRole> build (Long userId, List<MetaPlayerRole> metaPlayerRoleList) {
+        metaPlayerRoleList.forEach(metaPlayerRole -> {
+            metaPlayerRole.setType(UserHoldTypeEnum.ASSET);
+            metaPlayerRole.setAddress("https://www.raex.vip/9th/productSearch?search=" + metaPlayerRole.getName() + "&source=TRANSFER");
+            List<Asset> assets = assetRepo.findAllByUserIdAndNameLike(userId, "%" + metaPlayerRole.getName() + "%");
+            metaPlayerRole.setHold(CollectionUtils.isNotEmpty(assets));
         });
+        return metaPlayerRoleList;
     }
 }

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

@@ -526,7 +526,7 @@ public class AuctionOrderService {
 
                     act.setStatus(AuctionStatus.PASS);
                     auctionActivityRepo.save(act);
-                    log.info("拍卖定时任务流拍{}", act.getId());
+                    //log.info("拍卖定时任务流拍{}", act.getId());
 
                     if (AuctionSource.TRANSFER.equals(act.getSource())) {
                         //改回资产状态

+ 71 - 50
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -31,6 +31,7 @@ import org.springframework.cache.annotation.Cacheable;
 import org.springframework.core.env.Environment;
 import org.springframework.data.domain.*;
 import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.redis.core.BoundHashOperations;
 import org.springframework.data.redis.core.BoundValueOperations;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.TaskScheduler;
@@ -45,6 +46,8 @@ 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;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -369,7 +372,7 @@ public class CollectionService {
     }
 
     @Transactional
-    public Collection createBlindBox(CreateBlindBox createBlindBox) {
+    public Collection createBlindBox(CreateBlindBox createBlindBox) throws Exception {
         Collection blindBox = createBlindBox.getBlindBox();
         if (blindBox.getId() != null) {
             throw new BusinessException("无法完成此操作");
@@ -397,26 +400,29 @@ public class CollectionService {
         blindBox.setStock(blindBox.getTotal());
         blindBox.setSale(0);
         collectionRepo.save(blindBox);
-        createBlindBox.getItems().stream().parallel().forEach(item -> {
-            Collection collection = list.stream().filter(i -> i.getId().equals(item.getCollectionId())).findAny()
-                    .orElseThrow(new BusinessException("所选藏品不存在"));
-            decreaseStock(collection.getId(), item.getTotal());
-            BlindBoxItem blindBoxItem = new BlindBoxItem();
-            BeanUtils.copyProperties(collection, blindBoxItem);
-            blindBoxItem.setId(null);
-            blindBoxItem.setOasisId(collection.getOasisId());
-            blindBoxItem.setRaceId(collection.getRaceId());
-            blindBoxItem.setGroupId(collection.getGroupId());
-            blindBoxItem.setItemId(collection.getItemId());
-            blindBoxItem.setCollectionId(item.getCollectionId());
-            blindBoxItem.setSale(0);
-            blindBoxItem.setTotal(item.getTotal());
-            blindBoxItem.setStock(item.getTotal());
-            blindBoxItem.setRare(item.isRare());
-            blindBoxItem.setBlindBoxId(blindBox.getId());
-            blindBoxItemRepo.saveAndFlush(blindBoxItem);
-            log.info("createBlindBoxItemSuccess" + blindBoxItem.getId());
-        });
+        new ForkJoinPool(128).submit(() -> {
+            createBlindBox.getItems().stream().parallel().forEach(item -> {
+                Collection collection = list.stream().filter(i -> i.getId().equals(item.getCollectionId()))
+                        .findFirst().get();
+                decreaseStock(collection.getId(), item.getTotal());
+                BlindBoxItem blindBoxItem = new BlindBoxItem();
+                BeanUtils.copyProperties(collection, blindBoxItem);
+                blindBoxItem.setId(null);
+                blindBoxItem.setOasisId(collection.getOasisId());
+                blindBoxItem.setCollectionId(item.getCollectionId());
+                blindBoxItem.setSale(0);
+                blindBoxItem.setTotal(item.getTotal());
+                blindBoxItem.setStock(item.getTotal());
+                blindBoxItem.setRare(item.isRare());
+                blindBoxItem.setRaceId(collection.getRaceId());
+                blindBoxItem.setGroupId(collection.getGroupId());
+                blindBoxItem.setItemId(collection.getItemId());
+                blindBoxItem.setBlindBoxId(blindBox.getId());
+                blindBoxItem.setCompanyId(collection.getCompanyId());
+                blindBoxItemRepo.saveAndFlush(blindBoxItem);
+                log.info("createBlindBoxItemSuccess" + blindBoxItem.getId());
+            });
+        }).get();
         return blindBox;
     }
 
@@ -436,15 +442,21 @@ public class CollectionService {
                 .build());
     }
 
-    public synchronized BlindBoxItem draw(Long userId, Long collectionId) {
+    public BlindBoxItem draw(Long userId, Long collectionId) {
+        log.info("blindBoxDraw, userId={}, collectionId={}", userId, collectionId);
         List<BlindBoxItem> items = blindBoxItemRepo.findByBlindBoxId(collectionId);
+        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(RedisKeys.DRAW_BLIND_BOX + collectionId);
+        Map<Object, Object> entries = operations.entries();
 
         Map<BlindBoxItem, Range<Integer>> randomRange = new HashMap<>();
         int c = 0, sum = 0;
         for (BlindBoxItem item : items) {
-            randomRange.put(item, Range.between(c, c + item.getStock()));
-            c += item.getStock();
-            sum += item.getStock();
+            if (item.getStock() > 0) {
+                int stock = Optional.ofNullable(entries.get(item.getCollectionId() + "")).map(i -> (int) i).orElse(item.getStock());
+                randomRange.put(item, Range.between(c, c + item.getStock()));
+                c += stock;
+                sum += stock;
+            }
         }
 
         int retry = 0;
@@ -453,34 +465,33 @@ public class CollectionService {
             retry++;
             if (userId == 3453161L || userId == 7194L || userId == 134613L) {
                 winItem = items.stream().filter(i -> i.getName().contains("SSR") && i.getStock() > 0).findFirst().orElse(null);
-                if (winItem != null) {
-                    continue;
-                }
             }
-            int rand = RandomUtils.nextInt(0, sum + 1);
-            for (Map.Entry<BlindBoxItem, Range<Integer>> entry : randomRange.entrySet()) {
-                BlindBoxItem item = entry.getKey();
-                Range<Integer> range = entry.getValue();
-                if (rand >= range.getMinimum() && rand < range.getMaximum()) {
-                    int total = items.stream().filter(i -> !i.isRare())
-                            .mapToInt(BlindBoxItem::getTotal).sum();
-                    int stock = items.stream().filter(i -> !i.isRare())
-                            .mapToInt(BlindBoxItem::getStock).sum();
-                    if (item.isRare()) {
-                        double nRate = stock / (double) total;
-                        double rRate = (item.getStock() - 1) / (double) item.getTotal();
-
-                        if (Math.abs(nRate - rRate) < (1 / (double) item.getTotal()) || retry > 1 || rRate == 0) {
-                            if (!(nRate > 0.1 && item.getStock() == 1)) {
-                                winItem = item;
+            if (winItem == null) {
+                int rand = RandomUtils.nextInt(0, sum + 1);
+                for (Map.Entry<BlindBoxItem, Range<Integer>> entry : randomRange.entrySet()) {
+                    BlindBoxItem item = entry.getKey();
+                    Range<Integer> range = entry.getValue();
+                    if (rand >= range.getMinimum() && rand < range.getMaximum()) {
+                        int total = items.stream().filter(i -> !i.isRare())
+                                .mapToInt(BlindBoxItem::getTotal).sum();
+                        int stock = items.stream().filter(i -> !i.isRare())
+                                .mapToInt(BlindBoxItem::getStock).sum();
+                        if (item.isRare()) {
+                            double nRate = stock / (double) total;
+                            double rRate = (item.getStock() - 1) / (double) item.getTotal();
+
+                            if (Math.abs(nRate - rRate) < (1 / (double) item.getTotal()) || retry > 1 || rRate == 0) {
+                                if (!(nRate > 0.1 && item.getStock() == 1)) {
+                                    winItem = item;
+                                }
                             }
-                        }
-                    } else {
-                        double nRate = (stock - 1) / (double) total;
-                        double rRate = item.getStock() / (double) item.getTotal();
+                        } else {
+                            double nRate = (stock - 1) / (double) total;
+                            double rRate = item.getStock() / (double) item.getTotal();
 
-                        if (Math.abs(nRate - rRate) < 0.2 || retry > 1 || nRate == 0) {
-                            winItem = item;
+                            if (Math.abs(nRate - rRate) < 0.2 || retry > 1 || nRate == 0) {
+                                winItem = item;
+                            }
                         }
                     }
                 }
@@ -489,6 +500,16 @@ public class CollectionService {
             if (retry > 100 && winItem == null) {
                 throw new BusinessException("盲盒抽卡失败");
             }
+
+            if (winItem != null) {
+                operations.putIfAbsent(winItem.getCollectionId() + "", winItem.getStock());
+                int stock = Math.toIntExact(operations.increment(winItem.getCollectionId() + "", -1));
+                if (stock < 0) {
+                    log.info("over draw {} {}", stock, winItem.getCollectionId());
+                    operations.increment(winItem.getCollectionId() + "", 1);
+                    winItem = null;
+                }
+            }
         }
 //        winItem.setStock(winItem.getStock() - 1);
 //        winItem.setSale(winItem.getSale() + 1);

+ 0 - 40
src/main/java/com/izouma/nineth/service/GiftOrderService.java

@@ -232,46 +232,6 @@ public class GiftOrderService {
 
     }
 
-    public void payOrderAlipay(Long id, Model model) {
-        try {
-            GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
-
-            if (order.getStatus() != OrderStatus.NOT_PAID) {
-                throw new BusinessException("订单状态错误");
-            }
-
-            JSONObject bizContent = new JSONObject();
-            bizContent.put("notifyUrl", alipayProperties.getNotifyUrl());
-            bizContent.put("returnUrl", alipayProperties.getReturnUrl());
-            bizContent.put("out_trade_no", String.valueOf(snowflakeIdWorker.nextId()));
-            bizContent.put("total_amount", order.getGasPrice().stripTrailingZeros().toPlainString());
-            bizContent.put("disable_pay_channels", "pcredit,creditCard");
-            if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
-                // 测试环境设为1分
-                bizContent.put("total_amount", "0.01");
-            }
-            bizContent.put("subject", "转赠GAS费");
-            bizContent.put("product_code", "QUICK_WAP_PAY");
-            JSONObject body = new JSONObject();
-            body.put("action", "payGiftOrder");
-            body.put("userId", order.getUserId());
-            body.put("orderId", order.getId());
-            bizContent.put("body", body.toJSONString());
-
-            AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
-            alipayRequest.setReturnUrl(alipayProperties.getReturnUrl());
-            alipayRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
-            alipayRequest.setBizContent(JSON.toJSONString(bizContent));
-
-            String form = alipayClient.pageExecute(alipayRequest).getBody();
-            model.addAttribute("form", form);
-        } catch (BusinessException err) {
-            model.addAttribute("errMsg", err.getError());
-        } catch (Exception e) {
-            model.addAttribute("errMsg", e.getMessage());
-        }
-    }
-
     public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException {
         GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
         if (order.getStatus() != OrderStatus.NOT_PAID) {

+ 165 - 0
src/main/java/com/izouma/nineth/service/MetaPlayerInfoService.java

@@ -0,0 +1,165 @@
+package com.izouma.nineth.service;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.izouma.nineth.domain.Asset;
+import com.izouma.nineth.domain.MetaItem;
+import com.izouma.nineth.domain.SpaceObjectsInfo;
+import com.izouma.nineth.dto.BuildingDTO;
+import com.izouma.nineth.dto.BuildingPosDTO;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.enums.MetaItemEnum;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.AssetRepo;
+import com.izouma.nineth.repo.MetaItemRepo;
+import com.izouma.nineth.repo.SpaceObjectsInfoRepo;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+public class MetaPlayerInfoService {
+
+    private SpaceObjectsInfoRepo spaceObjectsInfoRepo;
+
+    private AssetRepo assetRepo;
+
+    private MetaItemRepo metaItemRepo;
+
+    public MetaRestResult<SpaceObjectsInfo> metaSpaceInfo(Long userId, Long spaceId) {
+        SpaceObjectsInfo spaceObjectsInfo = spaceObjectsInfoRepo.findBySpaceId(spaceId);
+        if (Objects.isNull(spaceObjectsInfo)) {
+            return MetaRestResult.returnError(String.format("不存在空间id:%S的空间信息", spaceId));
+        }
+        spaceObjectsInfo.setPersonal(spaceObjectsInfo.getUserId().equals(userId));
+        // 如果是查询个人空间物品信息,统计用户剩余物品信息
+        try {
+            if (spaceObjectsInfo.isPersonal()) {
+                List<SpaceObjectsInfo> spaceObjectsInfos = spaceObjectsInfoRepo.findAllByUserId(userId);
+                spaceObjectsInfo.setBuildingList(buildingList(userId, spaceObjectsInfos));
+            }
+        } catch (Exception e) {
+            return MetaRestResult.returnError(e.getMessage());
+        }
+        return MetaRestResult.returnSuccess(spaceObjectsInfo);
+    }
+
+    public MetaRestResult<SpaceObjectsInfo> updateMetaPersonalSpace(SpaceObjectsInfo spaceObjectsInfo) {
+        if(Objects.isNull(spaceObjectsInfo.getId())) {
+            return MetaRestResult.returnError("参数不合法,缺少空间物品信息记录id");
+        }
+        SpaceObjectsInfo dbSpaceObjectsInfo = spaceObjectsInfoRepo.findById(spaceObjectsInfo.getId()).orElse(null);
+        if(Objects.isNull(dbSpaceObjectsInfo)) {
+            return MetaRestResult.returnError(String.format("操作失败:不存在id[%S]的空间物品数据", spaceObjectsInfo.getSpaceId()));
+        }
+        List<BuildingPosDTO> buildingPosList = spaceObjectsInfo.getBuildingPosList();
+        if (CollectionUtil.isEmpty(buildingPosList)) {
+            dbSpaceObjectsInfo.setBuildingPosList(null);
+            return MetaRestResult.returnSuccess(spaceObjectsInfoRepo.save(dbSpaceObjectsInfo));
+        }
+        Map<Long, Integer> map = new HashMap<>();
+        // 统计要修改的个人空间各物品数据
+        buildingPosList.forEach(buildingPosDTO -> {
+            Long key = buildingPosDTO.getItemId();
+            if (map.containsKey(key)) {
+                map.put(key, map.get(key) + 1);
+            } else {
+                map.put(key, 1);
+            }
+        });
+        List<SpaceObjectsInfo> spaceObjectsInfos = spaceObjectsInfoRepo.findAllByUserIdAndIdNot(dbSpaceObjectsInfo.getUserId(), dbSpaceObjectsInfo.getId());
+        List<BuildingDTO> buildingDTOS = buildingList(dbSpaceObjectsInfo.getUserId(), spaceObjectsInfos);
+        // 查询用户所有已经编辑过的物品id
+        Set<Long> keys = map.keySet();
+        if (!check(keys, buildingDTOS)) {
+            return MetaRestResult.returnError("当前用户物品数据异常:用户未持有相关藏品,个人空间中有已编辑藏品信息");
+        }
+        // 比较个人空间拥有的各物品最大数量
+        try {
+            keys.forEach(key -> {
+                buildingDTOS.forEach(buildingDTO -> {
+                    if (key.equals(buildingDTO.getItmeId())) {
+                        if (map.get(key) > buildingDTO.getCount()) {
+                            throw new BusinessException("操作失败:当前用户修改该物品数量大于用户持有最大数量");
+                        }
+                    }
+                });
+            });
+        } catch (Exception e) {
+            return MetaRestResult.returnError(e.getMessage());
+        }
+        dbSpaceObjectsInfo.setBuildingPosList(spaceObjectsInfo.getBuildingPosList());
+        return MetaRestResult.returnSuccess("修改成功", spaceObjectsInfoRepo.save(dbSpaceObjectsInfo));
+    }
+
+    private List<BuildingDTO> buildingList(Long userId, List<SpaceObjectsInfo> spaceObjectsInfos) {
+        List<BuildingDTO> buildingList = new ArrayList<>();
+        List<MetaItem> metaItems = metaItemRepo.findAllByType(MetaItemEnum.BUILDING);
+        // 统计该用户所有物品信息
+        metaItems.forEach(metaItem -> {
+            List<Asset> assets = assetRepo.findAllByUserIdAndNameLike(userId, "%" + metaItem.getName() + "%");
+            if (CollectionUtil.isNotEmpty(assets)) {
+                buildingList.add(new BuildingDTO(metaItem.getId(), metaItem.getName(), assets.size()));
+            }
+        });
+        // 如果该该用户没有空间信息直接返回 buildingList
+        if (CollectionUtil.isEmpty(spaceObjectsInfos)) {
+            return buildingList;
+        }
+        // 统计该用户已经编辑过的物品信息
+        Map<Long, Integer> map = new HashMap<>();
+        spaceObjectsInfos.forEach(spaceObjectsInfo -> {
+            if (CollectionUtil.isNotEmpty(spaceObjectsInfo.getBuildingPosList())) {
+                spaceObjectsInfo.getBuildingPosList().forEach(buildingPosDTO -> {
+                    Long key = buildingPosDTO.getItemId();
+                    if (map.containsKey(key)) {
+                        map.put(key, map.get(key) + 1);
+                    } else {
+                        map.put(key, 1);
+                    }
+                });
+            }
+        });
+        if(CollectionUtil.isEmpty(buildingList)) {
+            // 如过buildingList为空,且用户有编辑过的物品返回错误
+            if (CollectionUtil.isNotEmpty(map)) {
+                throw new BusinessException("当前用户物品数据异常:用户未持有相关藏品,个人空间中有已编辑藏品信息");
+            }
+            // 如过buildingList为空,且用户没有编辑过物品 正常返回buildingList
+            return buildingList;
+        }
+        // 如过buildingList不为为空,且用户没有编辑过物品,返回buildingList
+        if (CollectionUtil.isEmpty(map)) {
+            return buildingList;
+        }
+        // 查询用户所有已经编辑过的物品id
+        Set<Long> keys = map.keySet();
+        if (!check(keys, buildingList)) {
+            throw new BusinessException("当前用户物品数据异常:用户未持有相关藏品,个人空间中有已编辑藏品信息");
+        }
+        // 查询剩余未编辑物品信息
+        buildingList.forEach(buildingDTO -> {
+            keys.forEach(key -> {
+                if (buildingDTO.getItmeId().equals(key)) {
+                    int newCount = buildingDTO.getCount() - map.get(key);
+                    // 如果用户剩余物品数量为负数,返回失败
+                    if (newCount < 0) {
+                        throw new BusinessException("当前用户物品数据异常:个人空间中编辑存在物品编辑数量大于持有量");
+                    }
+                    buildingDTO.setCount(newCount);
+                }
+            });
+        });
+        // 返回用户剩余物品信息
+        return buildingList;
+    }
+
+    public boolean check(Set<Long> keys, List<BuildingDTO> buildingList) {
+        // 查询用户拥有的所有的物品id
+        List<Long> all = buildingList.stream().map(BuildingDTO::getItmeId).collect(Collectors.toList());
+        // 如果用户编辑过未拥有的物品返回错误
+        return new HashSet<>(all).containsAll(keys);
+    }
+}

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

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

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

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

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

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

+ 142 - 0
src/main/java/com/izouma/nineth/service/OrderPayService.java

@@ -1,6 +1,11 @@
 package com.izouma.nineth.service;
 
 import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.request.AlipayTradePrecreateRequest;
+import com.alipay.api.response.AlipayTradePrecreateResponse;
+import com.izouma.nineth.config.AlipayProperties;
 import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.domain.*;
@@ -18,8 +23,12 @@ import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
 
 import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
 import java.util.Map;
 import java.util.Objects;
@@ -53,6 +62,8 @@ public class OrderPayService {
     private final AuctionOrderRepo    auctionOrderRepo;
     private final AuctionOrderService auctionOrderService;
     private final IdentityAuthRepo    identityAuthRepo;
+    private final AlipayClient        alipayClient;
+    private final AlipayProperties    alipayProperties;
 
     public static void setPayChannel(String payChannel) {
         log.info("set pay channel {}", payChannel);
@@ -79,6 +90,52 @@ public class OrderPayService {
         throw new BusinessException(Constants.PAY_ERR_MSG);
     }
 
+    private String aliRequest(Long orderId, BigDecimal amount, String subject, String type) {
+        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
+        request.setNotifyUrl(alipayProperties.getNotifyUrl());
+        JSONObject bizContent = new JSONObject();
+        bizContent.put("out_trade_no", orderId + "");
+        bizContent.put("total_amount", amount);
+        bizContent.put("subject", subject);
+        JSONObject body = new JSONObject();
+        body.put("type", type);
+        body.put("orderId", orderId);
+        bizContent.put("body", body.toString());
+
+        request.setBizContent(bizContent.toString());
+        AlipayTradePrecreateResponse response = null;
+        try {
+            response = alipayClient.execute(request);
+        } catch (AlipayApiException e) {
+            e.printStackTrace();
+            throw new BusinessException(Constants.PAY_ERR_MSG, e.getErrMsg());
+        }
+        if (response.isSuccess()) {
+            return response.getQrCode();
+        } else {
+            throw new BusinessException(response.getSubMsg());
+        }
+    }
+
+    @Cacheable(value = "payOrder", key = "'order#'+#orderId")
+    public String payOrderAli(Long orderId) {
+        Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != OrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+
+        String qrCode = aliRequest(orderId, order.getTotalPrice(), order.getName(), Constants.OrderNotifyType.ORDER);
+
+        String ua = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("User-Agent");
+        if (ua.toLowerCase().contains("micromessenger")) {
+            return "/static/wx_alipay_bridge.html?payUrl=" + URLEncoder.encode(Constants.ALIPAY_URL_SCHEME + qrCode, StandardCharsets.UTF_8)
+                    + "&orderId=" + orderId + "&type=order&returnUrl="
+                    + URLEncoder.encode(generalProperties.getHost() + "/9th/store", StandardCharsets.UTF_8);
+        } else {
+            return Constants.ALIPAY_URL_SCHEME + qrCode;
+        }
+    }
+
     @Cacheable(value = "payOrder", key = "'order#'+#orderId")
     public String payOrderQuick(Long orderId) {
         Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
@@ -166,6 +223,25 @@ public class OrderPayService {
         throw new BusinessException(Constants.PAY_ERR_MSG);
     }
 
+    @Cacheable(value = "payOrder", key = "'gift#'+#orderId")
+    public String payGiftAli(Long orderId) {
+        GiftOrder order = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != OrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+
+        String qrCode = aliRequest(orderId, order.getGasPrice(), "转赠", Constants.OrderNotifyType.GIFT);
+
+        String ua = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("User-Agent");
+        if (ua.toLowerCase().contains("micromessenger")) {
+            return "/static/wx_alipay_bridge.html?payUrl=" + URLEncoder.encode(Constants.ALIPAY_URL_SCHEME + qrCode, StandardCharsets.UTF_8)
+                    + "&orderId=" + orderId + "&type=gift&returnUrl="
+                    + URLEncoder.encode(generalProperties.getHost() + "/9th/store", StandardCharsets.UTF_8);
+        } else {
+            return Constants.ALIPAY_URL_SCHEME + qrCode;
+        }
+    }
+
     @Cacheable(value = "payOrder", key = "'gift#'+#orderId")
     public String payGiftQuick(Long orderId) {
         GiftOrder order = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
@@ -237,6 +313,25 @@ public class OrderPayService {
         throw new BusinessException("绿洲宇宙冷却系统已启动,请稍后支付");
     }
 
+    @Cacheable(value = "payOrder", key = "'mintOrder#'+#orderId")
+    public String payMintAli(Long orderId) {
+        MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+
+        String qrCode = aliRequest(orderId, order.getGasPrice(), "铸造活动:" + order.getMintActivityId(), Constants.OrderNotifyType.MINT);
+
+        String ua = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("User-Agent");
+        if (ua.toLowerCase().contains("micromessenger")) {
+            return "/static/wx_alipay_bridge.html?payUrl=" + URLEncoder.encode(Constants.ALIPAY_URL_SCHEME + qrCode, StandardCharsets.UTF_8)
+                    + "&orderId=" + orderId + "&type=mintOrder&returnUrl="
+                    + URLEncoder.encode(generalProperties.getHost() + "/9th/store", StandardCharsets.UTF_8);
+        } else {
+            return Constants.ALIPAY_URL_SCHEME + qrCode;
+        }
+    }
+
     @Cacheable(value = "payOrder", key = "'mintOrder#'+#orderId")
     public String payMintQuick(Long orderId) {
         MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
@@ -330,6 +425,34 @@ public class OrderPayService {
         throw new BusinessException("绿洲宇宙冷却系统已启动,请稍后支付");
     }
 
+    public String payRechargeAli(Long userId, BigDecimal amount) {
+        BigDecimal minAmount = sysConfigService.getBigDecimal("min_recharge_amount");
+        if (amount.compareTo(minAmount) < 0) {
+            throw new BusinessException("充值金额不能小于" + minAmount);
+        }
+        if (amount.compareTo(new BigDecimal("50000")) > 0) {
+            throw new BusinessException("充值金额不能大于50000");
+        }
+        RechargeOrder order = RechargeOrder.builder()
+                .id(snowflakeIdWorker.nextId())
+                .userId(userId)
+                .amount(amount)
+                .status(OrderStatus.NOT_PAID)
+                .build();
+        rechargeOrderRepo.save(order);
+
+        String qrCode = aliRequest(order.getId(), order.getAmount(), "余额充值", Constants.OrderNotifyType.RECHARGE);
+
+        String ua = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("User-Agent");
+        if (ua.toLowerCase().contains("micromessenger")) {
+            return "/static/wx_alipay_bridge.html?payUrl=" + URLEncoder.encode(Constants.ALIPAY_URL_SCHEME + qrCode, StandardCharsets.UTF_8)
+                    + "&orderId=" + order.getId() + "&type=recharge&returnUrl="
+                    + URLEncoder.encode(generalProperties.getHost() + "/9th/store", StandardCharsets.UTF_8);
+        } else {
+            return Constants.ALIPAY_URL_SCHEME + qrCode;
+        }
+    }
+
     public Map<String, Object> rechargeAgreement(Long userId, BigDecimal amount, String bindCardId) {
         BigDecimal minAmount = sysConfigService.getBigDecimal("min_recharge_amount");
         if (amount.compareTo(minAmount) < 0) {
@@ -474,6 +597,25 @@ public class OrderPayService {
         throw new BusinessException("绿洲宇宙冷却系统已启动,请稍后支付");
     }
 
+    @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
+    public String payAuctionAli(Long orderId) {
+        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+
+        String qrCode = aliRequest(orderId, order.getTotalPrice(), "拍卖:" + order.getName(), Constants.OrderNotifyType.AUCTION);
+
+        String ua = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("User-Agent");
+        if (ua.toLowerCase().contains("micromessenger")) {
+            return "/static/wx_alipay_bridge.html?payUrl=" + URLEncoder.encode(Constants.ALIPAY_URL_SCHEME + qrCode, StandardCharsets.UTF_8)
+                    + "&orderId=" + orderId + "&type=auctionOrder&returnUrl="
+                    + URLEncoder.encode(generalProperties.getHost() + "/9th/store", StandardCharsets.UTF_8);
+        } else {
+            return Constants.ALIPAY_URL_SCHEME + qrCode;
+        }
+    }
+
     @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
     public String payAuctionQuick(Long orderId) {
         AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));

+ 1 - 40
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -376,6 +376,7 @@ public class OrderService {
                     .countId(collection.getCountId())
                     .vip(vip)
                     .vipPoint(usePoint)
+                    .companyId(collection.getCompanyId())
                     .build();
             if (coupon != null) {
                 coupon.setUsed(true);
@@ -460,46 +461,6 @@ public class OrderService {
         return map;
     }
 
-    public void payOrderAlipay(Long id, Model model) {
-        try {
-            Order order = orderRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("订单不存在"));
-
-            if (order.getStatus() != OrderStatus.NOT_PAID) {
-                throw new BusinessException("订单状态错误");
-            }
-
-            JSONObject bizContent = new JSONObject();
-            bizContent.put("notifyUrl", alipayProperties.getNotifyUrl());
-            bizContent.put("returnUrl", alipayProperties.getReturnUrl());
-            bizContent.put("out_trade_no", String.valueOf(snowflakeIdWorker.nextId()));
-            bizContent.put("total_amount", order.getTotalPrice().stripTrailingZeros().toPlainString());
-            bizContent.put("disable_pay_channels", "pcredit,creditCard");
-            if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
-                // 测试环境设为1分
-                bizContent.put("total_amount", "0.01");
-            }
-            bizContent.put("subject", order.getName());
-            bizContent.put("product_code", "QUICK_WAP_PAY");
-            JSONObject body = new JSONObject();
-            body.put("action", "payOrder");
-            body.put("userId", order.getUserId());
-            body.put("orderId", order.getId());
-            bizContent.put("body", body.toJSONString());
-
-            AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
-            alipayRequest.setReturnUrl(alipayProperties.getReturnUrl());
-            alipayRequest.setNotifyUrl(alipayProperties.getNotifyUrl());
-            alipayRequest.setBizContent(JSON.toJSONString(bizContent));
-
-            String form = alipayClient.pageExecute(alipayRequest).getBody();
-            model.addAttribute("form", form);
-        } catch (BusinessException err) {
-            model.addAttribute("errMsg", err.getError());
-        } catch (Exception e) {
-            model.addAttribute("errMsg", e.getMessage());
-        }
-    }
-
     public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException {
         Order order = orderRepo.findByIdAndDelFalse(id).orElseThrow(new BusinessException("订单不存在"));
         if (order.getStatus() != OrderStatus.NOT_PAID) {

+ 8 - 0
src/main/java/com/izouma/nineth/service/SysConfigService.java

@@ -251,6 +251,14 @@ public class SysConfigService {
                     .options("payEase,sandPay")
                     .build());
         }
+        if (list.stream().noneMatch(i -> i.getName().equals("parallel_airdrop"))) {
+            sysConfigRepo.save(SysConfig.builder()
+                    .name("parallel_airdrop")
+                    .desc("并发空投")
+                    .type(SysConfig.ValueType.BOOLEAN)
+                    .value("1")
+                    .build());
+        }
         SearchMode searchMode = SearchMode.valueOf(sysConfigRepo.findByName("default_search_mode").get().getValue());
         JpaUtils.setDefaultSearchMode(searchMode);
 

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

@@ -359,10 +359,10 @@ public class UserService {
         }
         User user = userRepo.findByUsernameAndDelFalse(username).orElseThrow(new BusinessException("账号或密码错误"));
 
-//        if (StringUtils.isEmpty(user.getPassword())
-//                || !passwordEncoder.matches(password, user.getPassword())) {
-//            throw new BusinessException("账号或密码错误");
-//        }
+        if (StringUtils.isEmpty(user.getPassword())
+                || !passwordEncoder.matches(password, user.getPassword())) {
+            throw new BusinessException("账号或密码错误");
+        }
 
         return user;
     }

+ 91 - 0
src/main/java/com/izouma/nineth/web/AlipayNotifyController.java

@@ -0,0 +1,91 @@
+package com.izouma.nineth.web;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.izouma.nineth.config.AlipayProperties;
+import com.izouma.nineth.config.GeneralProperties;
+import com.izouma.nineth.enums.PayMethod;
+import com.izouma.nineth.event.OrderNotifyEvent;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.MapUtils;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+@RestController
+@Slf4j
+@AllArgsConstructor
+public class AlipayNotifyController {
+    private AlipayProperties  alipayProperties;
+    private GeneralProperties generalProperties;
+    private RocketMQTemplate  rocketMQTemplate;
+
+    @PostMapping("/alipay/notify")
+    @ResponseBody
+    public String notify(HttpServletRequest request) throws AlipayApiException {
+        Map<String, String> params = new HashMap<>();
+        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
+        for (Map.Entry<String, String[]> entry : entrySet) {
+            String name = entry.getKey();
+            String[] values = entry.getValue();
+            int valLen = values.length;
+
+            if (valLen == 1) {
+                params.put(name, values[0]);
+            } else if (valLen > 1) {
+                StringBuilder sb = new StringBuilder();
+                for (String val : values) {
+                    sb.append(",").append(val);
+                }
+                params.put(name, sb.toString().substring(1));
+            } else {
+                params.put(name, "");
+            }
+        }
+        log.info("支付宝回调 {}", JSON.toJSONString(params, true));
+        AlipaySignature.rsaCheckV1(params, alipayProperties.getAliPublicKey(), "UTF-8", "RSA2");
+        if (MapUtils.getString(params, "trade_status").equals("TRADE_SUCCESS")) {
+            JSONObject body = JSON.parseObject(params.get("body"));
+            String type = body.getString("type");
+            Long orderId = body.getLong("orderId");
+            String tradeNo = params.get("trade_no");
+            switch (type) {
+                case "order":
+                    rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                            new OrderNotifyEvent(orderId, PayMethod.ALIPAY,
+                                    tradeNo, System.currentTimeMillis()));
+                    break;
+                case "gift":
+                    rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                            new OrderNotifyEvent(orderId, PayMethod.ALIPAY, tradeNo,
+                                    System.currentTimeMillis(), OrderNotifyEvent.TYPE_GIFT_ORDER));
+                    break;
+                case "mintOrder":
+                    rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                            new OrderNotifyEvent(orderId, PayMethod.ALIPAY, tradeNo,
+                                    System.currentTimeMillis(), OrderNotifyEvent.TYPE_MINT_ORDER));
+                    break;
+                case "recharge":
+                    rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                            new OrderNotifyEvent(orderId, PayMethod.ALIPAY, tradeNo,
+                                    System.currentTimeMillis(), OrderNotifyEvent.TYPE_RECHARGE));
+                    break;
+                case "auctionOrder":
+                    rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                            new OrderNotifyEvent(orderId, PayMethod.ALIPAY, tradeNo,
+                                    System.currentTimeMillis(), OrderNotifyEvent.TYPE_AUCTION_ORDER));
+                    break;
+            }
+        }
+        return "success";
+    }
+}

+ 11 - 13
src/main/java/com/izouma/nineth/web/AssetController.java

@@ -4,9 +4,7 @@ import com.fasterxml.jackson.annotation.JsonView;
 import com.izouma.nineth.TokenHistory;
 import com.izouma.nineth.domain.Asset;
 import com.izouma.nineth.domain.GiftOrder;
-import com.izouma.nineth.dto.AssetDTO;
-import com.izouma.nineth.dto.PageQuery;
-import com.izouma.nineth.dto.UserHistory;
+import com.izouma.nineth.dto.*;
 import com.izouma.nineth.enums.CollectionType;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.AssetRepo;
@@ -37,11 +35,11 @@ import java.util.concurrent.ExecutionException;
 @RequestMapping("/asset")
 @AllArgsConstructor
 public class AssetController extends BaseController {
-    private AssetService     assetService;
-    private AssetRepo        assetRepo;
+    private AssetService assetService;
+    private AssetRepo assetRepo;
     private GiftOrderService giftOrderService;
-    private OrderRepo        orderRepo;
-    private CacheService     cacheService;
+    private OrderRepo orderRepo;
+    private CacheService cacheService;
 
     //@PreAuthorize("hasRole('ADMIN')")
 //    @PostMapping("/save")
@@ -193,9 +191,10 @@ public class AssetController extends BaseController {
 
     @ApiOperation("销毁")
     @PostMapping("/destroy")
-    public void destroy(@RequestParam Long id ,@RequestParam String tradeCode) {
-        assetService.destroy(id, SecurityUtils.getAuthenticatedUser().getId(),tradeCode);
+    public void destroy(@RequestParam Long id, @RequestParam String tradeCode) {
+        assetService.destroy(id, SecurityUtils.getAuthenticatedUser().getId(), tradeCode);
     }
+
     @ApiOperation("开盲盒")
     @PostMapping("/open")
     public void open(@RequestParam Long id) {
@@ -229,10 +228,9 @@ public class AssetController extends BaseController {
         assetService.lockAsset(SecurityUtils.getAuthenticatedUser().getId(), assetId, duration);
     }
 
-    @PreAuthorize("hasRole('ADMIN')")
-    @GetMapping("/giveBonus")
-    public void giveBonus() {
-        assetService.giveBonus();
+    @GetMapping("/{userId}/metaPlayerRole")
+    public List<MetaPlayerRoleClassify> MetaPlayerRoles(@PathVariable Long userId) {
+        return assetService.metaPlayerRole(userId);
     }
 }
 

+ 11 - 0
src/main/java/com/izouma/nineth/web/AuctionOrderController.java

@@ -1,6 +1,7 @@
 package com.izouma.nineth.web;
 
 import com.izouma.nineth.domain.AuctionOrder;
+import com.izouma.nineth.domain.GiftOrder;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.enums.AuctionPaymentType;
 import com.izouma.nineth.exception.BusinessException;
@@ -17,7 +18,9 @@ import org.springframework.web.bind.annotation.*;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @RestController
 @RequestMapping("/auctionOrder")
@@ -98,6 +101,14 @@ public class AuctionOrderController extends BaseController {
         return auctionOrderRepo.findById(id).orElseThrow(new BusinessException("无记录"));
     }
 
+    @GetMapping("/{id}/status")
+    public Map<String, Object> status(@PathVariable Long id) {
+        AuctionOrder order = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
+        return new HashMap<>() {{
+            put("status", order.getStatus());
+        }};
+    }
+
     @PostMapping("/del/{id}")
     public void del(@PathVariable Long id) {
         auctionOrderRepo.softDelete(id);

+ 27 - 4
src/main/java/com/izouma/nineth/web/CollectionController.java

@@ -28,9 +28,8 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 
 @RestController
@@ -79,6 +78,7 @@ public class CollectionController extends BaseController {
     @PostMapping("/all")
     @JsonView(Collection.View.Basic.class)
     public Page<Collection> all(@RequestBody PageQuery pageQuery) {
+        pageQuery.getQuery().putIfAbsent("companyId", 1);
         Page<Collection> page = collectionService.all(pageQuery).toPage();
         collectionService.queryUserDetail(page.getContent());
         return page;
@@ -130,7 +130,7 @@ public class CollectionController extends BaseController {
 
     @PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/createBlindBox")
-    public Collection createBlindBox(@RequestBody CreateBlindBox createBlindBox) {
+    public Collection createBlindBox(@RequestBody CreateBlindBox createBlindBox) throws Exception {
         return collectionService.createBlindBox(createBlindBox);
     }
 
@@ -235,5 +235,28 @@ public class CollectionController extends BaseController {
     public List<Collection> setScancodeOasis(@RequestBody List<Long> oasisIds) {
         return collectionService.setOasisScancode(oasisIds);
     }
+
+    @GetMapping("/count/{search}")
+    public Map<String, String> countNum(@PathVariable String search) {
+        PageQuery pageQuery = new PageQuery();
+        pageQuery.setPage(0);
+        pageQuery.setSize(1);
+        pageQuery.getQuery().put("onShelf", true);
+        pageQuery.getQuery().put("source", "TRANSFER");
+        pageQuery.getQuery().put("del", false);
+        pageQuery.setSearch(search);
+        pageQuery.getQuery().put("salable", false);
+        long onlyShowNum = collectionService.all(pageQuery).getTotal();
+        pageQuery.getQuery().put("salable", true);
+        long consignmentNum = collectionService.all(pageQuery).getTotal();
+        pageQuery.getQuery().remove("salable");
+        pageQuery.getQuery().put("inPaying", true);
+        long transactingNum = collectionService.all(pageQuery).getTotal();
+        Map<String, String> map = new HashMap<>();
+        map.put("onlyShowNum", String.valueOf(onlyShowNum));
+        map.put("consignmentNum", String.valueOf(consignmentNum));
+        map.put("transactingNum", String.valueOf(transactingNum));
+        return map;
+    }
 }
 

+ 12 - 0
src/main/java/com/izouma/nineth/web/GiftOrderController.java

@@ -1,6 +1,7 @@
 package com.izouma.nineth.web;
 
 import com.izouma.nineth.domain.GiftOrder;
+import com.izouma.nineth.domain.Order;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.GiftOrderRepo;
 import lombok.AllArgsConstructor;
@@ -9,6 +10,9 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.HashMap;
+import java.util.Map;
+
 @RestController
 @RequestMapping("/giftOrder")
 @AllArgsConstructor
@@ -19,4 +23,12 @@ public class GiftOrderController extends BaseController {
     public GiftOrder get(@PathVariable Long id) {
         return giftOrderRepo.findById(id).orElseThrow(new BusinessException("无记录"));
     }
+
+    @GetMapping("/{id}/status")
+    public Map<String, Object> status(@PathVariable Long id) {
+        GiftOrder order = giftOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
+        return new HashMap<>() {{
+            put("status", order.getStatus());
+        }};
+    }
 }

+ 43 - 0
src/main/java/com/izouma/nineth/web/MetaBonusSceneController.java

@@ -0,0 +1,43 @@
+package com.izouma.nineth.web;
+
+
+import com.izouma.nineth.domain.MetaBonusScene;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.repo.MetaBonusSceneRepo;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+@RestController
+@RequestMapping("/metaBonusScene")
+@AllArgsConstructor
+public class MetaBonusSceneController {
+
+    private MetaBonusSceneRepo metaBonusSceneRepo;
+
+    @GetMapping("/{userId}/find")
+    public MetaRestResult<List<MetaBonusScene>> find(@PathVariable Long userId) {
+        List<MetaBonusScene> metaBonusScenes = metaBonusSceneRepo.findAllByUserId(userId);
+        return MetaRestResult.returnSuccess(metaBonusScenes);
+    }
+
+    @PostMapping("/save")
+    public MetaRestResult<MetaBonusScene> save(@RequestBody MetaBonusScene metaBonusScene) {
+        if (Objects.nonNull(metaBonusScene.getId())) {
+            return MetaRestResult.returnError("参数错误:id必须为空");
+        }
+        if (Objects.isNull(metaBonusScene.getUserId())) {
+            return MetaRestResult.returnError("参数错误:缺少用户id");
+        }
+        if (Objects.isNull(metaBonusScene.getType())) {
+            return MetaRestResult.returnError("参数错误:缺少彩蛋类型");
+        }
+        MetaBonusScene dbMetaBonusScene = metaBonusSceneRepo.findByUserIdAndType(metaBonusScene.getUserId(), metaBonusScene.getType());
+        if (Objects.nonNull(dbMetaBonusScene)) {
+          return MetaRestResult.returnError("该玩家已经触发过同类型彩蛋,无法多次触发");
+        }
+        return MetaRestResult.returnSuccess(metaBonusSceneRepo.save(metaBonusScene));
+    }
+}

+ 91 - 0
src/main/java/com/izouma/nineth/web/MetaPlayerInfoController.java

@@ -0,0 +1,91 @@
+package com.izouma.nineth.web;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.izouma.nineth.domain.*;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.repo.*;
+import com.izouma.nineth.service.MetaPlayerInfoService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+@RestController
+@RequestMapping("/metaPlayerInfo")
+@AllArgsConstructor
+public class MetaPlayerInfoController {
+
+    private UserRepo userRepo;
+
+    private MetaPlayerInfoService metaPlayerInfoService;
+
+    private MetaPlayerOfflineInfoRepo metaPlayerOfflineInfoRepo;
+
+    private MetaPlayerWearRepo metaPlayerWearRepo;
+
+    private MetaUserRepo metaUserRepo;
+
+    private PurchaseLevelRepo purchaseLevelRepo;
+
+    @GetMapping("/{userId}/detail")
+    public MetaRestResult<MetaPlayerInfo> findMetaPlayerInfo(@PathVariable Long userId) {
+        User user = userRepo.findById(userId).orElse(null);
+        if (Objects.isNull(user)) {
+            return MetaRestResult.returnError("用户信息为空");
+        }
+        MetaPlayerInfo metaPlayerInfo = new MetaPlayerInfo();
+        metaPlayerInfo.setAvatar(user.getAvatar());
+        metaPlayerInfo.setLevel(user.getLevel());
+        int maxStartLevel = purchaseLevelRepo.findMaxStartLevel();
+        PurchaseLevel purchaseLevel;
+        if (user.getLevel() >= maxStartLevel) {
+            purchaseLevel = purchaseLevelRepo.findByStartLevel(maxStartLevel);
+        } else {
+            purchaseLevel = purchaseLevelRepo.findByLevel(user.getLevel());
+        }
+        metaPlayerInfo.setRealm(purchaseLevel.getRealm());
+        metaPlayerInfo.setTitle(purchaseLevel.getTitle());
+        metaPlayerInfo.setNickname(user.getNickname());
+        MetaPlayerOfflineInfo metaPlayerOfflineInfo = metaPlayerOfflineInfoRepo.findLastByUserId(userId);
+        if (Objects.isNull(metaPlayerOfflineInfo)) {
+            return MetaRestResult.returnSuccess(metaPlayerInfo);
+        }
+        metaPlayerInfo.setSkinId(metaPlayerOfflineInfo.getSkinId());
+        List<Long> clothesId = metaPlayerOfflineInfo.getClothesId();
+        if (CollectionUtil.isEmpty(clothesId)) {
+            return MetaRestResult.returnSuccess(metaPlayerInfo);
+        }
+        List<MetaPlayerWear> metaPlayerWears = metaPlayerWearRepo.findAllByIdIn(clothesId);
+        metaPlayerInfo.setMetaPlayerWearList(metaPlayerWears);
+        return MetaRestResult.returnSuccess(metaPlayerInfo);
+    }
+
+    @GetMapping("/{userId}/{spaceId}/metaSpaceInfo")
+    public MetaRestResult<SpaceObjectsInfo> metaSpaceInfo(@PathVariable Long userId, @PathVariable Long spaceId) {
+        return metaPlayerInfoService.metaSpaceInfo(userId, spaceId);
+    }
+
+    @PostMapping("/updateMetaPersonalSpace")
+    public MetaRestResult<SpaceObjectsInfo> updateMetaPersonalSpace(@RequestBody SpaceObjectsInfo spaceObjectsInfo) {
+        return metaPlayerInfoService.updateMetaPersonalSpace(spaceObjectsInfo);
+    }
+
+    @PostMapping("/saveOffline")
+    public MetaRestResult<MetaPlayerOfflineInfo> saveOffline(@RequestBody MetaPlayerOfflineInfo metaPlayerOfflineInfo) {
+        if (Objects.isNull(metaPlayerOfflineInfo.getUserId())) {
+            return MetaRestResult.returnError("参数错误,缺少用户id");
+        }
+        MetaPlayerOfflineInfo save = metaPlayerOfflineInfoRepo.save(metaPlayerOfflineInfo);
+        return MetaRestResult.returnSuccess(save);
+    }
+
+    @GetMapping("/{phone}/allowLogin")
+    public MetaRestResult<Boolean> allowLogin(@PathVariable Long phone) {
+        MetaUser metaUser = metaUserRepo.findByPhone(phone);
+        if (Objects.isNull(metaUser)) {
+            return MetaRestResult.returnSuccess(Boolean.FALSE);
+        }
+        return MetaRestResult.returnSuccess(metaUser.isAllowLogin());
+    }
+}

+ 97 - 0
src/main/java/com/izouma/nineth/web/MetaPlayerWearController.java

@@ -0,0 +1,97 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.MetaPlayerWear;
+import com.izouma.nineth.domain.MetaPlayerWearCount;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.MetaPlayerWearEnum;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.MetaPlayerWearRepo;
+import com.izouma.nineth.service.MetaPlayerWearService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+@RestController
+@RequestMapping("/metaPlayerWear")
+@AllArgsConstructor
+public class MetaPlayerWearController extends BaseController {
+    private MetaPlayerWearService metaPlayerWearService;
+    private MetaPlayerWearRepo metaPlayerWearRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public MetaPlayerWear save(@RequestBody MetaPlayerWear record) {
+        if (record.getId() != null) {
+            MetaPlayerWear orig = metaPlayerWearRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return metaPlayerWearRepo.save(orig);
+        }
+        return metaPlayerWearRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<MetaPlayerWear> all(@RequestBody PageQuery pageQuery) {
+        return metaPlayerWearService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public MetaPlayerWear get(@PathVariable Long id) {
+        return metaPlayerWearRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        metaPlayerWearRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<MetaPlayerWear> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+
+    @GetMapping("/{userId}/allWears")
+    public MetaRestResult<MetaPlayerWearCount> allWears(@PathVariable Long userId) {
+        MetaPlayerWearCount metaPlayerWearCount = new MetaPlayerWearCount();
+        metaPlayerWearCount.setUserId(userId);
+        try {
+            Arrays.stream(MetaPlayerWearEnum.values()).forEach(metaPlayerWearEnum -> {
+                List<MetaPlayerWear> metaPlayerWears = metaPlayerWearRepo.findAllByUserIdAndType(userId, metaPlayerWearEnum);
+                switch (metaPlayerWearEnum) {
+                    case HEADDRESS:
+                        metaPlayerWearCount.setHeaddressList(metaPlayerWears);
+                        break;
+                    case CLOTHES:
+                        metaPlayerWearCount.setClothesList(metaPlayerWears);
+                        break;
+                    case TROUSERS:
+                        metaPlayerWearCount.setTrousersList(metaPlayerWears);
+                        break;
+                    case SHOES:
+                        metaPlayerWearCount.setShoesList(metaPlayerWears);
+                        break;
+                    case ACTION:
+                        metaPlayerWearCount.setActionList(metaPlayerWears);
+                        break;
+                    default:
+                        throw new BusinessException("不支持的服饰类型");
+                }
+            });
+        } catch (Exception e) {
+            return MetaRestResult.returnError(e.getMessage());
+        }
+        return MetaRestResult.returnSuccess(metaPlayerWearCount);
+    }
+}
+

+ 87 - 0
src/main/java/com/izouma/nineth/web/MetaSpatialInfoController.java

@@ -0,0 +1,87 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.MetaSpatialInfo;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.MetaSpatialInfoRepo;
+import com.izouma.nineth.service.MetaSpatialInfoService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+@RestController
+@RequestMapping("/metaSpatialInfo")
+@AllArgsConstructor
+public class MetaSpatialInfoController extends BaseController {
+    private MetaSpatialInfoService metaSpatialInfoService;
+    private MetaSpatialInfoRepo metaSpatialInfoRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public MetaSpatialInfo save(@RequestBody MetaSpatialInfo record) {
+        MetaSpatialInfo metaSpatialInfo = metaSpatialInfoRepo.findByRegionAndCoordinateAndDel(record.getRegion(), record.getCoordinate(), false);
+        if (Objects.nonNull(metaSpatialInfo) && !Objects.equals(metaSpatialInfo.getId(), record.getId())) {
+            throw new BusinessException("当前区域内已经存在该坐标的空间");
+        }
+        if (record.getId() != null) {
+            MetaSpatialInfo orig = metaSpatialInfoRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return metaSpatialInfoRepo.save(orig);
+        }
+        return metaSpatialInfoRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<MetaSpatialInfo> all(@RequestBody PageQuery pageQuery) {
+        return metaSpatialInfoService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public MetaSpatialInfo get(@PathVariable Long id) {
+        return metaSpatialInfoRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        metaSpatialInfoRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<MetaSpatialInfo> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+
+    @GetMapping("/{userId}/find")
+    public MetaRestResult<List<MetaSpatialInfo>> findByUserId(@PathVariable Long userId) {
+        List<MetaSpatialInfo> metaSpatialInfos = metaSpatialInfoRepo.findAllByUserIdAndDel(userId, false);
+        return MetaRestResult.returnSuccess(metaSpatialInfos);
+    }
+
+    @GetMapping("/onSale")
+    public MetaRestResult<Integer> findOnSale() {
+        int onSale = metaSpatialInfoRepo.countBySaleAndDel(true, false);
+        return MetaRestResult.returnSuccess(onSale);
+    }
+
+    @GetMapping("/{id}/detail")
+    public MetaRestResult<MetaSpatialInfo> detail(@PathVariable Long id) {
+        MetaSpatialInfo metaSpatialInfo = metaSpatialInfoRepo.findById(id).orElse(null);
+        if (Objects.isNull(metaSpatialInfo)) {
+            return MetaRestResult.returnError(String.format("没有id为:%S的空间信息", id));
+        }
+        return MetaRestResult.returnSuccess(metaSpatialInfo);
+    }
+}
+

+ 86 - 0
src/main/java/com/izouma/nineth/web/OnOffController.java

@@ -0,0 +1,86 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.OnOff;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.OnOffRepo;
+import com.izouma.nineth.service.OnOffService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+@RestController
+@RequestMapping("/onOff")
+@AllArgsConstructor
+public class OnOffController extends BaseController {
+    private OnOffService onOffService;
+    private OnOffRepo onOffRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public OnOff save(@RequestBody OnOff record) {
+        if (record.getId() != null) {
+            OnOff orig = onOffRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return onOffRepo.save(orig);
+        }
+        return onOffRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<OnOff> all(@RequestBody PageQuery pageQuery) {
+        return onOffService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public OnOff get(@PathVariable Long id) {
+        return onOffRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        onOffRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<OnOff> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+
+    @GetMapping("/{id}/detail")
+    public MetaRestResult<OnOff> detail(@PathVariable Long id) {
+        OnOff onOff = onOffRepo.findById(id).orElse(null);
+        if (Objects.isNull(onOff)) {
+            return MetaRestResult.returnError(String.format("没有id为:%S的开关信息", id));
+        }
+        return MetaRestResult.returnSuccess(onOff);
+    }
+    @PostMapping("/meta/save")
+    public MetaRestResult<OnOff> metaSave(@RequestBody OnOff onOff) {
+        if (Objects.isNull(onOff.getStatus())) {
+            return MetaRestResult.returnError("参数不合法,缺少开关状态");
+        }
+        if(Objects.isNull(onOff.getId())) {
+            return MetaRestResult.returnSuccess("新增成功", onOffRepo.save(onOff));
+        }
+        OnOff orig = onOffRepo.findById(onOff.getId()).orElse(null);
+        if (Objects.isNull(orig)) {
+            return MetaRestResult.returnError(String.format("没有id为:%S的开关信息", onOff.getId()));
+        }
+        ObjUtils.merge(orig, onOff);
+        return MetaRestResult.returnSuccess("修改成功", onOffRepo.save(orig));
+    }
+}
+

+ 30 - 0
src/main/java/com/izouma/nineth/web/OrderPayControllerV2.java

@@ -35,6 +35,12 @@ public class OrderPayControllerV2 {
         return orderPayService.payOrder(id);
     }
 
+    @RequestMapping(value = "/ali")
+    @ResponseBody
+    public String payOrderAli(Long id) {
+        return orderPayService.payOrderAli(id);
+    }
+
     @RequestMapping(value = "/alipay_wx")
     public String payOrderAlipayWx(Long id, Model model) {
         Order order = orderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
@@ -88,6 +94,12 @@ public class OrderPayControllerV2 {
         return orderPayService.payGiftOrder(id);
     }
 
+    @RequestMapping(value = "/gift/ali")
+    @ResponseBody
+    public String payGiftOrderAli(Long id) {
+        return orderPayService.payGiftAli(id);
+    }
+
     @RequestMapping(value = "/gift/alipay_wx")
     public String payGiftOrderAlipayWx(Long id, Model model) {
         String payUrl = orderPayService.payGiftOrder(id);
@@ -126,6 +138,12 @@ public class OrderPayControllerV2 {
         return orderPayService.payMintOrder(id);
     }
 
+    @RequestMapping(value = "/mint/ali")
+    @ResponseBody
+    public String payMintAli(Long id) {
+        return orderPayService.payMintAli(id);
+    }
+
     @RequestMapping(value = "/mint/alipay_wx")
     public String payMintOrderAlipayWx(Long id, Model model) {
         String payUrl = orderPayService.payMintOrder(id);
@@ -164,6 +182,12 @@ public class OrderPayControllerV2 {
         return orderPayService.recharge(SecurityUtils.getAuthenticatedUser().getId(), amount);
     }
 
+    @RequestMapping(value = "/recharge/ali")
+    @ResponseBody
+    public String rechargeAli(@RequestParam BigDecimal amount) {
+        return orderPayService.payRechargeAli(SecurityUtils.getAuthenticatedUser().getId(), amount);
+    }
+
     @RequestMapping(value = "/recharge/agreement")
     @ResponseBody
     public Map<String, Object> payRechargeAgreement(@RequestParam Long userId, @RequestParam BigDecimal amount,
@@ -189,6 +213,12 @@ public class OrderPayControllerV2 {
         return orderPayService.payAuctionOrder(id);
     }
 
+    @RequestMapping(value = "/auction/ali")
+    @ResponseBody
+    public String payAuctionOrderAli(Long id) {
+        return orderPayService.payAuctionAli(id);
+    }
+
     @RequestMapping(value = "/auction/alipay_wx", method = RequestMethod.GET)
     public String payAuctionOrderAlipayWx(Long id, Model model) {
         String payUrl = orderPayService.payAuctionOrder(id);

+ 3 - 2
src/main/java/com/izouma/nineth/web/RarityLabelController.java

@@ -2,6 +2,7 @@ package com.izouma.nineth.web;
 
 import com.izouma.nineth.domain.RarityLabel;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.Rarity;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.RarityLabelRepo;
 import com.izouma.nineth.service.RarityLabelService;
@@ -51,12 +52,12 @@ public class RarityLabelController extends BaseController {
     }
 
     @GetMapping("/label/{name}")
-    public String[] findByName(@PathVariable String name) {
+    public List<Rarity> findByName(@PathVariable String name) {
         RarityLabel rarityLabel = rarityLabelRepo.findByNameAndDel(name, false);
         if (Objects.isNull(rarityLabel)) {
             return null;
         }
-        return rarityLabel.getLabel().split(",");
+        return rarityLabel.getLabel();
     }
 
     @PostMapping("/del/{id}")

+ 35 - 0
src/main/java/com/izouma/nineth/web/RechargeOrderController.java

@@ -0,0 +1,35 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.GiftOrder;
+import com.izouma.nineth.domain.RechargeOrder;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.RechargeOrderRepo;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/rechargeOrder")
+@AllArgsConstructor
+public class RechargeOrderController {
+
+    private final RechargeOrderRepo rechargeOrderRepo;
+
+    @GetMapping("/get/{id}")
+    public RechargeOrder get(@PathVariable Long id) {
+        return rechargeOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
+    }
+
+    @GetMapping("/{id}/status")
+    public Map<String, Object> status(@PathVariable Long id) {
+        RechargeOrder order = rechargeOrderRepo.findById(id).orElseThrow(new BusinessException("订单不存在"));
+        return new HashMap<>() {{
+            put("status", order.getStatus());
+        }};
+    }
+}

+ 17 - 0
src/main/java/com/izouma/nineth/web/UserHoldCountController.java

@@ -1,5 +1,6 @@
 package com.izouma.nineth.web;
 
+import com.alibaba.excel.util.CollectionUtils;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.dto.UserHoldDTO;
 import com.izouma.nineth.service.UserHoldCountService;
@@ -33,7 +34,23 @@ public class UserHoldCountController {
         List<UserHoldDTO> userHoldDTOs = userHoldCountService.all(pageQuery).getContent();
         userHoldDTOs.sort(Comparator.comparing(UserHoldDTO::getPrice).reversed());
         return userHoldDTOs;
+    }
 
+    @GetMapping("/app/top")
+    public List<UserHoldDTO> appTop() {
+        List<UserHoldDTO> top = top();
+        if (CollectionUtils.isEmpty(top)) {
+            return null;
+        }
+        top.forEach(userHoldDTO -> {
+            userHoldDTO.setNum(0);
+            userHoldDTO.setUsername(null);
+            userHoldDTO.setPrefixName(null);
+            userHoldDTO.setName(null);
+            userHoldDTO.setPrice(null);
+            userHoldDTO.setUserId(null);
+        });
+        return top;
     }
 
     @GetMapping("/excel")

+ 8 - 4
src/main/resources/application.yaml

@@ -197,7 +197,7 @@ alipay:
   api-key: CRv5YFAOIEGY5PgVf14Y9g==
   app-cert-path: classpath:cert/appCertPublicKey_2021002120645023.crt
   root-cert-path: classpath:cert/alipayRootCert.crt
-  notify-url: https://test.raex.vip/notify/order/alipay
+  notify-url: https://testorder.raex.vip/alipay/notify
   return-url: https://test.raex.vip/9th/home
 #adapay:
 #  app-id: app_0e8d3acb-3d95-4ebb-8445-e470c378a787
@@ -276,6 +276,8 @@ general:
   broadcast-event-topic: broadcast-event-topic-test
   register-group: register-group-test
   register-topic: register-topic-test
+alipay:
+  notify-url: https://testorder.raex.vip/alipay/notify
 sandpay:
   notify-url: https://testorder.raex.vip/sandpay/notify
 hmpay:
@@ -385,7 +387,7 @@ wx:
     refund-notify-url: https://www.raex.vip/wx/refundNotify
     return-url: https://www.raex.vip/9th/orders
 alipay:
-  notify-url: https://www.raex.vip/notify/order/alipay
+  notify-url: https://order.raex.vip/alipay/notify
   return-url: https://www.raex.vip/9th/home
 adapay:
   notify-url: https://order.raex.vip/notify/adapay
@@ -440,7 +442,7 @@ wx:
     refund-notify-url: https://www.raex.vip/wx/refundNotify
     return-url: https://www.raex.vip/9th/orders
 alipay:
-  notify-url: https://www.raex.vip/notify/order/alipay
+  notify-url: https://order.raex.vip/alipay/notify
   return-url: https://www.raex.vip/9th/home
 adapay:
   notify-url: https://order.raex.vip/notify/adapay
@@ -493,6 +495,8 @@ rocketmq:
   name-server: 172.29.50.102:9876
   producer:
     group: my-producer
+alipay:
+  notify-url: https://testorder.raex.vip/alipay/notify
 sandpay:
   notify-url: https://testorder.raex.vip/sandpay/notify
 hmpay:
@@ -538,7 +542,7 @@ wx:
     refund-notify-url: https://oasis.raex.vip/wx/refundNotify
     return-url: https://oasis.raex.vip/9th/orders
 alipay:
-  notify-url: https://oasis.raex.vip/notify/order/alipay
+  notify-url: https://oasis.raex.vip/alipay/notify
   return-url: https://oasis.raex.vip/9th/home
 adapay:
   notify-url: https://oasis.raex.vip/notify/adapay

+ 1 - 0
src/main/resources/genjson/MetaPlayerWear.json

@@ -0,0 +1 @@
+{"tableName":"MetaPlayerWear","className":"MetaPlayerWear","remark":"元宇宙服饰","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/java/com/izouma/nineth","viewPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src/views","routerPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src","resourcesPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"userId","modelName":"userId","remark":"用户id","showInList":true,"showInForm":true,"formType":"number","required":true},{"name":"action","modelName":"action","remark":"模型","showInList":true,"showInForm":true,"formType":"singleLineText","required":true},{"name":"type","modelName":"type","remark":"类型","showInList":true,"showInForm":true,"formType":"select","required":true,"apiFlag":"1","optionsValue":"[{\"label\":\"头饰\",\"value\":\"HEADDRESS\"},{\"label\":\"衣服\",\"value\":\"CLOTHES\"},{\"label\":\"裤子\",\"value\":\"TROUSERS\"},{\"label\":\"鞋子\",\"value\":\"SHOES\"},{\"label\":\"动作\",\"value\":\"ACTION\"},{\"label\":\"皮肤\",\"value\":\"SKIN\"}]"},{"name":"offline","modelName":"offline","remark":"离线时保存","showInList":false,"showInForm":false,"formType":"singleLineText"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.MetaPlayerWear"}

+ 1 - 0
src/main/resources/genjson/MetaSpatialInfo.json

@@ -0,0 +1 @@
+{"tableName":"MetaSpatialInfo","className":"MetaSpatialInfo","remark":"元宇宙空间信息","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/java/com/izouma/nineth","viewPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src/views","routerPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src","resourcesPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"region","modelName":"region","remark":"所属区域","showInList":true,"showInForm":true,"formType":"select","required":true,"apiFlag":"1","optionsValue":"[{\"label\":\"1区\",\"value\":\"ONE\"},{\"label\":\"2区\",\"value\":\"TWO\"},{\"label\":\"3区\",\"value\":\"THREE\"},{\"label\":\"4区\",\"value\":\"FOUR\"},{\"label\":\"5区\",\"value\":\"FIVE\"},{\"label\":\"6区\",\"value\":\"SIX\"},{\"label\":\"7区\",\"value\":\"SEVEN\"},{\"label\":\"8区\",\"value\":\"EIGHT\"},{\"label\":\"9区\",\"value\":\"NINE\"}]"},{"name":"xCoordinate","modelName":"xCoordinate","remark":"区域内坐标x轴","showInList":true,"showInForm":true,"formType":"singleLineText","required":true},{"name":"yCoordinate","modelName":"yCoordinate","remark":"区域内坐标y轴","showInList":true,"showInForm":true,"formType":"singleLineText","required":true},{"name":"sale","modelName":"sale","remark":"状态","showInList":true,"showInForm":true,"formType":"switch","required":true},{"name":"userId","modelName":"userId","remark":"所属用户id","showInList":true,"showInForm":true,"formType":"number","required":true},{"name":"size","modelName":"size","remark":"空间大小","showInList":true,"showInForm":true,"formType":"number","required":true}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.MetaSpatialInfo"}

+ 1 - 0
src/main/resources/genjson/OnOff.json

@@ -0,0 +1 @@
+{"tableName":"OnOff","className":"OnOff","remark":"开关表","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/java/com/izouma/nineth","viewPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src/views","routerPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src","resourcesPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"status","modelName":"status","remark":"状态:开/关","showInList":true,"showInForm":true,"formType":"select","required":true,"validate":false,"apiFlag":"1","optionsValue":"[{\"label\":\"开\",\"value\":\"ON\"},{\"label\":\"关\",\"value\":\"OFF\"}]"},{"name":"remark","modelName":"remark","remark":"remark","showInList":true,"showInForm":true,"formType":"singleLineText","required":true,"validate":false}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.OnOff"}

+ 198 - 0
src/main/resources/static/wx_alipay_bridge.html

@@ -0,0 +1,198 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <title>支付</title>
+    <script src="./js/jquery.min.js"></script>
+    <style>
+        html {
+            width: 100%;
+            height: 100%;
+            padding: 0;
+            margin: 0;
+            font-family: -apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif;
+            font-size: 14px;
+            font-weight: 400;
+            line-height: 1.6;
+        }
+
+        body {
+            width: 100%;
+            height: 100%;
+            padding: 0;
+            margin: 0;
+            background: #1677ff;
+            /*background: #1677ff url(./img/zfb.png) no-repeat top;*/
+            background-size: 100%;
+        }
+
+        .overlay .overlay-img {
+            width: 186px;
+            height: 210px;
+            margin-right: 15px;
+        }
+
+        .overlay .overlay-text1 {
+            width: 272px;
+            font-size: 16px;
+            font-weight: bold;
+            color: rgba(255, 255, 255, 1);
+            line-height: 22px;
+            margin: 15px auto 0;
+        }
+
+        .overlay .overlay-text2 {
+            width: 272px;
+            font-size: 13px;
+            color: rgba(255, 255, 255, 1);
+            line-height: 18px;
+            margin: 15px auto 0;
+        }
+
+        .overlay {
+            position: fixed;
+            top: 0;
+            right: 0;
+            bottom: 0;
+            left: 0;
+            background: rgba(0, 0, 0, 0.7);
+            display: none;
+        }
+
+        #btn-zfb {
+            width: 100%;
+            margin: auto;
+            background: #ffffff;
+            line-height: 52px;
+            height: 52px;
+            outline: none;
+            color: #1677ff;
+            font-size: 18px;
+            letter-spacing: 3px;
+            font-weight: 400;
+            border-radius: 8px;
+            border: none;
+        }
+
+        #btn-zfb:active {
+            background: rgba(240, 240, 240, 1);
+        }
+
+        #btn-finish:active {
+            background: rgba(0, 0, 0, 0.03);
+        }
+
+        #btn-finish {
+            width: 100%;
+            line-height: 50px;
+            height: 52px;
+            outline: none;
+            font-size: 18px;
+            letter-spacing: 3px;
+            font-weight: 400;
+            border-radius: 8px;
+            margin: 20px auto 0 auto;
+            border: 2px solid #ffffff;
+            color: #ffffff;
+            background: none;
+        }
+
+        .btns {
+            width: 80%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            position: absolute;
+            left: 0;
+            right: 0;
+            top: 0;
+            bottom: 0;
+            margin: auto;
+            flex-direction: column;
+        }
+
+        .btns a {
+            width: 100%;
+        }
+    </style>
+</head>
+
+<body>
+<div class="overlay" id="overlay">
+    <div style="text-align: right"><img class="overlay-img" src="./img/zhifu_img_liulanqi_ios.png" alt=""/>
+    </div>
+    <div class="overlay-text1">请点击右上角,选择在默认浏览器中打开然后继续完成支付</div>
+    <div class="overlay-text2">注:由于微信限制,需要在浏览器中打开才可以完成支付宝支付</div>
+</div>
+
+<div class="btn-wrapper" style="display: none">
+    <div class="btns">
+        <a id="link">
+            <button id="btn-zfb">打开支付宝</button>
+        </a>
+        <a id="" href="weixin://">
+            <button id="btn-finish">我已完成支付</button>
+        </a>
+    </div>
+</div>
+<script>
+    function GetQueryString(name) {
+        var after = window.location.href.split("?")[1];
+        if (after) {
+            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+            var r = after.match(reg);
+            if (r != null) {
+                return decodeURIComponent(r[2]);
+            } else {
+                return null;
+            }
+        }
+    }
+
+    var payUrl = decodeURIComponent(GetQueryString("payUrl"));
+    var returnUrl = decodeURIComponent(GetQueryString("returnUrl"));
+    var orderId = decodeURIComponent(GetQueryString("orderId"));
+    var type = decodeURIComponent(GetQueryString("type"));
+    $('#link').attr('href', payUrl);
+
+
+    window.onload = function () {
+        if (/micromessenger/i.test(navigator.userAgent)) {
+            $('.overlay').fadeIn();
+            setInterval(function () {
+                var url;
+                switch (type) {
+                    case 'order':
+                        url = '/order/' + orderId + '/status';
+                        break;
+                    case 'gift':
+                        url = '/giftOrder/' + orderId + '/status';
+                        break;
+                    case 'mintOrder':
+                        url = '/mintOrder/' + orderId + '/status';
+                        break;
+                    case 'recharge':
+                        url = '/recharge/' + orderId + '/status';
+                        break;
+                    case 'auctionOrder':
+                        url = '/auctionOrder/' + orderId + '/status';
+                        break;
+                }
+                $.get(url, function (data) {
+                    if (data.status === 'FINISH') {
+                        window.location.replace(returnUrl);
+                    }
+                });
+            }, 1000);
+        } else {
+            $('.btn-wrapper').fadeIn();
+            $('#link')[0].click();
+        }
+    }
+</script>
+</body>
+
+</html>

+ 1 - 1
src/main/vue/src/components/CollectionCoupon.vue

@@ -40,7 +40,7 @@ export default {
                     {
                         sort: 'sort,desc;createdAt,desc',
                         search: query,
-                        query: { del: false, source: 'OFFICIAL', couponPayment: true }
+                        query: { del: false, source: 'OFFICIAL'}
                     },
                     { body: 'json' }
                 )

+ 94 - 0
src/main/vue/src/components/ColorSelect.vue

@@ -0,0 +1,94 @@
+<template>
+  <div class="public-input-color-picker">
+    <el-input v-model="value2" :placeholder="placeholder" @input="_change" :disabled="true" ></el-input>
+    <el-color-picker
+      v-model="value2"
+      :show-alpha="showAlpha"
+      :predefine="predefineColors"
+      @change="_change"
+    ></el-color-picker>
+  </div>
+</template>
+<script>
+/**
+ * 输入框颜色选择器
+ * ps:预定义颜色中,如果颜色不对,将无法选中
+ *
+ * @showAlpha 是否支持透明度 默认false
+ * @predefineColors 预定义颜色
+ * @value 默认颜色编码 示例:#ffffff
+ * @placeholder 输入框提示文字
+ */
+export default {
+  name: 'public-input-color-picker',
+  data() {
+    return {
+      value2: ''
+    }
+  },
+  props: {
+    showAlpha: { type: Boolean, default: false },
+    predefineColors: {
+      type: Array,
+      default: () => {
+        return [
+          '#ff4500',
+          '#ff8c00',
+          '#ffd700',
+          '#90ee90',
+          '#00ced1',
+          '#1e90ff',
+          '#c71585',
+          '#FF0000'
+        ]
+      }
+    },
+    value: { type: String, default: '' },
+    placeholder: { type: String, default: '请输入颜色编码' }
+  },
+  components: {},
+  computed: {},
+  created() {
+  },
+  onload() { },
+  onShow() { },
+  watch: {
+    value: {
+      handler: function (newVal, oldVal) {
+        this.value2 = JSON.parse(JSON.stringify(newVal))
+      },
+      immediate: true,
+      deep: true
+    }
+  },
+  mounted() { },
+  methods: {
+    _change(val) {
+      if (val) {
+        this.$emit('change', val)
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+@box-width: 200px;
+.public-input-color-picker {
+  // display: flex;
+  // -webkit-box-align: center;
+  // -webkit-align-items: center;
+  // align-items: center;
+  position: relative;
+  width: @box-width;
+  .el-input {
+    width: @box-width;
+  }
+  .el-color-picker {
+    position: absolute;
+    right: 0;
+    /deep/ .el-color-picker__trigger {
+      border: 0;
+    }
+  }
+}
+</style>

+ 49 - 1
src/main/vue/src/router.js

@@ -963,6 +963,54 @@ const router = new Router({
                     meta: {
                        title: '稀有度标签',
                     },
+               },
+                {
+                    path: '/onOffEdit',
+                    name: 'OnOffEdit',
+                    component: () => import(/* webpackChunkName: "onOffEdit" */ '@/views/OnOffEdit.vue'),
+                    meta: {
+                       title: '开关编辑',
+                    },
+                },
+                {
+                    path: '/onOffList',
+                    name: 'OnOffList',
+                    component: () => import(/* webpackChunkName: "onOffList" */ '@/views/OnOffList.vue'),
+                    meta: {
+                       title: '开关',
+                    },
+               },
+                {
+                    path: '/metaSpatialInfoEdit',
+                    name: 'MetaSpatialInfoEdit',
+                    component: () => import(/* webpackChunkName: "metaSpatialInfoEdit" */ '@/views/MetaSpatialInfoEdit.vue'),
+                    meta: {
+                       title: '元宇宙空间信息编辑',
+                    },
+                },
+                {
+                    path: '/metaSpatialInfoList',
+                    name: 'MetaSpatialInfoList',
+                    component: () => import(/* webpackChunkName: "metaSpatialInfoList" */ '@/views/MetaSpatialInfoList.vue'),
+                    meta: {
+                       title: '元宇宙空间信息',
+                    },
+               },
+                {
+                    path: '/metaPlayerWearEdit',
+                    name: 'MetaPlayerWearEdit',
+                    component: () => import(/* webpackChunkName: "metaPlayerWearEdit" */ '@/views/MetaPlayerWearEdit.vue'),
+                    meta: {
+                       title: '元宇宙服饰编辑',
+                    },
+                },
+                {
+                    path: '/metaPlayerWearList',
+                    name: 'MetaPlayerWearList',
+                    component: () => import(/* webpackChunkName: "metaPlayerWearList" */ '@/views/MetaPlayerWearList.vue'),
+                    meta: {
+                       title: '元宇宙服饰',
+                    },
                }
                 /**INSERT_LOCATION**/
             ]
@@ -1023,4 +1071,4 @@ router.beforeEach((to, from, next) => {
     }
 });
 
-export default router;
+export default router;

+ 22 - 0
src/main/vue/src/views/BannerEdit.vue

@@ -22,6 +22,13 @@
                     <el-form-item prop="pic" label="图片">
                         <single-upload v-model="formData.pic"></single-upload>
                     </el-form-item>
+                    <el-form-item prop="backgroundColor" label="背景颜色">
+                        <color-select
+                            :value="formData.backgroundColor"
+                            placeholder="请输入背景颜色"
+                            @change="(val)=>{formData.backgroundColor=val}"
+                        />
+                    </el-form-item>
                     <el-form-item prop="remark" label="备注">
                         <el-input v-model="formData.remark"></el-input>
                     </el-form-item>
@@ -83,7 +90,15 @@
     </div>
 </template>
 <script>
+
+import colorSelect from "../components/ColorSelect.vue";
+
 export default {
+
+    components: {
+      colorSelect
+    },
+
     name: 'BannerEdit',
     created() {
         if (this.$route.query.id) {
@@ -113,6 +128,13 @@ export default {
             formData: {
                 link: false
             },
+            //颜色初始数据
+            colorList: ["#FFC0CB", "#DB7093", "#FF1493", "#DC143C",
+                "#DDA0DD", "#DA70D6", "#FF00FF", "#9370DB",
+                "#800080", "#6495ED", "#4169E1", "#0000FF",
+                "#87CEEB", "#E1FFFF", "#00FFFF", "#7FFFAA",
+                "#00FF7F", "#008000", "#FFFFE0", "#FFFF00",
+                "#FFD700", "#F5DEB3", "#FFA500", "#CD5C5C"],
             rules: {
                 name: [
                     {

+ 17 - 4
src/main/vue/src/views/BlindBoxEdit.vue

@@ -150,11 +150,22 @@
                         <el-button size="mini" @click="addItem" :disabled="!canEdit">添加作品</el-button>
                     </el-form-item>
                     <el-form-item prop="prefixName" label="系列名称">
-                        <el-input v-model="formData.prefixName" style="width: 200px" placeholder="请输入系列名称"></el-input>
+                        <el-input
+                            v-model="formData.prefixName"
+                            style="width: 200px"
+                            placeholder="请输入系列名称"
+                        ></el-input>
                     </el-form-item>
                     <!-- <el-form-item prop="likes" label="点赞">
                         <el-input-number v-model="formData.likes"></el-input-number>
                     </el-form-item> -->
+                    <el-form-item prop="chainFlag" label="上链">
+                        <el-select v-model="formData.chainFlag" placeholder="请选择">
+                            <el-option label="蚂蚁链+华储链" :value="3" :key="3"></el-option>
+                            <el-option label="华储链" :value="2" :key="2"></el-option>
+                            <el-option label="蚂蚁链" :value="1" :key="1"></el-option>
+                        </el-select>
+                    </el-form-item>
                     <el-form-item prop="scanCode" label="仅扫码可见">
                         <el-radio v-model="formData.scanCode" :label="true">是</el-radio>
                         <el-radio v-model="formData.scanCode" :label="false">否</el-radio>
@@ -273,7 +284,7 @@
                         <single-upload v-model="formData.registerBg"></single-upload>
                     </el-form-item>
                     <el-form-item label="活动规则" v-if="formData.assignment > 0">
-                         <rich-text style="width:500px" onlyText v-model="formData.rule"></rich-text>
+                        <rich-text style="width:500px" onlyText v-model="formData.rule"></rich-text>
                     </el-form-item>
                     <el-form-item class="form-submit">
                         <el-button @click="onSave" :loading="saving" type="primary" v-if="!formData.id">
@@ -464,7 +475,8 @@ export default {
                 scanCode: false,
                 noSoldOut: true,
                 assignment: 0,
-                couponPayment: false
+                couponPayment: false,
+                chainFlag: 3
             },
             rules: {
                 name: [
@@ -674,7 +686,8 @@ export default {
                         },
                         trigger: 'blur'
                     }
-                ]
+                ],
+                chainFlag: [{ required: true, message: '请选择区块链', trigger: 'blur' }]
             },
             typeOptions: [
                 { label: '默认', value: 'DEFAULT' },

+ 9 - 2
src/main/vue/src/views/CollectionEdit.vue

@@ -181,7 +181,13 @@
                             placeholder="请输入系列名称"
                         ></el-input>
                     </el-form-item>
-
+                    <el-form-item prop="chainFlag" label="上链">
+                        <el-select v-model="formData.chainFlag" placeholder="请选择">
+                            <el-option label="蚂蚁链+华储链" :value="3" :key="3"></el-option>
+                            <el-option label="华储链" :value="2" :key="2"></el-option>
+                            <el-option label="蚂蚁链" :value="1" :key="1"></el-option>
+                        </el-select>
+                    </el-form-item>
                     <!-- <el-form-item prop="likes" label="点赞">
                         <el-input-number v-model="formData.likes"></el-input-number>
                     </el-form-item> -->
@@ -668,7 +674,8 @@ export default {
                         },
                         trigger: 'blur'
                     }
-                ]
+                ],
+                chainFlag: [{ required: true, message: '请选择区块链', trigger: 'blur' }]
             },
             typeOptions: [
                 { label: '默认', value: 'DEFAULT' },

+ 178 - 0
src/main/vue/src/views/MetaPlayerWearEdit.vue

@@ -0,0 +1,178 @@
+<template>
+	<div class="edit-view">
+		<page-title>
+			<el-button @click="$router.go(-1)" :disabled="saving"> 取消 </el-button>
+			<el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id"> 删除 </el-button>
+			<el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+		</page-title>
+		<div class="edit-view__content-wrapper">
+			<div class="edit-view__content-section">
+				<el-form
+					:model="formData"
+					:rules="rules"
+					ref="form"
+					label-width="94px"
+					label-position="right"
+					size="small"
+					style="max-width: 500px"
+				>
+					<el-form-item prop="userId" label="用户id">
+						<el-input-number type="number" v-model="formData.userId"> </el-input-number>
+					</el-form-item>
+                    <el-form-item prop="model" label="模型">
+						<model-upload
+							:limit="1"
+							v-model="formData.model"
+							:customUrl="customUrl"
+							accept="application/zip"
+							format="json"
+							single
+						>
+						</model-upload>
+						<div class="tip">请将FBX文件与贴图打包成zip压缩包上传</div>
+					</el-form-item>
+					<el-form-item label="相机距离" v-if="formData.model">
+						<el-input-number v-model="scale" :min="0.1" :step="0.1"> </el-input-number>
+					</el-form-item>
+					<el-form-item label="Y轴偏移" v-if="formData.model">
+						<el-input-number v-model="yOffset"> </el-input-number>
+					</el-form-item>
+					<el-form-item prop="type" label="类型">
+						<el-select v-model="formData.type" clearable filterable placeholder="请选择">
+							<el-option
+								v-for="item in typeOptions"
+								:key="item.value"
+								:label="item.label"
+								:value="item.value"
+							>
+							</el-option>
+						</el-select>
+					</el-form-item>
+					<el-form-item class="form-submit">
+						<el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+						<el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+							删除
+						</el-button>
+						<el-button @click="$router.go(-1)" :disabled="saving"> 取消 </el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</div>
+	</div>
+</template>
+<script>
+import resolveUrl from 'resolve-url';
+import ModelUpload from '../components/ModelUpload.vue';
+import SingleUpload from '../components/SingleUpload.vue';
+import TagSelect from '../components/TagSelect.vue';
+export default {
+	name: 'MetaPlayerWearEdit',
+    components: { ModelUpload, SingleUpload, TagSelect },
+	created() {
+		if (this.$route.query.id) {
+			this.$http
+				.get('metaPlayerWear/get/' + this.$route.query.id)
+				.then(res => {
+                    if (res.model) {
+                        let url = new URL(res.model.url);
+                        this.scale = Number(url.searchParams.get('scale')) || 1;
+                        this.yOffset = Number(url.searchParams.get('yOffset')) || 0;
+                        res.model.url = url.origin + url.pathname;
+                    }
+					this.formData = res;
+				})
+				.catch(e => {
+					console.log(e);
+					this.$message.error(e.error);
+				});
+		}
+	},
+	data() {
+		return {
+			saving: false,
+            scale: 1,
+			yOffset: 0,
+            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel'),
+			formData: {},
+			rules: {
+				userId: [
+					{
+						required: true,
+						message: '请输入用户id',
+						trigger: 'blur'
+					}
+				],
+				model: [
+					{
+						required: true,
+						message: '请上传模型',
+						trigger: 'blur'
+					}
+				],
+				type: [
+					{
+						required: true,
+						message: '请输入类型',
+						trigger: 'blur'
+					}
+				]
+			},
+			typeOptions: [
+				{ label: '头饰', value: 'HEADDRESS' },
+				{ label: '衣服', value: 'CLOTHES' },
+				{ label: '裤子', value: 'TROUSERS' },
+				{ label: '鞋子', value: 'SHOES' },
+				{ label: '动作', value: 'ACTION' }
+			]
+		};
+	},
+	methods: {
+		onSave() {
+			this.$refs.form.validate(valid => {
+				if (valid) {
+					this.submit();
+				} else {
+					return false;
+				}
+			});
+		},
+		submit() {
+			let data = { ...this.formData };
+            if (data.model) {
+                data.model.url = data.model.url + '?scale=' + this.scale + '&yOffset=' + this.yOffset;
+            }
+			this.saving = true;
+			this.$http
+				.post('/metaPlayerWear/save', data, { body: 'json' })
+				.then(res => {
+					this.saving = false;
+					this.$message.success('成功');
+					this.$router.go(-1);
+				})
+				.catch(e => {
+					console.log(e);
+					this.saving = false;
+					this.$message.error(e.error);
+				});
+		},
+		onDelete() {
+			this.$confirm('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+				.then(() => {
+					return this.$http.post(`/metaPlayerWear/del/${this.formData.id}`);
+				})
+				.then(() => {
+					this.$message.success('删除成功');
+					this.$router.go(-1);
+				})
+				.catch(e => {
+					if (e !== 'cancel') {
+						console.log(e);
+						this.$message.error((e || {}).error || '删除失败');
+					}
+				});
+		}
+	}
+};
+</script>
+<style lang="less" scoped>
+</style>

+ 192 - 0
src/main/vue/src/views/MetaPlayerWearList.vue

@@ -0,0 +1,192 @@
+<template>
+	<div class="list-view">
+		<page-title>
+			<el-button
+				@click="addRow"
+				type="primary"
+				icon="el-icon-plus"
+				:disabled="fetchingData || downloading"
+				class="filter-item"
+			>
+				新增
+			</el-button>
+			<el-button
+				@click="download"
+				icon="el-icon-upload2"
+				:loading="downloading"
+				:disabled="fetchingData"
+				class="filter-item"
+			>
+				导出
+			</el-button>
+		</page-title>
+		<div class="filters-container">
+			<el-input
+				placeholder="搜索..."
+				v-model="search"
+				clearable
+				class="filter-item search"
+				@keyup.enter.native="getData"
+			>
+				<el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
+			</el-input>
+		</div>
+		<el-table
+			:data="tableData"
+			row-key="id"
+			ref="table"
+			header-row-class-name="table-header-row"
+			header-cell-class-name="table-header-cell"
+			row-class-name="table-row"
+			cell-class-name="table-cell"
+			:height="tableHeight"
+			v-loading="fetchingData"
+		>
+			<el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+			<el-table-column prop="id" label="ID" width="100"> </el-table-column>
+			<el-table-column prop="userId" label="用户id"> </el-table-column>
+			<el-table-column prop="model.name" label="模型"> </el-table-column>
+			<el-table-column prop="type" label="类型" :formatter="typeFormatter"> </el-table-column>
+			<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>
+				</template>
+			</el-table-column>
+		</el-table>
+		<div class="pagination-wrapper">
+			<!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)"> 批量编辑 </el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1"> 批量操作1 </el-button>
+                    <el-button @click="operation2"> 批量操作2 </el-button>
+                    <el-button @click="toggleMultipleMode(false)"> 取消 </el-button>
+                </el-button-group>
+            </div> -->
+			<el-pagination
+				background
+				@size-change="onSizeChange"
+				@current-change="onCurrentChange"
+				:current-page="page"
+				:page-sizes="[10, 20, 30, 40, 50]"
+				:page-size="pageSize"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="totalElements"
+			>
+			</el-pagination>
+		</div>
+	</div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+	name: 'MetaPlayerWearList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/metaPlayerWear/all',
+			downloading: false,
+			typeOptions: [
+				{ label: '头饰', value: 'HEADDRESS' },
+				{ label: '衣服', value: 'CLOTHES' },
+				{ label: '裤子', value: 'TROUSERS' },
+				{ label: '鞋子', value: 'SHOES' },
+				{ label: '动作', value: 'ACTION' }
+			]
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		typeFormatter(row, column, cellValue, index) {
+			let selectedOption = this.typeOptions.find(i => i.value === cellValue);
+			if (selectedOption) {
+				return selectedOption.label;
+			}
+			return '';
+		},
+		beforeGetData() {
+			return { search: this.search, query: { del: false } };
+		},
+		toggleMultipleMode(multipleMode) {
+			this.multipleMode = multipleMode;
+			if (!multipleMode) {
+				this.$refs.table.clearSelection();
+			}
+		},
+		addRow() {
+			this.$router.push({
+				path: '/metaPlayerWearEdit',
+				query: {
+					...this.$route.query
+				}
+			});
+		},
+		editRow(row) {
+			this.$router.push({
+				path: '/metaPlayerWearEdit',
+				query: {
+					id: row.id
+				}
+			});
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/metaPlayerWear/excel', {
+					responseType: 'blob',
+					params: { size: 10000 }
+				})
+				.then(res => {
+					console.log(res);
+					this.downloading = false;
+					const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+					const link = document.createElement('a');
+					link.href = downloadUrl;
+					link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+					document.body.appendChild(link);
+					link.click();
+					link.remove();
+				})
+				.catch(e => {
+					console.log(e);
+					this.downloading = false;
+					this.$message.error(e.error);
+				});
+		},
+		operation1() {
+			this.$notify({
+				title: '提示',
+				message: this.selection
+			});
+		},
+		operation2() {
+			this.$message('操作2');
+		},
+		deleteRow(row) {
+			this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+				.then(() => {
+					return this.$http.post(`/metaPlayerWear/del/${row.id}`);
+				})
+				.then(() => {
+					this.$message.success('删除成功');
+					this.getData();
+				})
+				.catch(e => {
+					if (e !== 'cancel') {
+						this.$message.error(e.error);
+					}
+				});
+		}
+	}
+};
+</script>
+<style lang="less" scoped>
+</style>

+ 192 - 0
src/main/vue/src/views/MetaSpatialInfoEdit.vue

@@ -0,0 +1,192 @@
+<template>
+	<div class="edit-view">
+		<page-title>
+			<el-button @click="$router.go(-1)" :disabled="saving"> 取消 </el-button>
+			<el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id"> 删除 </el-button>
+			<el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+		</page-title>
+		<div class="edit-view__content-wrapper">
+			<div class="edit-view__content-section">
+				<el-form
+					:model="formData"
+					:rules="rules"
+					ref="form"
+					label-width="116px"
+					label-position="right"
+					size="small"
+					style="max-width: 500px"
+				>
+					<el-form-item prop="region" label="所属区域">
+						<el-select v-model="formData.region" clearable filterable placeholder="请选择">
+							<el-option
+								v-for="item in regionOptions"
+								:key="item.value"
+								:label="item.label"
+								:value="item.value"
+							>
+							</el-option>
+						</el-select>
+					</el-form-item>
+					<el-form-item prop="x" label="区域内坐标x轴">
+						<el-input v-model="formData.x"> </el-input>
+					</el-form-item>
+					<el-form-item prop="y" label="区域内坐标y轴">
+						<el-input v-model="formData.y"> </el-input>
+					</el-form-item>
+                    <el-form-item prop="z" label="区域内坐标z轴">
+						<el-input v-model="formData.z"> </el-input>
+					</el-form-item>
+					<el-form-item prop="sale" label="挂售">
+						<el-switch v-model="formData.sale"> </el-switch>
+					</el-form-item>
+					<el-form-item prop="userId" label="所属用户id">
+						<el-input-number type="number" v-model="formData.userId"> </el-input-number>
+					</el-form-item>
+					<el-form-item prop="size" label="空间大小(m)²">
+						<el-input-number type="number" v-model="formData.size"> </el-input-number>
+					</el-form-item>
+					<el-form-item class="form-submit">
+						<el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+						<el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+							删除
+						</el-button>
+						<el-button @click="$router.go(-1)" :disabled="saving"> 取消 </el-button>
+					</el-form-item>
+				</el-form>
+			</div>
+		</div>
+	</div>
+</template>
+<script>
+export default {
+	name: 'MetaSpatialInfoEdit',
+	created() {
+		if (this.$route.query.id) {
+			this.$http
+				.get('metaSpatialInfo/get/' + this.$route.query.id)
+				.then(res => {
+                    if (res.coordinate) {
+                        res.x = res.coordinate.x
+                        res.y = res.coordinate.y
+                        res.z = res.coordinate.z
+                    }
+					this.formData = res;
+				})
+				.catch(e => {
+					console.log(e);
+					this.$message.error(e.error);
+				});
+		}
+	},
+	data() {
+		return {
+			saving: false,
+			formData: {},
+			rules: {
+				region: [
+					{
+						required: true,
+						message: '请输入所属区域',
+						trigger: 'blur'
+					}
+				],
+				x: [
+					{
+						required: true,
+						message: '请输入区域内坐标x轴',
+						trigger: 'blur'
+					}
+				],
+				y: [
+					{
+						required: true,
+						message: '请输入区域内坐标y轴',
+						trigger: 'blur'
+					}
+				],
+                z: [
+					{
+						required: true,
+						message: '请输入区域内坐标z轴',
+						trigger: 'blur'
+					}
+				],
+				userId: [
+					{
+						required: true,
+						message: '请输入所属用户id',
+						trigger: 'blur'
+					}
+				],
+				size: [
+					{
+						required: true,
+						message: '请输入空间大小',
+						trigger: 'blur'
+					}
+				]
+			},
+			regionOptions: [
+				{ label: '1区', value: 'ONE' },
+				{ label: '2区', value: 'TWO' },
+				{ label: '3区', value: 'THREE' },
+				{ label: '4区', value: 'FOUR' },
+				{ label: '5区', value: 'FIVE' },
+				{ label: '6区', value: 'SIX' },
+				{ label: '7区', value: 'SEVEN' },
+				{ label: '8区', value: 'EIGHT' },
+				{ label: '9区', value: 'NINE' }
+			]
+		};
+	},
+	methods: {
+		onSave() {
+			this.$refs.form.validate(valid => {
+				if (valid) {
+					this.submit();
+				} else {
+					return false;
+				}
+			});
+		},
+		submit() {
+			let data = { ...this.formData };
+            data.coordinate = {x:this.formData.x, y:this.formData.y, z:this.formData.z}
+            delete data.x
+            delete data.y
+            delete data.z
+			this.saving = true;
+			this.$http
+				.post('/metaSpatialInfo/save', data, { body: 'json' })
+				.then(res => {
+					this.saving = false;
+					this.$message.success('成功');
+					this.$router.go(-1);
+				})
+				.catch(e => {
+					console.log(e);
+					this.saving = false;
+					this.$message.error(e.error);
+				});
+		},
+		onDelete() {
+			this.$confirm('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+				.then(() => {
+					return this.$http.post(`/metaSpatialInfo/del/${this.formData.id}`);
+				})
+				.then(() => {
+					this.$message.success('删除成功');
+					this.$router.go(-1);
+				})
+				.catch(e => {
+					if (e !== 'cancel') {
+						console.log(e);
+						this.$message.error((e || {}).error || '删除失败');
+					}
+				});
+		}
+	}
+};
+</script>
+<style lang="less" scoped>
+</style>

+ 206 - 0
src/main/vue/src/views/MetaSpatialInfoList.vue

@@ -0,0 +1,206 @@
+<template>
+	<div class="list-view">
+		<page-title>
+			<el-button
+				@click="addRow"
+				type="primary"
+				icon="el-icon-plus"
+				:disabled="fetchingData || downloading"
+				class="filter-item"
+			>
+				新增
+			</el-button>
+			<el-button
+				@click="download"
+				icon="el-icon-upload2"
+				:loading="downloading"
+				:disabled="fetchingData"
+				class="filter-item"
+			>
+				导出
+			</el-button>
+		</page-title>
+		<div class="filters-container">
+			<el-input
+				placeholder="搜索..."
+				v-model="search"
+				clearable
+				class="filter-item search"
+				@keyup.enter.native="getData"
+			>
+				<el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
+			</el-input>
+		</div>
+		<el-table
+			:data="tableData"
+			row-key="id"
+			ref="table"
+			header-row-class-name="table-header-row"
+			header-cell-class-name="table-header-cell"
+			row-class-name="table-row"
+			cell-class-name="table-cell"
+			:height="tableHeight"
+			v-loading="fetchingData"
+		>
+			<el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+			<el-table-column prop="id" label="ID" width="100"> </el-table-column>
+			<el-table-column prop="region" label="所属区域" :formatter="regionFormatter"> </el-table-column>
+			<el-table-column prop="coordinate" label="区域内坐标">
+                <template slot-scope="{ row }">
+					{{ 'x=' + row.coordinate.x + ' , ' + 'y=' + row.coordinate.y + ' , ' + 'z=' + row.coordinate.z }} 
+				</template>
+            </el-table-column>
+			<el-table-column prop="sale" label="状态">
+				<template slot-scope="{ row }">
+					{{ row.sale ? '可售' : '未售' }} 
+				</template>
+			</el-table-column>
+			<el-table-column prop="userId" label="所属用户id"> </el-table-column>
+			<el-table-column prop="size" label="空间大小"> </el-table-column>
+			<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>
+				</template>
+			</el-table-column>
+		</el-table>
+		<div class="pagination-wrapper">
+			<!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)"> 批量编辑 </el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1"> 批量操作1 </el-button>
+                    <el-button @click="operation2"> 批量操作2 </el-button>
+                    <el-button @click="toggleMultipleMode(false)"> 取消 </el-button>
+                </el-button-group>
+            </div> -->
+			<el-pagination
+				background
+				@size-change="onSizeChange"
+				@current-change="onCurrentChange"
+				:current-page="page"
+				:page-sizes="[10, 20, 30, 40, 50]"
+				:page-size="pageSize"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="totalElements"
+			>
+			</el-pagination>
+		</div>
+	</div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+	name: 'MetaSpatialInfoList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/metaSpatialInfo/all',
+			downloading: false,
+			regionOptions: [
+				{ label: '1区', value: 'ONE' },
+				{ label: '2区', value: 'TWO' },
+				{ label: '3区', value: 'THREE' },
+				{ label: '4区', value: 'FOUR' },
+				{ label: '5区', value: 'FIVE' },
+				{ label: '6区', value: 'SIX' },
+				{ label: '7区', value: 'SEVEN' },
+				{ label: '8区', value: 'EIGHT' },
+				{ label: '9区', value: 'NINE' }
+			]
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		regionFormatter(row, column, cellValue, index) {
+			let selectedOption = this.regionOptions.find(i => i.value === cellValue);
+			if (selectedOption) {
+				return selectedOption.label;
+			}
+			return '';
+		},
+		beforeGetData() {
+			return { search: this.search, query: { del: false } };
+		},
+		toggleMultipleMode(multipleMode) {
+			this.multipleMode = multipleMode;
+			if (!multipleMode) {
+				this.$refs.table.clearSelection();
+			}
+		},
+		addRow() {
+			this.$router.push({
+				path: '/metaSpatialInfoEdit',
+				query: {
+					...this.$route.query
+				}
+			});
+		},
+		editRow(row) {
+			this.$router.push({
+				path: '/metaSpatialInfoEdit',
+				query: {
+					id: row.id
+				}
+			});
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/metaSpatialInfo/excel', {
+					responseType: 'blob',
+					params: { size: 10000 }
+				})
+				.then(res => {
+					console.log(res);
+					this.downloading = false;
+					const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+					const link = document.createElement('a');
+					link.href = downloadUrl;
+					link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+					document.body.appendChild(link);
+					link.click();
+					link.remove();
+				})
+				.catch(e => {
+					console.log(e);
+					this.downloading = false;
+					this.$message.error(e.error);
+				});
+		},
+		operation1() {
+			this.$notify({
+				title: '提示',
+				message: this.selection
+			});
+		},
+		operation2() {
+			this.$message('操作2');
+		},
+		deleteRow(row) {
+			this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+				.then(() => {
+					return this.$http.post(`/metaSpatialInfo/del/${row.id}`);
+				})
+				.then(() => {
+					this.$message.success('删除成功');
+					this.getData();
+				})
+				.catch(e => {
+					if (e !== 'cancel') {
+						this.$message.error(e.error);
+					}
+				});
+		}
+	}
+};
+</script>
+<style lang="less" scoped>
+</style>

+ 125 - 0
src/main/vue/src/views/OnOffEdit.vue

@@ -0,0 +1,125 @@
+<template>
+    <div class="edit-view">
+        <page-title>
+            <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+            <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                删除
+            </el-button>
+            <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+        </page-title>
+        <div class="edit-view__content-wrapper">
+            <div class="edit-view__content-section">
+                <el-form :model="formData" :rules="rules" ref="form" label-width="100px" label-position="right"
+                         size="small"
+                         style="max-width: 500px;">
+                        <el-form-item prop="status" label="状态">
+                                    <el-select v-model="formData.status" clearable filterable placeholder="请选择">
+                                        <el-option
+                                                v-for="item in statusOptions"
+                                                :key="item.value"
+                                                :label="item.label"
+                                                :value="item.value">
+                                        </el-option>
+                                    </el-select>
+                        </el-form-item>
+                        <el-form-item prop="remark" label="备注">
+                                    <el-input v-model="formData.remark"></el-input>
+                        </el-form-item>
+                    <el-form-item class="form-submit">
+                        <el-button @click="onSave" :loading="saving" type="primary">
+                            保存
+                        </el-button>
+                        <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                            删除
+                        </el-button>
+                        <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+    export default {
+        name: 'OnOffEdit',
+        created() {
+            if (this.$route.query.id) {
+                this.$http
+                    .get('onOff/get/' + this.$route.query.id)
+                    .then(res => {
+                        this.formData = res;
+                    })
+                    .catch(e => {
+                        console.log(e);
+                        this.$message.error(e.error);
+                    });
+            }
+        },
+        data() {
+            return {
+                saving: false,
+                formData: {
+                },
+                rules: {
+                    status: [
+                        {
+                            required: true,
+                            message: '请输入状态:开/关',
+                            trigger: 'blur'
+                        },
+                    ],
+                    remark: [
+                        {
+                            required: true,
+                            message: '请输入remark',
+                            trigger: 'blur'
+                        },
+                    ],
+                },
+                statusOptions: [{"label":"开","value":"ON"},{"label":"关","value":"OFF"}],
+            }
+        },
+        methods: {
+            onSave() {
+                this.$refs.form.validate((valid) => {
+                    if (valid) {
+                        this.submit();
+                    } else {
+                        return false;
+                    }
+                });
+            },
+            submit() {
+                let data = {...this.formData};
+
+                this.saving = true;
+                this.$http
+                    .post('/onOff/save', data, {body: 'json'})
+                    .then(res => {
+                        this.saving = false;
+                        this.$message.success('成功');
+                        this.$router.go(-1);
+                    })
+                    .catch(e => {
+                        console.log(e);
+                        this.saving = false;
+                        this.$message.error(e.error);
+                    });
+            },
+            onDelete() {
+                this.$confirm('删除将无法恢复,确认要删除么?', '警告', {type: 'error'}).then(() => {
+                    return this.$http.post(`/onOff/del/${this.formData.id}`)
+                }).then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                }).catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error((e || {}).error || '删除失败');
+                    }
+                })
+            },
+        }
+    }
+</script>
+<style lang="less" scoped></style>

+ 188 - 0
src/main/vue/src/views/OnOffList.vue

@@ -0,0 +1,188 @@
+<template>
+	<div class="list-view">
+		<page-title>
+			<el-button
+				@click="addRow"
+				type="primary"
+				icon="el-icon-plus"
+				:disabled="fetchingData || downloading"
+				class="filter-item"
+			>
+				新增
+			</el-button>
+			<el-button
+				@click="download"
+				icon="el-icon-upload2"
+				:loading="downloading"
+				:disabled="fetchingData"
+				class="filter-item"
+			>
+				导出
+			</el-button>
+		</page-title>
+		<div class="filters-container">
+			<el-input
+				placeholder="搜索..."
+				v-model="search"
+				clearable
+				class="filter-item search"
+				@keyup.enter.native="getData"
+			>
+				<el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
+			</el-input>
+		</div>
+		<el-table
+			:data="tableData"
+			row-key="id"
+			ref="table"
+			header-row-class-name="table-header-row"
+			header-cell-class-name="table-header-cell"
+			row-class-name="table-row"
+			cell-class-name="table-cell"
+			:height="tableHeight"
+			v-loading="fetchingData"
+		>
+			<el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+			<el-table-column prop="id" label="ID" width="100"> </el-table-column>
+			<el-table-column prop="status" label="状态" :formatter="statusFormatter"> </el-table-column>
+			<el-table-column prop="remark" label="备注"> </el-table-column>
+			<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>
+				</template>
+			</el-table-column>
+		</el-table>
+		<div class="pagination-wrapper">
+			<!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)"> 批量编辑 </el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1"> 批量操作1 </el-button>
+                    <el-button @click="operation2"> 批量操作2 </el-button>
+                    <el-button @click="toggleMultipleMode(false)"> 取消 </el-button>
+                </el-button-group>
+            </div> -->
+			<el-pagination
+				background
+				@size-change="onSizeChange"
+				@current-change="onCurrentChange"
+				:current-page="page"
+				:page-sizes="[10, 20, 30, 40, 50]"
+				:page-size="pageSize"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="totalElements"
+			>
+			</el-pagination>
+		</div>
+	</div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+	name: 'OnOffList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/onOff/all',
+			downloading: false,
+			statusOptions: [
+				{ label: '开', value: 'ON' },
+				{ label: '关', value: 'OFF' }
+			]
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		statusFormatter(row, column, cellValue, index) {
+			let selectedOption = this.statusOptions.find(i => i.value === cellValue);
+			if (selectedOption) {
+				return selectedOption.label;
+			}
+			return '';
+		},
+		beforeGetData() {
+			return { search: this.search, query: { del: false } };
+		},
+		toggleMultipleMode(multipleMode) {
+			this.multipleMode = multipleMode;
+			if (!multipleMode) {
+				this.$refs.table.clearSelection();
+			}
+		},
+		addRow() {
+			this.$router.push({
+				path: '/onOffEdit',
+				query: {
+					...this.$route.query
+				}
+			});
+		},
+		editRow(row) {
+			this.$router.push({
+				path: '/onOffEdit',
+				query: {
+					id: row.id
+				}
+			});
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/onOff/excel', {
+					responseType: 'blob',
+					params: { size: 10000 }
+				})
+				.then(res => {
+					console.log(res);
+					this.downloading = false;
+					const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+					const link = document.createElement('a');
+					link.href = downloadUrl;
+					link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+					document.body.appendChild(link);
+					link.click();
+					link.remove();
+				})
+				.catch(e => {
+					console.log(e);
+					this.downloading = false;
+					this.$message.error(e.error);
+				});
+		},
+		operation1() {
+			this.$notify({
+				title: '提示',
+				message: this.selection
+			});
+		},
+		operation2() {
+			this.$message('操作2');
+		},
+		deleteRow(row) {
+			this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+				.then(() => {
+					return this.$http.post(`/onOff/del/${row.id}`);
+				})
+				.then(() => {
+					this.$message.success('删除成功');
+					this.getData();
+				})
+				.catch(e => {
+					if (e !== 'cancel') {
+						this.$message.error(e.error);
+					}
+				});
+		}
+	}
+};
+</script>
+<style lang="less" scoped>
+</style>

+ 10 - 9
src/main/vue/src/views/RarityLabelEdit.vue

@@ -22,7 +22,7 @@
                                             <el-option
                                                     v-for="item in labelOptions"
                                                     :key="item.value"
-                                                    :label="item.label"
+                                                    :label="item.name"
                                                     :value="item.value">
                                             </el-option>
                                         </el-select>
@@ -50,8 +50,12 @@
                 this.$http
                     .get('rarityLabel/get/' + this.$route.query.id)
                     .then(res => {
+                        var label = []
                         if (res.label) {
-                            res.label = res.label.split(',');
+                            for(var i=0;i<res.label.length;i++){
+                                label.push(res.label[i].name)
+                            }
+                            res.label = label;
                         } else {
                             res.label = [];
                         }
@@ -84,7 +88,7 @@
                         },
                     ],
                 },
-                labelOptions: [{"label":"SSR","value":"SSR"},{"label":"SR","value":"SR"},{"label":"R","value":"R"},{"label":"U","value":"U"}],
+                labelOptions: [{"name":"SSR","value":"SSR"},{"name":"SR","value":"SR"},{"name":"R","value":"R"},{"name":"U","value":"U"}],
             }
         },
         methods: {
@@ -99,14 +103,11 @@
             },
             submit() {
                 let data = {...this.formData};
-                let str = '';
+                var labels = []
                 for(var i=0;i<data.label.length;i++){
-                    str += data.label[i] + ",";
+                    labels.push({name:data.label[i], value:data.label[i]})
                 }
-                if (str.length > 0) {
-                    str = str.substr(0, str.length - 1);
-                }
-                data.label = str;
+                data.label = labels;
                 this.saving = true;
                 this.$http
                     .post('/rarityLabel/save', data, {body: 'json'})

+ 14 - 9
src/main/vue/src/views/RarityLabelList.vue

@@ -27,14 +27,9 @@
             <el-table-column v-if="multipleMode" align="center" type="selection"
                              width="50">
             </el-table-column>
-            <el-table-column prop="id" label="ID" width="100">
-            </el-table-column>
-                                <el-table-column prop="name" label="name"
->
-                    </el-table-column>
-                    <el-table-column prop="label" label="label"
->
-                    </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"></el-table-column>
+            <el-table-column prop="name" label="name"></el-table-column>
+            <el-table-column prop="label" label="label" :formatter="labelFormatter"></el-table-column>
             <el-table-column
                     label="操作"
                     align="center"
@@ -86,6 +81,16 @@
             }
         },
         methods: {
+            labelFormatter(row, column, cellValue, index) {
+                let str = ''
+                for(var i=0;i<cellValue.length;i++){
+                    str += cellValue[i].name + ","
+                }
+                if (str.length > 0) {
+                    str = str.substr(0, str.length - 1);
+                }
+                return str;
+        },
             beforeGetData() {
                 return { search: this.search, query: { del: false } };
             },
@@ -114,7 +119,7 @@
             download() {
                 this.downloading = true;
                 this.$axios
-                    .get("/rarityLabel/excel", { 
+                    .get("/rarityLabel/excel", {
                         responseType: "blob",
                         params: { size: 10000 }
                     })

+ 8 - 6
src/test/java/com/izouma/nineth/RedisTest.java

@@ -3,6 +3,7 @@ package com.izouma.nineth;
 import com.izouma.nineth.config.RedisKeys;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundHashOperations;
 import org.springframework.data.redis.core.RedisTemplate;
 
 public class RedisTest extends ApplicationTests {
@@ -10,12 +11,13 @@ public class RedisTest extends ApplicationTests {
     private RedisTemplate<String, Object> redisTemplate;
 
     @Test
-    public void testOrder() {
-        System.out.println(redisTemplate.opsForValue().get(RedisKeys.CREATE_ORDER + "931502789098471424"));
+    public void testHash() {
+        BoundHashOperations<String, Object, Object> operations = redisTemplate.boundHashOps(RedisKeys.DRAW_BLIND_BOX + "111");
+        System.out.println(operations.entries());
+        operations.putIfAbsent("1", "1");
+        operations.putIfAbsent("1", "2");
+        System.out.println(operations.entries());
+        System.out.println(operations.get("111"));
     }
 
-    @Test
-    public void testasdf() {
-        System.out.println(redisTemplate.opsForValue().get(RedisKeys.COLLECTION_STOCK + "sdfasdf"));
-    }
 }

+ 3 - 2
src/test/java/com/izouma/nineth/service/CollectionServiceTest.java

@@ -32,6 +32,7 @@ import java.nio.file.Files;
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -106,7 +107,7 @@ class CollectionServiceTest extends ApplicationTests {
     }
 
     @Test
-    public void createBlindBox() throws IOException {
+    public void createBlindBox() throws Exception {
 //        List<Long> arr = Arrays.asList(6862110L, 6862511L, 6862516L, 6862533L, 6862689L, 6862792L, 6862867L, 6863009L, 6863047L,
 //                6863188L, 6863438L, 6863449L, 6863588L, 6863608L, 6863671L, 6863745L, 6863760L, 6863938L, 6863954L, 6864008L, 6864081L);
         List<Collection> items = collectionRepo.findByNameLike("MUGEN无限:未央宗#%")
@@ -218,7 +219,7 @@ class CollectionServiceTest extends ApplicationTests {
     }
 
     @Test
-    public void createBlindBox1() throws IOException {
+    public void createBlindBox1() throws Exception {
 
         File file = new File("/Users/qiufangchao/Desktop/mingdan.xlsx");
         UploadDataListener<TestExcelDTO> listener = new UploadDataListener<>();