Pārlūkot izejas kodu

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

 Conflicts:
	src/test/java/com/izouma/nineth/service/UserServiceTest.java
lidongze 2 gadi atpakaļ
vecāks
revīzija
70d067f613
78 mainītis faili ar 3509 papildinājumiem un 640 dzēšanām
  1. 2 0
      src/main/java/com/izouma/nineth/converter/MetaGameAwardConverter.java
  2. 2 0
      src/main/java/com/izouma/nineth/converter/MetaGameAwardListConverter.java
  3. 27 0
      src/main/java/com/izouma/nineth/converter/MetaLuckyDrawAwardConverter.java
  4. 34 0
      src/main/java/com/izouma/nineth/converter/MetaLuckyDrawAwardListConverter.java
  5. 2 0
      src/main/java/com/izouma/nineth/converter/MetaPlayerRoleListConverter.java
  6. 2 0
      src/main/java/com/izouma/nineth/converter/MetaZombieDTOListConverter.java
  7. 1 1
      src/main/java/com/izouma/nineth/domain/Asset.java
  8. 21 6
      src/main/java/com/izouma/nineth/domain/MetaAdvertRecord.java
  9. 1 5
      src/main/java/com/izouma/nineth/domain/MetaGameBoxPoints.java
  10. 70 0
      src/main/java/com/izouma/nineth/domain/MetaLuckyDraw.java
  11. 31 0
      src/main/java/com/izouma/nineth/domain/MetaLuckyDrawAwardModel.java
  12. 43 0
      src/main/java/com/izouma/nineth/domain/MetaLuckyDrawAwardReceiveRecord.java
  13. 54 0
      src/main/java/com/izouma/nineth/domain/MetaProp.java
  14. 9 1
      src/main/java/com/izouma/nineth/domain/MetaSwitch.java
  15. 44 16
      src/main/java/com/izouma/nineth/domain/PublicScreenChat.java
  16. 6 0
      src/main/java/com/izouma/nineth/domain/TradeAuctionOrder.java
  17. 20 0
      src/main/java/com/izouma/nineth/enums/ChatType.java
  18. 5 7
      src/main/java/com/izouma/nineth/enums/MetaPropTriggerType.java
  19. 26 0
      src/main/java/com/izouma/nineth/enums/MetaPropType.java
  20. 20 0
      src/main/java/com/izouma/nineth/enums/MetaPropUsedType.java
  21. 19 0
      src/main/java/com/izouma/nineth/enums/MetaSwitchType.java
  22. 5 1
      src/main/java/com/izouma/nineth/repo/DomainOrderRepo.java
  23. 3 2
      src/main/java/com/izouma/nineth/repo/MetaAdvertRecordRepo.java
  24. 3 4
      src/main/java/com/izouma/nineth/repo/MetaGameBoxPointsRepo.java
  25. 18 0
      src/main/java/com/izouma/nineth/repo/MetaLuckyDrawAwardReceiveRecordRepo.java
  26. 18 0
      src/main/java/com/izouma/nineth/repo/MetaLuckyDrawRepo.java
  27. 16 0
      src/main/java/com/izouma/nineth/repo/MetaPropRepo.java
  28. 2 1
      src/main/java/com/izouma/nineth/repo/MetaSwitchRepo.java
  29. 1 1
      src/main/java/com/izouma/nineth/service/AssetService.java
  30. 96 26
      src/main/java/com/izouma/nineth/service/DomainOrderService.java
  31. 26 0
      src/main/java/com/izouma/nineth/service/MetaAdvertRecordService.java
  32. 3 7
      src/main/java/com/izouma/nineth/service/MetaGameBoxPointsService.java
  33. 1 1
      src/main/java/com/izouma/nineth/service/MetaGameCopyService.java
  34. 20 0
      src/main/java/com/izouma/nineth/service/MetaLuckyDrawAwardReceiveRecordService.java
  35. 20 0
      src/main/java/com/izouma/nineth/service/MetaLuckyDrawService.java
  36. 20 0
      src/main/java/com/izouma/nineth/service/MetaPropService.java
  37. 5 5
      src/main/java/com/izouma/nineth/service/OrderPayService.java
  38. 62 24
      src/main/java/com/izouma/nineth/service/TradeAuctionOrderService.java
  39. 36 0
      src/main/java/com/izouma/nineth/service/UserService.java
  40. 25 1
      src/main/java/com/izouma/nineth/utils/LotteryUtils.java
  41. 0 1
      src/main/java/com/izouma/nineth/utils/excel/ExcelUtils.java
  42. 0 42
      src/main/java/com/izouma/nineth/utils/excel/MetaPointTypeEnumConverter.java
  43. 23 6
      src/main/java/com/izouma/nineth/web/AuthenticationController.java
  44. 3 26
      src/main/java/com/izouma/nineth/web/MetaAdvertRecordController.java
  45. 1 2
      src/main/java/com/izouma/nineth/web/MetaGameBoxPointsController.java
  46. 89 0
      src/main/java/com/izouma/nineth/web/MetaLuckyDrawAwardReceiveRecordController.java
  47. 128 0
      src/main/java/com/izouma/nineth/web/MetaLuckyDrawController.java
  48. 60 0
      src/main/java/com/izouma/nineth/web/MetaPropController.java
  49. 11 2
      src/main/java/com/izouma/nineth/web/MetaSwitchController.java
  50. BIN
      src/main/resources/font/VonwaonBitmap_16pxLite.ttf
  51. 0 0
      src/main/resources/genjson/MetaLuckyDraw.json
  52. 1 0
      src/main/resources/genjson/MetaLuckyDrawAwardReceiveRecord.json
  53. 1 0
      src/main/resources/genjson/MetaProp.json
  54. 76 11
      src/main/vue/src/router.js
  55. 5 0
      src/main/vue/src/util/regRules.js
  56. 178 162
      src/main/vue/src/views/DomainOrderList.vue
  57. 2 1
      src/main/vue/src/views/MetaAccessoriesEdit.vue
  58. 165 125
      src/main/vue/src/views/MetaAdvertRecordEdit.vue
  59. 8 10
      src/main/vue/src/views/MetaAdvertRecordList.vue
  60. 2 2
      src/main/vue/src/views/MetaCommonSwitchEdit.vue
  61. 206 0
      src/main/vue/src/views/MetaCommonSwitchList.vue
  62. 2 1
      src/main/vue/src/views/MetaGameCopyEdit.vue
  63. 2 2
      src/main/vue/src/views/MetaGameCopyList.vue
  64. 2 1
      src/main/vue/src/views/MetaGameStageAwardEdit.vue
  65. 118 0
      src/main/vue/src/views/MetaLuckyDrawAwardReceiveRecordList.vue
  66. 491 0
      src/main/vue/src/views/MetaLuckyDrawEdit.vue
  67. 209 0
      src/main/vue/src/views/MetaLuckyDrawList.vue
  68. 232 0
      src/main/vue/src/views/MetaPropEdit.vue
  69. 223 0
      src/main/vue/src/views/MetaPropList.vue
  70. 2 1
      src/main/vue/src/views/MetaSignAwardEdit.vue
  71. 2 1
      src/main/vue/src/views/MetaSignEdit.vue
  72. 2 1
      src/main/vue/src/views/MetaSpatialInfoEdit.vue
  73. 2 1
      src/main/vue/src/views/MetaTaskEdit.vue
  74. 128 0
      src/main/vue/src/views/MetaWebsocketSwitchEdit.vue
  75. 5 5
      src/main/vue/src/views/MetaWebsocketSwitchList.vue
  76. 158 0
      src/main/vue/src/views/PrivateScreenChatList.vue
  77. 133 123
      src/main/vue/src/views/PublicScreenChatList.vue
  78. 20 5
      src/test/java/com/izouma/nineth/service/UserServiceTest.java

+ 2 - 0
src/main/java/com/izouma/nineth/converter/MetaGameAwardConverter.java

@@ -5,7 +5,9 @@ import com.izouma.nineth.domain.MetaGameAward;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
 
+@Converter
 public class MetaGameAwardConverter implements AttributeConverter<MetaGameAward, String> {
 
     @Override

+ 2 - 0
src/main/java/com/izouma/nineth/converter/MetaGameAwardListConverter.java

@@ -6,8 +6,10 @@ import com.izouma.nineth.domain.MetaGameAward;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
 import java.util.List;
 
+@Converter
 public class MetaGameAwardListConverter implements AttributeConverter<List<MetaGameAward>, String> {
 
     @Override

+ 27 - 0
src/main/java/com/izouma/nineth/converter/MetaLuckyDrawAwardConverter.java

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

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

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

+ 2 - 0
src/main/java/com/izouma/nineth/converter/MetaPlayerRoleListConverter.java

@@ -6,8 +6,10 @@ import com.izouma.nineth.dto.MetaPlayerRole;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
 import java.util.List;
 
+@Converter
 public class MetaPlayerRoleListConverter implements AttributeConverter<List<MetaPlayerRole>, String> {
 
     @Override

+ 2 - 0
src/main/java/com/izouma/nineth/converter/MetaZombieDTOListConverter.java

@@ -6,8 +6,10 @@ import com.izouma.nineth.dto.MetaZombieDTO;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
 import java.util.List;
 
+@Converter
 public class MetaZombieDTOListConverter implements AttributeConverter<List<MetaZombieDTO>, String> {
 
     @Override

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

@@ -346,7 +346,7 @@ public class Asset extends CollectionBaseEntity {
                 .minter(user.getNickname())
                 .minterId(user.getId())
                 .minterAvatar(user.getAvatar())
-                .name("元域名:" + domainOrder.getDomainName())
+                .name("RID元宇宙域名 " + domainOrder.getDomainName())
                 .pic(domainOrder.getPic())
                 .model3d(null)
                 .category("元域名")

+ 21 - 6
src/main/java/com/izouma/nineth/domain/MetaAdvertRecord.java

@@ -8,7 +8,9 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import javax.persistence.Column;
 import javax.persistence.Entity;
+import java.math.BigDecimal;
 
 @Data
 @AllArgsConstructor
@@ -25,12 +27,25 @@ public class MetaAdvertRecord extends BaseEntity {
     @ExcelProperty("链接地址")
     private String link;
 
-    @ApiModelProperty("用途")
-    @ExcelProperty("用途")
-    private int application;
+    @ApiModelProperty("编号")
+    @ExcelProperty("编号")
+    private int number;
 
-    @ApiModelProperty("是否发布")
-    @ExcelProperty("是否发布")
-    private boolean publish;
+    @ApiModelProperty("区域")
+    @ExcelProperty("区域")
+    private int region;
+
+    @ApiModelProperty("名称")
+    @ExcelProperty("名称")
+    private String name;
+
+    @ApiModelProperty("详情")
+    @ExcelProperty("详情")
+    private String detail;
+
+    @ApiModelProperty("价格")
+    @ExcelProperty("价格")
+    @Column(precision = 10, scale = 2)
+    private BigDecimal price;
 
 }

+ 1 - 5
src/main/java/com/izouma/nineth/domain/MetaGameBoxPoints.java

@@ -3,7 +3,6 @@ package com.izouma.nineth.domain;
 import com.alibaba.excel.annotation.ExcelIgnore;
 import com.alibaba.excel.annotation.ExcelProperty;
 import com.izouma.nineth.annotations.Searchable;
-import com.izouma.nineth.enums.MetaPointTypeEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
@@ -11,8 +10,6 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
 import javax.persistence.Transient;
 
 @Data
@@ -29,8 +26,7 @@ public class MetaGameBoxPoints extends BaseEntity {
 
     @ApiModelProperty("类型")
     @ExcelProperty("类型")
-    @Enumerated(EnumType.STRING)
-    private MetaPointTypeEnum type;
+    private int type;
 
     @ApiModelProperty("昵称")
     @ExcelIgnore

+ 70 - 0
src/main/java/com/izouma/nineth/domain/MetaLuckyDraw.java

@@ -0,0 +1,70 @@
+package com.izouma.nineth.domain;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.izouma.nineth.converter.MetaLuckyDrawAwardListConverter;
+import com.izouma.nineth.converter.MintRuleConverter;
+import com.izouma.nineth.dto.MintActivityRule;
+import com.izouma.nineth.enums.EntryModeType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙抽奖活动")
+public class MetaLuckyDraw extends BaseEntity {
+
+    @ApiModelProperty("活动名称")
+    @ExcelProperty("活动名称")
+    private String name;
+
+    @ApiModelProperty("规则说明")
+    @Column(columnDefinition = "TEXT")
+    @ExcelProperty("规则说明")
+    private String detail;
+
+    @ApiModelProperty("入场方式")
+    @Enumerated(EnumType.STRING)
+    @ExcelProperty("入场方式")
+    private EntryModeType entryModeType;
+
+    @ApiModelProperty("所需金币数量")
+    @ExcelProperty("所需金币数量")
+    private int goldNum;
+
+    @ApiModelProperty("藏品规则")
+    @Convert(converter = MintRuleConverter.class)
+    @Column(columnDefinition = "TEXT")
+    @ExcelProperty("藏品规则")
+    private MintActivityRule rule;
+
+    @Column(columnDefinition = "tinyint unsigned default 0")
+    @ApiModelProperty("是否审核")
+    @ExcelProperty("是否审核")
+    private boolean audit = false;
+
+    @ApiModelProperty("藏品名称")
+    @ExcelProperty("藏品名称")
+    private String collectionName;
+
+    @ApiModelProperty("所需nft数量")
+    @ExcelProperty("所需nft数量")
+    private int num;
+
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = MetaLuckyDrawAwardListConverter.class)
+    @ApiModelProperty("奖励配置")
+    @ExcelProperty("奖励配置")
+    private List<MetaLuckyDrawAwardModel> metaLuckDrawAwards;
+
+    @ApiModelProperty("是否发布")
+    @ExcelProperty("是否发布")
+    private boolean publish;
+}

+ 31 - 0
src/main/java/com/izouma/nineth/domain/MetaLuckyDrawAwardModel.java

@@ -0,0 +1,31 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.enums.MetaAwardTypeEnum;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class MetaLuckyDrawAwardModel {
+
+    @ApiModelProperty("奖励名称")
+    private String name;
+
+    @ApiModelProperty("奖励类型")
+    private MetaAwardTypeEnum awardType;
+
+    @ApiModelProperty("奖励配置(金币奖励为金币数量,NFT奖励为NFT奖励图片地址)")
+    private String config;
+
+    @ApiModelProperty("概率")
+    private int probability;
+
+    @ApiModelProperty("是否需要限制数量(针对NFT奖励)")
+    private boolean limitNum;
+
+    @ApiModelProperty("奖池最大数量")
+    private int maxNum;
+}

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

@@ -0,0 +1,43 @@
+package com.izouma.nineth.domain;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.izouma.nineth.converter.MetaLuckyDrawAwardConverter;
+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 java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙抽奖活动领取记录")
+public class MetaLuckyDrawAwardReceiveRecord extends BaseEntity {
+
+    @ApiModelProperty("用户id")
+    @ExcelProperty("用户id")
+    private Long userId;
+
+    @ApiModelProperty("抽奖活动id")
+    @ExcelProperty("抽奖活动id")
+    private Long metaLuckyDrawId;
+
+    @ApiModelProperty("奖励名称")
+    @ExcelProperty("奖励名称")
+    private String metaLuckDrawAwardName;
+
+    @ApiModelProperty("玩家抽奖获得的奖励")
+    @ExcelProperty("玩家抽奖获得的奖励")
+    @Convert(converter = MetaLuckyDrawAwardConverter.class)
+    private MetaLuckyDrawAwardModel metaLuckDrawAward;
+
+    @ApiModelProperty("奖励获得时间")
+    @ExcelProperty("奖励获得时间")
+    private LocalDateTime finishTime;
+
+}

+ 54 - 0
src/main/java/com/izouma/nineth/domain/MetaProp.java

@@ -0,0 +1,54 @@
+package com.izouma.nineth.domain;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.izouma.nineth.enums.MetaPropTriggerType;
+import com.izouma.nineth.enums.MetaPropType;
+import com.izouma.nineth.enums.MetaPropUsedType;
+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 MetaProp extends BaseEntity {
+
+    @ApiModelProperty("名称")
+    @ExcelProperty("名称")
+    private String name;
+
+    @ApiModelProperty("介绍")
+    @ExcelProperty("介绍")
+    private String introduction;
+
+    @ApiModelProperty("图片")
+    @ExcelProperty("图片")
+    private String pic;
+
+    @ApiModelProperty("道具类型")
+    @ExcelProperty("道具类型")
+    @Enumerated(EnumType.STRING)
+    private MetaPropType propType;
+
+    @ApiModelProperty("使用类型")
+    @ExcelProperty("使用类型")
+    @Enumerated(EnumType.STRING)
+    private MetaPropUsedType usedType;
+
+    @ApiModelProperty("触发类型")
+    @ExcelProperty("触发类型")
+    @Enumerated(EnumType.STRING)
+    private MetaPropTriggerType triggerType;
+
+    @ApiModelProperty("限时道具时间,单位小时")
+    @ExcelProperty("限时道具时间,单位小时")
+    private int times;
+}

+ 9 - 1
src/main/java/com/izouma/nineth/domain/MetaSwitch.java

@@ -2,6 +2,7 @@ package com.izouma.nineth.domain;
 
 
 import com.alibaba.excel.annotation.ExcelProperty;
+import com.izouma.nineth.enums.MetaSwitchType;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
@@ -9,12 +10,14 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
 @Entity
-@ApiModel("元宇宙空间码头列表")
+@ApiModel("元宇宙开关")
 public class MetaSwitch extends BaseEntity {
 
     @ApiModelProperty("开关名称")
@@ -28,4 +31,9 @@ public class MetaSwitch extends BaseEntity {
     @ApiModelProperty("开关状态")
     @ExcelProperty("开关状态")
     private boolean status;
+
+    @ApiModelProperty("开关类型")
+    @ExcelProperty("开关类型")
+    @Enumerated(EnumType.STRING)
+    private MetaSwitchType switchType;
 }

+ 44 - 16
src/main/java/com/izouma/nineth/domain/PublicScreenChat.java

@@ -2,7 +2,7 @@ package com.izouma.nineth.domain;
 
 import com.alibaba.excel.annotation.ExcelIgnore;
 import com.alibaba.excel.annotation.ExcelProperty;
-import com.izouma.nineth.annotations.Searchable;
+import com.izouma.nineth.enums.ChatType;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
@@ -10,6 +10,8 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.Transient;
 import java.time.LocalDateTime;
 
@@ -20,30 +22,28 @@ import java.time.LocalDateTime;
 @ApiModel("元宇宙用户聊天信息")
 public class PublicScreenChat extends BaseEntity {
 
-    @ApiModelProperty("发送方昵称")
-    @ExcelProperty("发送方昵称")
-    @Searchable
+    @ApiModelProperty("消息发送方昵称")
+    @ExcelProperty("消息发送方昵称")
     private String nickname;
 
-    @ApiModelProperty("发送方用户id")
-    @ExcelProperty("发送方用户id")
-    @Searchable
+    @ApiModelProperty("消息发送方用户id")
+    @ExcelProperty("消息发送方用户id")
     private String userId;
 
-    @ApiModelProperty("勋章等级")
-    @ExcelProperty("勋章等级")
+    @ApiModelProperty("消息发送方勋章等级")
+    @ExcelProperty("消息发放送勋章等级")
     private int level;
 
-    @ApiModelProperty("境界")
-    @ExcelProperty("境界")
+    @ApiModelProperty("消息发送方境界")
+    @ExcelProperty("消息发送方境界")
     private String realm;
 
-    @ApiModelProperty("头衔")
-    @ExcelProperty("头衔")
+    @ApiModelProperty("消息发送方头衔")
+    @ExcelProperty("消息发送方头衔")
     private String title;
 
-    @ApiModelProperty("头像")
-    @ExcelProperty("头像")
+    @ApiModelProperty("消息发送方头像")
+    @ExcelProperty("消息发送方头像")
     private String avatar;
 
     @ApiModelProperty("消息内容")
@@ -62,7 +62,7 @@ public class PublicScreenChat extends BaseEntity {
     @ExcelIgnore
     private int recall;
 
-    @ApiModelProperty("类型 1:直播 2:元宇宙聊天")
+    @ApiModelProperty("类型 1:元宇宙聊天 2:直播")
     @ExcelIgnore
     private int type;
 
@@ -72,4 +72,32 @@ public class PublicScreenChat extends BaseEntity {
 
     @Transient
     private boolean myself;
+
+    @ApiModelProperty("消息类型")
+    @Enumerated(EnumType.STRING)
+    private ChatType chatType;
+
+    @ApiModelProperty("消息接受方昵称")
+    @ExcelProperty("消息接受方昵称")
+    private String toUserNickname;
+
+    @ApiModelProperty("消息接收方用户id")
+    @ExcelProperty("消息接收方用户id")
+    private Long toUserId;
+
+    @ApiModelProperty("消息接收方勋章等级")
+    @ExcelProperty("消息接收方勋章等级")
+    private int toUserLevel;
+
+    @ApiModelProperty("消息接收方境界")
+    @ExcelProperty("消息接收方境界")
+    private String toUserRealm;
+
+    @ApiModelProperty("消息接收方头衔")
+    @ExcelProperty("消息接收方头衔")
+    private String toUserTitle;
+
+    @ApiModelProperty("消息接收方头像")
+    @ExcelProperty("消息接收方头像")
+    private String toUserAvatar;
 }

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

@@ -112,4 +112,10 @@ public class TradeAuctionOrder extends BaseEntity {
     private TradeAuctionStatus tradeAuctionStatus;
 
     private Long invitor;
+
+    private String invitorPhone;
+
+    private String invitorParentPhone;
+
+    private Long parentInvitor;
 }

+ 20 - 0
src/main/java/com/izouma/nineth/enums/ChatType.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.enums;
+
+public enum ChatType {
+
+    DEFAULT("默认"),
+
+    PUBLIC("公频聊天"),
+
+    PRIVATE("私聊");
+
+    private final String description;
+
+    ChatType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 5 - 7
src/main/java/com/izouma/nineth/enums/MetaPointTypeEnum.java → src/main/java/com/izouma/nineth/enums/MetaPropTriggerType.java

@@ -1,20 +1,18 @@
 package com.izouma.nineth.enums;
 
+public enum MetaPropTriggerType {
 
-public enum MetaPointTypeEnum {
+    ACTIVE("主动"),
 
-    GAME_BOX("GAME_BOX"),
-
-    ZOMBIE("ZOMBIE");
+    PASSIVE("被动");
 
     private final String description;
 
-    MetaPointTypeEnum(String description) {
+    MetaPropTriggerType(String description) {
         this.description = description;
     }
 
     public String getDescription() {
         return description;
     }
-}
-
+}

+ 26 - 0
src/main/java/com/izouma/nineth/enums/MetaPropType.java

@@ -0,0 +1,26 @@
+package com.izouma.nineth.enums;
+
+public enum MetaPropType {
+
+    ACCESSORIES("配饰"),
+
+    VEHICLE("载具"),
+
+    MATERIAL("材料"),
+
+    EXPERIENCE("体验卡"),
+
+    PETS("宠物"),
+
+    OTHER("其他");
+
+    private final String description;
+
+    MetaPropType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 20 - 0
src/main/java/com/izouma/nineth/enums/MetaPropUsedType.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.enums;
+
+public enum MetaPropUsedType {
+
+    PERMANENT("永久"),
+
+    DISPOSABLE("一次性"),
+
+    LIMITED("限时");
+
+    private final String description;
+
+    MetaPropUsedType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

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

@@ -0,0 +1,19 @@
+package com.izouma.nineth.enums;
+
+
+public enum MetaSwitchType {
+
+    WEBSOCKET("websocket开关"),
+
+    COMMON("普通开关");
+
+    private final String description;
+
+    MetaSwitchType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

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

@@ -4,6 +4,8 @@ import com.izouma.nineth.domain.DomainOrder;
 import com.izouma.nineth.enums.CollectionStatus;
 import com.izouma.nineth.enums.OrderStatus;
 import com.izouma.nineth.service.LikeService;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Modifying;
@@ -21,10 +23,12 @@ public interface DomainOrderRepo extends JpaRepository<DomainOrder, Long>, JpaSp
 
     List<DomainOrder> findAllByUserIdAndOrderStatus(Long userId, OrderStatus status);
 
+    List<DomainOrder> findAllByPicNameInAndOrderStatus(List<String> strings, OrderStatus status);
+
     Integer countAllByDomainNameEqualsAndOrderStatusNot(String name, OrderStatus status);
 
     @Query(value = "select c from DomainOrder c where c.picName like ?1 and c.orderStatus <> ?2")
-    List<DomainOrder> searchUsedDomain(String name, OrderStatus status);
+    Page<DomainOrder> searchUsedDomain(String name, OrderStatus status, Pageable pageable);
 
     List<DomainOrder> findByOrderStatusAndCreatedAtBeforeAndDelFalse(OrderStatus orderStatus, LocalDateTime createdAt);
 }

+ 3 - 2
src/main/java/com/izouma/nineth/repo/MetaAdvertRecordRepo.java

@@ -15,7 +15,8 @@ public interface MetaAdvertRecordRepo extends JpaRepository<MetaAdvertRecord, Lo
     @Transactional
     void softDelete(Long id);
 
-    MetaAdvertRecord findByApplicationAndPublishAndDel(int application, boolean publish, boolean del);
+    List<MetaAdvertRecord> findByDel(boolean del);
 
-    List<MetaAdvertRecord> findByPublishAndDel(boolean used, boolean del);
+    @Query("select max(m.number) from MetaAdvertRecord m ")
+    Integer findMaxNumber();
 }

+ 3 - 4
src/main/java/com/izouma/nineth/repo/MetaGameBoxPointsRepo.java

@@ -1,7 +1,6 @@
 package com.izouma.nineth.repo;
 
 import com.izouma.nineth.domain.MetaGameBoxPoints;
-import com.izouma.nineth.enums.MetaPointTypeEnum;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Query;
@@ -12,11 +11,11 @@ import java.util.Map;
 public interface MetaGameBoxPointsRepo extends JpaRepository<MetaGameBoxPoints, Long>, JpaSpecificationExecutor<MetaGameBoxPoints> {
 
     @Query(value = "SELECT m.user_id userId, m.score score, m.type type, u.nickname nickname, u.avatar head, RANK() OVER(ORDER BY m.score DESC) scoreRank FROM meta_game_box_points m LEFT JOIN user u ON m.user_id = u.id where m.type = ?1 LIMIT 50", nativeQuery = true)
-    List<Map<String, Object>> findTopFiftyByType(String type);
+    List<Map<String, Object>> findTopFiftyByType(int type);
 
     @Query(value = "SELECT m.score score, a.scoreRank scoreRank FROM meta_game_box_points m LEFT JOIN ( SELECT user_id userId, RANK() OVER ( ORDER BY score DESC ) scoreRank FROM meta_game_box_points where type = ?2 ) a ON m.user_id = a.userId  WHERE m.user_id = ?1 and m.type = ?2", nativeQuery = true)
-    Map<String, Object> findInfoByUserIdAndType(Long userId, String type);
+    Map<String, Object> findInfoByUserIdAndType(Long userId, int type);
 
-    MetaGameBoxPoints findByUserIdAndType(Long userId, MetaPointTypeEnum type);
+    MetaGameBoxPoints findByUserIdAndType(Long userId, int type);
 
 }

+ 18 - 0
src/main/java/com/izouma/nineth/repo/MetaLuckyDrawAwardReceiveRecordRepo.java

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

+ 18 - 0
src/main/java/com/izouma/nineth/repo/MetaLuckyDrawRepo.java

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

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

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

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

@@ -1,6 +1,7 @@
 package com.izouma.nineth.repo;
 
 import com.izouma.nineth.domain.MetaSwitch;
+import com.izouma.nineth.enums.MetaSwitchType;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Modifying;
@@ -15,7 +16,7 @@ public interface MetaSwitchRepo extends JpaRepository<MetaSwitch, Long>, JpaSpec
     @Transactional
     void softDelete(Long id);
 
-    MetaSwitch findByNameAndDel(String name, boolean del);
+    MetaSwitch findByNameAndSwitchTypeAndDel(String name, MetaSwitchType switchType, boolean del);
 
     @Query("update MetaSwitch t set t.status = ?2 where t.id = ?1")
     @Modifying

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

@@ -264,7 +264,7 @@ public class AssetService {
         asset.setNumber(number);
         asset.setOrderId(orderId);
         asset.setPrice(price);
-        asset.setPrefixName("元域名");
+        asset.setPrefixName("RID");
         asset.setTags(new HashSet<>());
         User fakeUser = null;
         if (safeFlag) {

+ 96 - 26
src/main/java/com/izouma/nineth/service/DomainOrderService.java

@@ -20,6 +20,9 @@ import lombok.AllArgsConstructor;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.springframework.data.annotation.Transient;
 import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
 import org.springframework.stereotype.Service;
 
 import javax.imageio.ImageIO;
@@ -69,28 +72,42 @@ public class DomainOrderService {
     public DomainOrder create(Long userId, String domain, BigDecimal price, Long year) {
         List<DomainOrder> notPaidOrders = domainOrderRepo.findAllByUserIdAndOrderStatus(userId, OrderStatus.NOT_PAID);
         Long superUserId = Long.valueOf(sysConfigService.getString("domain_superUserId"));
+        if (notPaidOrders.size() > 0) {
+            throw new BusinessException("已存在未支付订单,不可继续下单");
+        }
+        if (domain.contains(".uni")) {
+            throw new BusinessException("域名后缀不符合规定.");
+        }
         if (!superUserId.equals(SecurityUtils.getAuthenticatedUser().getId())) {
             if (isContainChinese(domain)) {
                 throw new BusinessException("禁止注册中文域名");
             }
-            if (notPaidOrders.size() > 0) {
-                throw new BusinessException("已存在未支付订单,不可继续下单");
-            }
             if (domain.length() < 9) {
                 throw new BusinessException("四位及以下域名只能官方创建。");
             }
         }
-        Map<String, Object> checkResult = check(domain);
+        String realName;
+        int dotIndex = domain.indexOf(".");
+        realName = domain.substring(0, dotIndex);
+        Map<String, Object> checkResult = check(realName);
         if (!(Boolean) checkResult.get("result")) {
             throw new BusinessException(checkResult.get("reason").toString());
         }
+        BigDecimal singlePrice = sysConfigService.getBigDecimal("domain_price");
+        if (singlePrice.multiply(BigDecimal.valueOf(year)).compareTo(price) != 0) {
+            throw new BusinessException("价格不符");
+        }
+//        LocalDateTime startTime = LocalDateTime.of(2023, 2, 10, 17, 0, 0);
+//        if (LocalDateTime.now().isBefore(startTime)) {
+//            throw new BusinessException("时间不符");
+//        }
 //        if (price.compareTo(BigDecimal.valueOf(40L)) != 0) {
 //            throw new BusinessException("订单价格与配置不符,请重新下单.");
 //        }
         User user = userRepo.findById(userId).orElseThrow(new BusinessException("未找到用户"));
         if (domain.contains(".")) {
-            int dotIndex = domain.indexOf(".");
-            domain = domain.substring(0, dotIndex);
+            int dotIndex1 = domain.indexOf(".");
+            domain = domain.substring(0, dotIndex1);
         }
 
         DomainOrder domainOrder = new DomainOrder();
@@ -108,11 +125,18 @@ public class DomainOrderService {
     }
 
     public Map<String, Object> check(String domain) {
+        Map<String, Object> result = new HashMap<>();
         Long superUserId = Long.valueOf(sysConfigService.getString("domain_superUserId"));
-        String visibleDomain = domain + ".nft";
+        String visibleDomain = domain;
+        if (domain.contains(".uni")) {
+            result.put("result", false);
+            result.put("reason", "禁止使用.uni");
+            return result;
+        }
+        if (!domain.contains(".nft")) {
+            visibleDomain = domain + ".nft";
+        }
         List<String> keywords = Arrays.asList(sysConfigService.getString("domain_keyword").split(","));
-
-        Map<String, Object> result = new HashMap<>();
         if (!superUserId.equals(SecurityUtils.getAuthenticatedUser().getId())) {
             if (keywords.stream().anyMatch(keyword -> org.apache.commons.lang.StringUtils.equals(keyword, domain))) {
                 result.put("result", false);
@@ -147,7 +171,8 @@ public class DomainOrderService {
             int dotIndex = domain.indexOf(".");
             domain = domain.substring(0, dotIndex);
         }
-        List<DomainOrder> used = domainOrderRepo.searchUsedDomain("%" + domain + "%", OrderStatus.CANCELLED);
+        Pageable pageable = PageRequest.of(0, 10, Sort.by("createdAt").descending());
+        List<DomainOrder> used = domainOrderRepo.searchUsedDomain("%" + domain + "%", OrderStatus.CANCELLED,pageable).getContent();
 
         String n = domain.substring(domain.length() - 1);
         List<Map<String, Object>> recommend = new ArrayList<>();
@@ -171,9 +196,14 @@ public class DomainOrderService {
 
         used.forEach(domainOrder -> {
             Map<String, Object> sold = new HashMap<>();
-            sold.put("domain", domainOrder.getDomainName().toLowerCase());
-            sold.put("sold", true);
-            result.add(sold);
+            if (!domainOrder.getDomainName().contains(".uni")) {
+                sold.put("domain", domainOrder.getDomainName().toLowerCase());
+                sold.put("sold", true);
+                result.add(sold);
+            }
+            if (result.size() > 9) {
+
+            }
         });
         return result;
     }
@@ -220,12 +250,12 @@ public class DomainOrderService {
         String domainName;
         if (domain.contains(".")) {
             int dotIndex = domain.indexOf(".");
-            domainName = domain.substring(0, dotIndex);
+            domainName = domain.substring(0, dotIndex).toUpperCase();
         } else {
-            domainName = domain;
+            domainName = domain.toUpperCase();
         }
         InputStream is1 = this.getClass()
-                .getResourceAsStream("/font/Akronim Regular_mianfeiziti1.ttf");
+                .getResourceAsStream("/font/VonwaonBitmap_16pxLite.ttf");
         Font font1 = Font.createFont(Font.TRUETYPE_FONT, is1);
         is1.close();
         InputStream is2 = this.getClass()
@@ -262,17 +292,57 @@ public class DomainOrderService {
 //                ImageUtils.Fit.COVER), 40);
 //        g.drawImage(avatarImg, 334, 136, null);
 
-        g.setColor(new Color(255, 255, 255));
-        Font topFont = font1.deriveFont(Font.PLAIN, 240f);
 
-        Font downFont = font2.deriveFont(Font.BOLD, 36f);
-        ImageUtils.drawCenteredString(g, domainName, new Rectangle(0, 243, shareImg
-                .getWidth(), 86), topFont);
-        ImageUtils.drawCenteredString(g, ".NFT", new Rectangle(0, 462, shareImg
-                .getWidth(), 86), topFont);
-        g.setColor(new Color(255, 255, 255));
-        ImageUtils.drawCenteredString(g, domain, new Rectangle(0, 612, shareImg
-                .getWidth(), 12), downFont);
+        int domainLength = domainName.length();
+        if (domainLength > 10) {
+            g.setColor(new Color(255, 255, 255));
+            Font topFont = font1.deriveFont(Font.PLAIN, 130f);
+            Font downFont = font2.deriveFont(Font.BOLD, 36f);
+            int subIndex = domainLength / 3;
+            String str1 = domainName.substring(0, subIndex);
+            String str2 = domainName.substring(subIndex, subIndex + subIndex);
+            String str3 = domainName.substring(subIndex + subIndex, domainLength);
+            ImageUtils.drawCenteredString(g, str1, new Rectangle(0, 180, shareImg
+                    .getWidth(), 86), topFont);
+            ImageUtils.drawCenteredString(g, str2, new Rectangle(0, 300, shareImg
+                    .getWidth(), 86), topFont);
+            ImageUtils.drawCenteredString(g, str3, new Rectangle(0, 420, shareImg
+                    .getWidth(), 86), topFont);
+            ImageUtils.drawCenteredString(g, ".NFT", new Rectangle(0, 540, shareImg
+                    .getWidth(), 86), topFont);
+            g.setColor(new Color(255, 255, 255));
+            ImageUtils.drawCenteredString(g, domain, new Rectangle(0, 650, shareImg
+                    .getWidth(), 12), downFont);
+        }
+        if (domainLength > 5 & domainLength <= 10) {
+            g.setColor(new Color(255, 255, 255));
+            Font topFont = font1.deriveFont(Font.PLAIN, 190f);
+            Font downFont = font2.deriveFont(Font.BOLD, 36f);
+            int subIndex = domainLength / 2;
+            String str1 = domainName.substring(0, subIndex);
+            String str2 = domainName.substring(subIndex, domainLength);
+            ImageUtils.drawCenteredString(g, str1, new Rectangle(0, 180, shareImg
+                    .getWidth(), 86), topFont);
+            ImageUtils.drawCenteredString(g, str2, new Rectangle(0, 350, shareImg
+                    .getWidth(), 86), topFont);
+            ImageUtils.drawCenteredString(g, ".NFT", new Rectangle(0, 520, shareImg
+                    .getWidth(), 86), topFont);
+            g.setColor(new Color(255, 255, 255));
+            ImageUtils.drawCenteredString(g, domain, new Rectangle(0, 650, shareImg
+                    .getWidth(), 12), downFont);
+        }
+        if (domainLength <= 5) {
+            g.setColor(new Color(255, 255, 255));
+            Font topFont = font1.deriveFont(Font.PLAIN, 240f);
+            Font downFont = font2.deriveFont(Font.BOLD, 36f);
+            ImageUtils.drawCenteredString(g, domainName, new Rectangle(17, 220, shareImg
+                    .getWidth(), 86), topFont);
+            ImageUtils.drawCenteredString(g, ".NFT", new Rectangle(-10, 420, shareImg
+                    .getWidth(), 86), topFont);
+            g.setColor(new Color(255, 255, 255));
+            ImageUtils.drawCenteredString(g, domain, new Rectangle(0, 620, shareImg
+                    .getWidth(), 12), downFont);
+        }
 
         //二维码
 //        QRCodeWriter qrCodeWriter = new QRCodeWriter();

+ 26 - 0
src/main/java/com/izouma/nineth/service/MetaAdvertRecordService.java

@@ -1,13 +1,19 @@
 package com.izouma.nineth.service;
 
+import com.izouma.nineth.annotations.RedisLock;
 import com.izouma.nineth.domain.MetaAdvertRecord;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.MetaAdvertRecordRepo;
 import com.izouma.nineth.utils.JpaUtils;
+import com.izouma.nineth.utils.ObjUtils;
 import lombok.AllArgsConstructor;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
 @Service
 @AllArgsConstructor
 public class MetaAdvertRecordService {
@@ -17,4 +23,24 @@ public class MetaAdvertRecordService {
     public Page<MetaAdvertRecord> all(PageQuery pageQuery) {
         return metaAdvertRecordRepo.findAll(JpaUtils.toSpecification(pageQuery, MetaAdvertRecord.class), JpaUtils.toPageRequest(pageQuery));
     }
+
+    public MetaAdvertRecord save(MetaAdvertRecord record) {
+        if (record.getId() != null) {
+            MetaAdvertRecord orig = metaAdvertRecordRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return metaAdvertRecordRepo.save(orig);
+        }
+        int maxNumber = findMaxNumber();
+        record.setNumber(maxNumber + 1);
+        return metaAdvertRecordRepo.save(record);
+    }
+
+    @RedisLock(value = "'maxNumber'", expire = 1, unit = TimeUnit.HOURS)
+    private int findMaxNumber() {
+        Integer maxNumber = metaAdvertRecordRepo.findMaxNumber();
+        if (Objects.isNull(maxNumber)) {
+            return 0;
+        }
+        return maxNumber;
+    }
 }

+ 3 - 7
src/main/java/com/izouma/nineth/service/MetaGameBoxPointsService.java

@@ -6,7 +6,6 @@ import com.izouma.nineth.domain.MetaGameBoxPoints;
 import com.izouma.nineth.dto.MetaGameBoxPointsDTO;
 import com.izouma.nineth.dto.MetaRestResult;
 import com.izouma.nineth.dto.PageQuery;
-import com.izouma.nineth.enums.MetaPointTypeEnum;
 import com.izouma.nineth.repo.MetaGameBoxPointsRepo;
 import com.izouma.nineth.utils.JpaUtils;
 import lombok.AllArgsConstructor;
@@ -28,7 +27,7 @@ public class MetaGameBoxPointsService {
         return metaGameBoxPointsRepo.findAll(JpaUtils.toSpecification(pageQuery, MetaGameBoxPoints.class), JpaUtils.toPageRequest(pageQuery));
     }
 
-    public MetaRestResult<MetaGameBoxPointsDTO> get(Long userId, MetaPointTypeEnum type) {
+    public MetaRestResult<MetaGameBoxPointsDTO> get(Long userId, int type) {
         MetaGameBoxPointsDTO metaGameBoxPointsDTO = new MetaGameBoxPointsDTO();
         MetaGameBoxPoints metaGameBoxPoints = metaGameBoxPointsRepo.findByUserIdAndType(userId, type);
         if (Objects.isNull(metaGameBoxPoints)) {
@@ -38,12 +37,12 @@ public class MetaGameBoxPointsService {
             metaGameBoxPoints.setScore(0);
             metaGameBoxPointsRepo.save(metaGameBoxPoints);
         }
-        Map<String, Object> metaGameBoxPointsMap = metaGameBoxPointsRepo.findInfoByUserIdAndType(userId, type.toString());
+        Map<String, Object> metaGameBoxPointsMap = metaGameBoxPointsRepo.findInfoByUserIdAndType(userId, type);
         String jsonStr = JSONObject.toJSONString(metaGameBoxPointsMap);
         metaGameBoxPoints = JSONObject.parseObject(jsonStr, MetaGameBoxPoints.class);
         metaGameBoxPointsDTO.setSelfHiScore(metaGameBoxPoints.getScore());
         metaGameBoxPointsDTO.setSelfRank(metaGameBoxPoints.getScoreRank());
-        List<Map<String, Object>> map = metaGameBoxPointsRepo.findTopFiftyByType(type.toString());
+        List<Map<String, Object>> map = metaGameBoxPointsRepo.findTopFiftyByType(type);
         JSONArray jsonArray = new JSONArray();
         jsonArray.addAll(map);
         List<MetaGameBoxPoints> data = jsonArray.toJavaList(MetaGameBoxPoints.class);
@@ -59,9 +58,6 @@ public class MetaGameBoxPointsService {
         if (Objects.isNull(record.getUserId())) {
             return MetaRestResult.returnError("Illegal parameter : userId can not be null");
         }
-        if (Objects.isNull(record.getType())) {
-            return MetaRestResult.returnError("Illegal parameter : type can not be null");
-        }
         MetaGameBoxPoints metaGameBoxPoints = metaGameBoxPointsRepo.findByUserIdAndType(record.getUserId(), record.getType());
         if (Objects.isNull(metaGameBoxPoints)) {
             metaGameBoxPoints = new MetaGameBoxPoints();

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

@@ -108,7 +108,7 @@ public class MetaGameCopyService {
         }
         metaGameProcess.setPoint(point);
         if (metaGameProcess.isCompleted()) {
-            metaGameProcess.setMetaGameAward(LotteryUtils.lottery(metaGameCopy.getMetaGameAwards()));
+            metaGameProcess.setMetaGameAward(LotteryUtils.lotteryForMetaGameAward(metaGameCopy.getMetaGameAwards()));
         }
         return MetaRestResult.returnSuccess(metaGameProcessRepo.save(metaGameProcess));
     }

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

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

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

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

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

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

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

@@ -745,7 +745,7 @@ public class OrderPayService {
         }
         BalanceRecord record = userBalanceService
                 .balancePay(order.getUserId(), amount, orderId, "拍卖");
-        tradeAuctionOrderService.commission(order, amount);
+        tradeAuctionOrderService.commission(order, order.getPrice());
         tradeAuctionOrderService.notify(orderId, record.getId().toString(), PayMethod.BALANCE);
     }
 
@@ -880,7 +880,7 @@ public class OrderPayService {
                 return hmPayService.requestAlipay(orderId + "", order.getPrice(),
                         "域名:" + order.getPicName(), HMPayService.getTimeout(order.getCreatedAt(), 180),
                         Constants.OrderNotifyType.DOMAIN, generalProperties
-                                .resolveFrontUrl(getCompanyId(), "/orderDetail?id=" + orderId));
+                                .resolveFrontUrl(getCompanyId(), "/domainname"));
         }
         throw new BusinessException("绿洲宇宙冷却系统已启动,请稍后支付");
     }
@@ -903,7 +903,7 @@ public class OrderPayService {
                     + "&orderId=" + orderId + "&type=domain&returnUrl="
                     + URLEncoder
                     .encode(generalProperties
-                            .resolveFrontUrl(getCompanyId(), "/orderDetail?id=" + orderId), StandardCharsets.UTF_8);
+                            .resolveFrontUrl(getCompanyId(), "/domainname"), StandardCharsets.UTF_8);
         } else {
             return Constants.ALIPAY_URL_SCHEME + qrCode;
         }
@@ -920,7 +920,7 @@ public class OrderPayService {
                 .orElseThrow(new BusinessException("请先完成实名认证"));
         return sandPayService.payQuick(orderId + "", "域名:" + order.getPicName(),
                 order.getPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.DOMAIN,
-                generalProperties.resolveFrontUrl(getCompanyId(), "/orderDetail?id=" + orderId));
+                generalProperties.resolveFrontUrl(getCompanyId(), "/domainname"));
     }
 
     @Cacheable(value = "payOrder", key = "'domain#'+#orderId")
@@ -934,7 +934,7 @@ public class OrderPayService {
                 .orElseThrow(new BusinessException("请先完成实名认证"));
         return sandPayService.payQuickBind(orderId + "", "域名:" + order.getPicName(),
                 order.getPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.DOMAIN,
-                generalProperties.resolveFrontUrl(getCompanyId(), "/orderDetail?id=" + orderId),
+                generalProperties.resolveFrontUrl(getCompanyId(), "/domainname"),
                 order.getUserId(), identityAuth.getRealName(), identityAuth.getIdNo());
     }
 

+ 62 - 24
src/main/java/com/izouma/nineth/service/TradeAuctionOrderService.java

@@ -113,7 +113,15 @@ public class TradeAuctionOrderService {
 
         if (user.getInviteType() != null) {
             if (user.getInviteType().equals(InviteType.AUCTION)) {
-                tradeAuctionOrder.setInvitor(Long.valueOf(user.getInviteCode()));
+                tradeAuctionOrder.setInvitorPhone(user.getInvitorPhone());
+            }
+            User parentUser = userRepo.findByPhoneAndDelFalse(user.getInvitorPhone()).orElse(null);
+            if (parentUser != null) {
+                if (parentUser.getInviteType() != null) {
+                    if (parentUser.getInviteType().equals(InviteType.AUCTION)) {
+                        tradeAuctionOrder.setInvitorParentPhone(parentUser.getInvitorPhone());
+                    }
+                }
             }
         }
         tradeAuctionOrder = tradeAuctionOrderRepo.save(tradeAuctionOrder);
@@ -344,30 +352,60 @@ public class TradeAuctionOrderService {
     }
 
     public void commission(TradeAuctionOrder order, BigDecimal price) {
-        if (order.getInvitor() != null) {
-            userRepo.findById(order.getInvitor()).ifPresent(user -> {
-                BigDecimal shareRatio = sysConfigService.getBigDecimal("auction_commission_rate");
-                if (StringUtils.isNotBlank(user.getSettleAccountId()) &&
-                        shareRatio != null && shareRatio.compareTo(BigDecimal.ZERO) > 0) {
-                    commissionRecordRepo.save(CommissionRecord.builder()
-                            .orderId(order.getId())
-                            .collectionId(order.getTradeAuctionId())
-                            .name(order.getName())
-                            .totalPrice(price)
-                            .nickname(user.getNickname())
-                            .userId(user.getId())
-                            .shareRatio(user.getShareRatio())
-                            .phone(user.getPhone())
-                            .shareAmount(price.multiply(shareRatio)
-                                    .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))
-                            .type(InviteType.AUCTION)
-                            .build());
+        List<String> officialPhones = Arrays.asList(sysConfigService.getString("ta_phone").split(","));
+        if (order.getInvitorPhone() != null) {
+            if (officialPhones.contains(order.getInvitorPhone())) {
+                userRepo.findByPhoneAndDelFalse(order.getInvitorPhone()).ifPresent(user -> {
+                    BigDecimal shareRatio = sysConfigService.getBigDecimal("auction_commission_rate");
+                    if (StringUtils.isNotBlank(user.getSettleAccountId()) &&
+                            shareRatio != null && shareRatio.compareTo(BigDecimal.ZERO) > 0) {
+                        commissionRecordRepo.save(CommissionRecord.builder()
+                                .orderId(order.getId())
+                                .collectionId(order.getTradeAuctionId())
+                                .name(order.getName())
+                                .totalPrice(price)
+                                .nickname(user.getNickname())
+                                .userId(user.getId())
+                                .shareRatio(user.getShareRatio())
+                                .phone(user.getPhone())
+                                .shareAmount(price.multiply(shareRatio)
+                                        .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP))
+                                .type(InviteType.AUCTION)
+                                .build());
 
-                    userBalanceService.addBalance(user.getId(), price.multiply(shareRatio)
-                            .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP), order
-                            .getId(), BalanceType.COMMISSION);
-                }
-            });
+                        userBalanceService.addBalance(user.getId(), price.multiply(shareRatio)
+                                .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP), order
+                                .getId(), BalanceType.COMMISSION);
+                    }
+                });
+            }
+        }
+        if (order.getInvitorParentPhone() != null) {
+            if (officialPhones.contains(order.getInvitorParentPhone())) {
+                userRepo.findByPhoneAndDelFalse(order.getInvitorParentPhone()).ifPresent(user -> {
+                    BigDecimal shareRatio = sysConfigService.getBigDecimal("auction_commission_step");
+                    if (StringUtils.isNotBlank(user.getSettleAccountId()) &&
+                            shareRatio != null && shareRatio.compareTo(BigDecimal.ZERO) > 0) {
+                        commissionRecordRepo.save(CommissionRecord.builder()
+                                .orderId(order.getId())
+                                .collectionId(order.getTradeAuctionId())
+                                .name(order.getName())
+                                .totalPrice(price)
+                                .nickname(user.getNickname())
+                                .userId(user.getId())
+                                .shareRatio(user.getShareRatio())
+                                .phone(user.getPhone())
+                                .shareAmount(price.multiply(shareRatio)
+                                        .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP))
+                                .type(InviteType.AUCTION)
+                                .build());
+
+                        userBalanceService.addBalance(user.getId(), price.multiply(shareRatio)
+                                .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP), order
+                                .getId(), BalanceType.COMMISSION);
+                    }
+                });
+            }
         }
     }
 

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

@@ -1227,4 +1227,40 @@ public class UserService {
             cacheService.clearUser(user.getId());
         }
     }
+
+    public void bindAuctionInvitor(Long userId, Long invitor) {
+        Invite invite = null;
+        InviteType type = InviteType.AUCTION;
+        User inviteUser = userRepo.findById(invitor).orElseThrow(new BusinessException("暂无用户"));
+        invite = inviteRepo.findFirstByCode(String.valueOf(invitor)).orElse(null);
+        if (invite == null) {
+            Invite newOne = new Invite();
+            newOne.setInviteNum(0);
+            newOne.setPhone(inviteUser.getPhone());
+            newOne.setCode(String.valueOf(invitor));
+            newOne.setName(inviteUser.getNickname());
+            newOne.setInviteType(InviteType.AUCTION);
+            invite = inviteRepo.save(newOne);
+        }
+        User user = userRepo.findById(userId).orElseThrow(new BusinessException("无此用户"));
+        user.setInvitorPhone(Optional.of(invite).map(Invite::getPhone).orElse(null));
+        user.setInvitorName(Optional.of(invite).map(Invite::getName).orElse(null));
+        user.setInviteCode(Optional.of(invite).map(Invite::getCode).orElse(null));
+        user.setCollectionInvitor(invitor);
+//            user.setCollectionId(collectionId)
+        user.setInviteType(type);
+        User orig = userRepo.findById(user.getId()).orElseThrow(new BusinessException("无记录"));
+        ObjUtils.merge(orig, user);
+        orig = save(orig);
+        userRepo.updateAssetMinter(orig.getId());
+        userRepo.updateAssetOwner(orig.getId());
+        userRepo.updateCollectionMinter(orig.getId());
+        userRepo.updateCollectionOwner(orig.getId());
+        userRepo.updateOrderMinter(orig.getId());
+        userRepo.updateHistoryFromUser(orig.getId());
+        userRepo.updateHistoryToUser(orig.getId());
+        userRepo.updateShowroomToUser(orig.getId());
+        cacheService.clearCollection();
+
+    }
 }

+ 25 - 1
src/main/java/com/izouma/nineth/utils/LotteryUtils.java

@@ -2,13 +2,14 @@ package com.izouma.nineth.utils;
 
 import com.google.common.util.concurrent.AtomicDouble;
 import com.izouma.nineth.domain.MetaGameAward;
+import com.izouma.nineth.domain.MetaLuckyDrawAwardModel;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 public class LotteryUtils {
-    public static MetaGameAward lottery(List<MetaGameAward> metaGameAwards) {
+    public static MetaGameAward lotteryForMetaGameAward(List<MetaGameAward> metaGameAwards) {
         int size = metaGameAwards.size();
         AtomicDouble sumProbability = new AtomicDouble();
         metaGameAwards.forEach(metaGameAward -> {
@@ -27,4 +28,27 @@ public class LotteryUtils {
         int lotteryIndex = sortAwardProbabilityList.indexOf(randomDouble);
         return metaGameAwards.get(lotteryIndex);
     }
+
+    public static MetaLuckyDrawAwardModel lotteryForMetaLuckyDrawAward(List<MetaLuckyDrawAwardModel> metaLuckyDrawAwardModels) {
+        int size = metaLuckyDrawAwardModels.size();
+        AtomicDouble sumProbability = new AtomicDouble();
+        metaLuckyDrawAwardModels.forEach(metaGameAward -> {
+            sumProbability.addAndGet(metaGameAward.getProbability());
+        });
+
+        List<Double> sortAwardProbabilityList = new ArrayList<>(size);
+        AtomicDouble tempSumProbability = new AtomicDouble();
+        metaLuckyDrawAwardModels.forEach(metaGameAward -> {
+            tempSumProbability.addAndGet(metaGameAward.getProbability());
+            sortAwardProbabilityList.add(tempSumProbability.get() / sumProbability.get());
+        });
+        double randomDouble = Math.random();
+        sortAwardProbabilityList.add(randomDouble);
+        Collections.sort(sortAwardProbabilityList);
+        int lotteryIndex = sortAwardProbabilityList.indexOf(randomDouble);
+        return metaLuckyDrawAwardModels.get(lotteryIndex);
+    }
+
+
+
 }

+ 0 - 1
src/main/java/com/izouma/nineth/utils/excel/ExcelUtils.java

@@ -37,7 +37,6 @@ public class ExcelUtils<T> {
                 .registerConverter(new ListConverter())
                 .registerConverter(new OperationSourceConverter())
                 .registerConverter(new RecordTypeConverter())
-                .registerConverter(new MetaPointTypeEnumConverter())
                 .registerConverter(new MetaIsLandTypeEnumConverter())
                 .registerConverter(new MoveTypeConverter())
                 .registerConverter(new MetaAwardTypeEnumConverter())

+ 0 - 42
src/main/java/com/izouma/nineth/utils/excel/MetaPointTypeEnumConverter.java

@@ -1,42 +0,0 @@
-package com.izouma.nineth.utils.excel;
-
-import com.alibaba.excel.converters.Converter;
-import com.alibaba.excel.enums.CellDataTypeEnum;
-import com.alibaba.excel.metadata.CellData;
-import com.alibaba.excel.metadata.GlobalConfiguration;
-import com.alibaba.excel.metadata.property.ExcelContentProperty;
-import com.izouma.nineth.enums.MetaPointTypeEnum;
-
-public class MetaPointTypeEnumConverter implements Converter<MetaPointTypeEnum> {
-
-    @Override
-    public Class supportJavaTypeKey() {
-        return MetaPointTypeEnum.class;
-    }
-
-    @Override
-    public CellDataTypeEnum supportExcelTypeKey() {
-        return null;
-    }
-
-    @Override
-    public MetaPointTypeEnum convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
-        try {
-            for (MetaPointTypeEnum value : MetaPointTypeEnum.values()) {
-                if (value.getDescription().equals(cellData.getStringValue())) {
-                    return value;
-                }
-            }
-        } catch (Exception ignored) {
-        }
-        return null;
-    }
-
-    @Override
-    public CellData convertToExcelData(MetaPointTypeEnum value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
-        if (value != null) {
-            return new CellData(value.getDescription());
-        }
-        return null;
-    }
-}

+ 23 - 6
src/main/java/com/izouma/nineth/web/AuthenticationController.java

@@ -14,10 +14,7 @@ import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -67,8 +64,18 @@ public class AuthenticationController {
 
     @PostMapping("/phoneLogin")
     @ApiOperation(value = "手机号验证码登录")
-    public String phoneLogin(String phone, String code) {
+    public String phoneLogin(String phone, String code, @RequestParam(required = false) Long invitor) {
         User user = userService.loginByPhone(phone, code);
+        InviteType userInviteType = user.getInviteType();
+        if (invitor != null) {
+            if (userInviteType != null) {
+                if (!userInviteType.equals(InviteType.AUCTION)) {
+                    userService.bindAuctionInvitor(user.getId(), invitor);
+                }
+            } else {
+                userService.bindAuctionInvitor(user.getId(), invitor);
+            }
+        }
         return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
     }
 
@@ -82,8 +89,18 @@ public class AuthenticationController {
 
     @PostMapping("/phonePwdLogin")
     @ApiOperation(value = "手机号密码登录")
-    public String phonePwdLogin(String phone, String password) {
+    public String phonePwdLogin(String phone, String password, @RequestParam(required = false) Long invitor) {
         User user = userService.loginByPhonePwd(phone, password);
+        InviteType userInviteType = user.getInviteType();
+        if (invitor != null) {
+            if (userInviteType != null) {
+                if (!userInviteType.equals(InviteType.AUCTION)) {
+                    userService.bindAuctionInvitor(user.getId(), invitor);
+                }
+            } else {
+                userService.bindAuctionInvitor(user.getId(), invitor);
+            }
+        }
         return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
     }
 

+ 3 - 26
src/main/java/com/izouma/nineth/web/MetaAdvertRecordController.java

@@ -7,7 +7,6 @@ import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.MetaAdvertRecordRepo;
 import com.izouma.nineth.service.MetaAdvertRecordService;
-import com.izouma.nineth.utils.ObjUtils;
 import com.izouma.nineth.utils.excel.ExcelUtils;
 import lombok.AllArgsConstructor;
 import org.springframework.data.domain.Page;
@@ -16,7 +15,6 @@ 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("/metaAdvertRecord")
@@ -28,21 +26,9 @@ public class MetaAdvertRecordController extends BaseController {
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public MetaAdvertRecord save(@RequestBody MetaAdvertRecord record) {
-        if(record.isPublish()) {
-            MetaAdvertRecord metaAdvertRecord = metaAdvertRecordRepo.findByApplicationAndPublishAndDel(record.getApplication(), true, false);
-            if (Objects.nonNull(metaAdvertRecord) && !Objects.equals(metaAdvertRecord.getId(), record.getId())) {
-                throw new BusinessException("当前用途下已存在发布状态的广告!");
-            }
-        }
-        if (record.getId() != null) {
-            MetaAdvertRecord orig = metaAdvertRecordRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
-            ObjUtils.merge(orig, record);
-            return metaAdvertRecordRepo.save(orig);
-        }
-        return metaAdvertRecordRepo.save(record);
+        return metaAdvertRecordService.save(record);
     }
 
-
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/all")
     public Page<MetaAdvertRecord> all(@RequestBody PageQuery pageQuery) {
@@ -68,20 +54,11 @@ public class MetaAdvertRecordController extends BaseController {
 
     @GetMapping("/findAll")
     public MetaRestResult<List<MetaAdvertRecord>> findAll() {
-        List<MetaAdvertRecord> metaAdvertRecords = metaAdvertRecordRepo.findByPublishAndDel(true, false);
+        List<MetaAdvertRecord> metaAdvertRecords = metaAdvertRecordRepo.findByDel(false);
         if (CollectionUtils.isEmpty(metaAdvertRecords)) {
-            return MetaRestResult.returnError("当前无发布广告,请先配置!");
+            return MetaRestResult.returnError("当前无广告,请先配置!");
         }
         return MetaRestResult.returnSuccess(metaAdvertRecords);
     }
-
-    @GetMapping("/{application}/findByApplication")
-    public MetaRestResult<MetaAdvertRecord> findByApplication(@PathVariable int application) {
-        MetaAdvertRecord metaAdvertRecord = metaAdvertRecordRepo.findByApplicationAndPublishAndDel(application, true, false);
-        if (Objects.isNull(metaAdvertRecord)) {
-            return MetaRestResult.returnError("当前用途无发布广告,请先配置!");
-        }
-        return MetaRestResult.returnSuccess(metaAdvertRecord);
-    }
 }
 

+ 1 - 2
src/main/java/com/izouma/nineth/web/MetaGameBoxPointsController.java

@@ -4,7 +4,6 @@ import com.izouma.nineth.domain.MetaGameBoxPoints;
 import com.izouma.nineth.dto.MetaGameBoxPointsDTO;
 import com.izouma.nineth.dto.MetaRestResult;
 import com.izouma.nineth.dto.PageQuery;
-import com.izouma.nineth.enums.MetaPointTypeEnum;
 import com.izouma.nineth.service.MetaGameBoxPointsService;
 import com.izouma.nineth.utils.excel.ExcelUtils;
 import lombok.AllArgsConstructor;
@@ -27,7 +26,7 @@ public class MetaGameBoxPointsController extends BaseController {
     }
 
     @GetMapping("/{userId}/{type}/get")
-    public MetaRestResult<MetaGameBoxPointsDTO> get(@PathVariable Long userId, @PathVariable MetaPointTypeEnum type) {
+    public MetaRestResult<MetaGameBoxPointsDTO> get(@PathVariable Long userId, @PathVariable int type) {
         return metaGameBoxPointsService.get(userId, type);
     }
 

+ 89 - 0
src/main/java/com/izouma/nineth/web/MetaLuckyDrawAwardReceiveRecordController.java

@@ -0,0 +1,89 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.MetaLuckyDraw;
+import com.izouma.nineth.domain.MetaLuckyDrawAwardModel;
+import com.izouma.nineth.domain.MetaLuckyDrawAwardReceiveRecord;
+import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.MetaAwardTypeEnum;
+import com.izouma.nineth.repo.MetaLuckyDrawAwardReceiveRecordRepo;
+import com.izouma.nineth.repo.MetaLuckyDrawRepo;
+import com.izouma.nineth.service.MetaLuckyDrawAwardReceiveRecordService;
+import com.izouma.nineth.utils.LotteryUtils;
+import com.izouma.nineth.utils.SecurityUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@RestController
+@RequestMapping("/metaLuckyDrawAwardReceiveRecord")
+@AllArgsConstructor
+public class MetaLuckyDrawAwardReceiveRecordController extends BaseController {
+
+    private MetaLuckyDrawAwardReceiveRecordService metaLuckyDrawAwardReceiveRecordService;
+
+    private MetaLuckyDrawRepo metaLuckyDrawRepo;
+
+    private MetaLuckyDrawAwardReceiveRecordRepo metaLuckyDrawAwardReceiveRecordRepo;
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public MetaRestResult<MetaLuckyDrawAwardReceiveRecord> save(@RequestBody MetaLuckyDrawAwardReceiveRecord metaLuckyDrawAwardReceiveRecord) {
+        if (Objects.isNull(metaLuckyDrawAwardReceiveRecord)) {
+            return MetaRestResult.returnError("Illegal parameter : params must be null");
+        }
+        if (Objects.isNull(metaLuckyDrawAwardReceiveRecord.getMetaLuckyDrawId())) {
+            return MetaRestResult.returnError("Illegal parameter : metaLuckyDrawId must be null");
+        }
+        Long userId = SecurityUtils.getAuthenticatedUser().getId();
+        MetaLuckyDraw metaLuckyDraw = metaLuckyDrawRepo.findById(metaLuckyDrawAwardReceiveRecord.getMetaLuckyDrawId()).orElse(null);
+        if (Objects.isNull(metaLuckyDraw)) {
+            return MetaRestResult.returnError("不存在该抽奖活动");
+        }
+        List<MetaLuckyDrawAwardModel> metaLuckDrawAwards = metaLuckyDraw.getMetaLuckDrawAwards();
+        if (CollectionUtils.isEmpty(metaLuckDrawAwards)) {
+            return MetaRestResult.returnError("该抽奖活动未配置奖励");
+        }
+        List<MetaLuckyDrawAwardModel> newMetaLuckDrawAwards = new ArrayList<>();
+        metaLuckDrawAwards.forEach(metaLuckyDrawAwardModel -> {
+            if (metaLuckyDrawAwardModel.getAwardType().equals(MetaAwardTypeEnum.NFT) && metaLuckyDrawAwardModel.isLimitNum()) {
+                int count = metaLuckyDrawAwardReceiveRecordRepo.countByMetaLuckyDrawIdAndMetaLuckDrawAwardName(metaLuckyDrawAwardReceiveRecord.getMetaLuckyDrawId(), metaLuckyDrawAwardModel.getName());
+                if (count < metaLuckyDrawAwardModel.getMaxNum()) {
+                    newMetaLuckDrawAwards.add(metaLuckyDrawAwardModel);
+                }
+            }
+            newMetaLuckDrawAwards.add(metaLuckyDrawAwardModel);
+        });
+        MetaLuckyDrawAwardModel metaLuckyDrawAwardModel = LotteryUtils.lotteryForMetaLuckyDrawAward(newMetaLuckDrawAwards);
+        metaLuckyDrawAwardReceiveRecord.setUserId(userId);
+        metaLuckyDrawAwardReceiveRecord.setFinishTime(LocalDateTime.now());
+        metaLuckyDrawAwardReceiveRecord.setMetaLuckDrawAward(metaLuckyDrawAwardModel);
+        metaLuckyDrawAwardReceiveRecord.setMetaLuckDrawAwardName(metaLuckyDrawAwardModel.getName());
+        return MetaRestResult.returnSuccess(metaLuckyDrawAwardReceiveRecordRepo.save(metaLuckyDrawAwardReceiveRecord));
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<MetaLuckyDrawAwardReceiveRecord> all(@RequestBody PageQuery pageQuery) {
+        return metaLuckyDrawAwardReceiveRecordService.all(pageQuery);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<MetaLuckyDrawAwardReceiveRecord> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 128 - 0
src/main/java/com/izouma/nineth/web/MetaLuckyDrawController.java

@@ -0,0 +1,128 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.MetaLuckyDraw;
+import com.izouma.nineth.domain.MetaLuckyDrawAwardModel;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.EntryModeType;
+import com.izouma.nineth.enums.MetaAwardTypeEnum;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.MetaLuckyDrawRepo;
+import com.izouma.nineth.service.MetaLuckyDrawService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@RestController
+@RequestMapping("/metaLuckyDraw")
+@AllArgsConstructor
+public class MetaLuckyDrawController extends BaseController {
+    private MetaLuckyDrawService metaLuckyDrawService;
+    private MetaLuckyDrawRepo metaLuckyDrawRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public MetaLuckyDraw save(@RequestBody MetaLuckyDraw record) {
+        List<MetaLuckyDrawAwardModel> metaLuckDrawAwards = record.getMetaLuckDrawAwards();
+        if (CollectionUtils.isNotEmpty(metaLuckDrawAwards)) {
+            AtomicInteger i = new AtomicInteger();
+            List<String> list = new ArrayList<>();
+            metaLuckDrawAwards.forEach(metaLuckDrawAward -> {
+                if (metaLuckDrawAward.getAwardType().equals(MetaAwardTypeEnum.GOLD)) {
+                    metaLuckDrawAward.setLimitNum(false);
+                    metaLuckDrawAward.setMaxNum(0);
+                }
+                if (metaLuckDrawAward.getAwardType().equals(MetaAwardTypeEnum.NFT) && !metaLuckDrawAward.isLimitNum()) {
+                    metaLuckDrawAward.setMaxNum(0);
+                }
+                list.add(metaLuckDrawAward.getName());
+                i.set(i.get() + metaLuckDrawAward.getProbability());
+            });
+            long count = list.stream().distinct().count();
+            if (count < list.size()) {
+                throw new BusinessException("奖励名称不可以重复!");
+            }
+            if (i.get() != 100) {
+                throw new BusinessException("奖励概率总和必须为100!");
+            }
+            record.setMetaLuckDrawAwards(metaLuckDrawAwards);
+        }
+        if (record.isPublish()) {
+            MetaLuckyDraw metaLuckyDraw = metaLuckyDrawRepo.findByNameAndPublishAndDel(record.getName(), true, false);
+            if (Objects.nonNull(metaLuckyDraw) && !Objects.equals(metaLuckyDraw.getId(), record.getId())) {
+                throw new BusinessException("该活动名称已经存在!");
+            }
+        }
+        if (record.getId() != null) {
+            MetaLuckyDraw orig = metaLuckyDrawRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            // 奖励配置为空重置奖励配置
+            if (CollectionUtils.isEmpty(metaLuckDrawAwards)) {
+                orig.setMetaLuckDrawAwards(null);
+            }
+            // NFT入场,重置金币入场数据
+            if (record.getEntryModeType().equals(EntryModeType.NFT)) {
+                record.setGoldNum(0);
+                // NFT入场 人工审核 重置自动匹配规则
+                if (record.isAudit()) {
+                    orig.setRule(null);
+                } else {
+                    // NFT入场 自动匹配 重置人工审核
+                    orig.setCollectionName(null);
+                }
+            }
+            // 金币入场 重置NFT入场规则
+            if (record.getEntryModeType().equals(EntryModeType.GOLD)) {
+                orig.setRule(null);
+                orig.setCollectionName(null);
+                orig.setNum(0);
+            }
+            // 金币或NFT入场
+            if (record.getEntryModeType().equals(EntryModeType.GOLD_OR_NFT)) {
+                // 人工审核 重置自动匹配规则
+                if (record.isAudit()) {
+                    orig.setRule(null);
+                } else {
+                    // 自动匹配 重置人工审核
+                    orig.setCollectionName(null);
+                }
+            }
+            return metaLuckyDrawRepo.save(orig);
+        }
+        return metaLuckyDrawRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<MetaLuckyDraw> all(@RequestBody PageQuery pageQuery) {
+        return metaLuckyDrawService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public MetaLuckyDraw get(@PathVariable Long id) {
+        return metaLuckyDrawRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        metaLuckyDrawRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<MetaLuckyDraw> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 60 - 0
src/main/java/com/izouma/nineth/web/MetaPropController.java

@@ -0,0 +1,60 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.MetaProp;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.MetaPropRepo;
+import com.izouma.nineth.service.MetaPropService;
+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;
+
+@RestController
+@RequestMapping("/metaProp")
+@AllArgsConstructor
+public class MetaPropController extends BaseController {
+    private MetaPropService metaPropService;
+    private MetaPropRepo metaPropRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public MetaProp save(@RequestBody MetaProp record) {
+        if (record.getId() != null) {
+            MetaProp orig = metaPropRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return metaPropRepo.save(orig);
+        }
+        return metaPropRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<MetaProp> all(@RequestBody PageQuery pageQuery) {
+        return metaPropService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public MetaProp get(@PathVariable Long id) {
+        return metaPropRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        metaPropRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<MetaProp> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 11 - 2
src/main/java/com/izouma/nineth/web/MetaSwitchController.java

@@ -27,9 +27,9 @@ public class MetaSwitchController extends BaseController {
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public MetaSwitch save(@RequestBody MetaSwitch record) {
-        MetaSwitch metaSwitch = metaSwitchRepo.findByNameAndDel(record.getName(), false);
+        MetaSwitch metaSwitch = metaSwitchRepo.findByNameAndSwitchTypeAndDel(record.getName(), record.getSwitchType(), false);
         if (Objects.nonNull(metaSwitch) && !Objects.equals(metaSwitch.getId(), record.getId())) {
-            throw new BusinessException("该配置名称已经存在!");
+            throw new BusinessException("该类型配置名称已经存在!");
         }
         if (record.getId() != null) {
             MetaSwitch orig = metaSwitchRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
@@ -68,6 +68,15 @@ public class MetaSwitchController extends BaseController {
         ExcelUtils.export(response, data);
     }
 
+    @GetMapping("/{id}/findSwitch")
+    public MetaRestResult<MetaSwitch> findSwitch(@PathVariable Long id) {
+        MetaSwitch metaSwitch = metaSwitchRepo.findById(id).orElse(null);
+        if (Objects.isNull(metaSwitch)) {
+            return MetaRestResult.returnError("缺少该开关配置");
+        }
+        return MetaRestResult.returnSuccess(metaSwitch);
+    }
+
     @GetMapping("/metaQuery")
     public MetaRestResult<List<MetaSwitch>> metaQuery() {
         return MetaRestResult.returnSuccess(metaSwitchRepo.findByDel(false));

BIN
src/main/resources/font/VonwaonBitmap_16pxLite.ttf


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
src/main/resources/genjson/MetaLuckyDraw.json


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

@@ -0,0 +1 @@
+{"tableName":"MetaLuckyDrawAwardReceiveRecord","className":"MetaLuckyDrawAwardReceiveRecord","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"},{"name":"metaLuckyDrawId","modelName":"metaLuckyDrawId","remark":"抽奖活动id","showInList":true,"showInForm":true,"formType":"number"},{"name":"metaLuckDrawAwardName","modelName":"metaLuckDrawAwardName","remark":"奖励名称","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"metaLuckDrawAward","modelName":"metaLuckDrawAward","remark":"玩家抽奖获得的奖励","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"finishTime","modelName":"finishTime","remark":"奖励获得时间","showInList":true,"showInForm":true,"formType":"datetime"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.MetaLuckyDrawAwardReceiveRecord"}

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

@@ -0,0 +1 @@
+{"tableName":"MetaProp","className":"MetaProp","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":"name","modelName":"name","remark":"名称","showInList":true,"showInForm":true,"formType":"singleLineText","required":true},{"name":"introduction","modelName":"introduction","remark":"介绍","showInList":true,"showInForm":true,"formType":"textarea","required":true},{"name":"pic","modelName":"pic","remark":"图片","showInList":true,"showInForm":true,"formType":"singleImage","required":true},{"name":"propType","modelName":"propType","remark":"道具类型","showInList":true,"showInForm":true,"formType":"select","required":true,"apiFlag":"1","optionsValue":"[{\"label\":\"永久\",\"value\":\"PERMANENT\"},{\"label\":\"一次性\",\"value\":\"DISPOSABLE\"},{\"label\":\"限时\",\"value\":\"LIMITED\"}]"},{"name":"usedType","modelName":"usedType","remark":"使用类型","showInList":true,"showInForm":true,"formType":"select","required":true,"apiFlag":"1","optionsValue":"[{\"label\":\"主动\",\"value\":\"ACTIVE\"},{\"label\":\"被动\",\"value\":\"PASSIVE\"}]"},{"name":"times","modelName":"times","remark":"限时道具时间,单位小时","showInList":true,"showInForm":true,"formType":"number"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.MetaProp"}

+ 76 - 11
src/main/vue/src/router.js

@@ -1253,7 +1253,16 @@ const router = new Router({
                     component: () =>
                         import(/* webpackChunkName: "publicScreenChatList" */ '@/views/PublicScreenChatList.vue'),
                     meta: {
-                        title: '公屏对话'
+                        title: '公屏聊天记录'
+                    }
+                },
+                {
+                    path: '/privateScreenChatList',
+                    name: 'PrivateScreenChatList',
+                    component: () =>
+                        import(/* webpackChunkName: "privateScreenChatList" */ '@/views/PrivateScreenChatList.vue'),
+                    meta: {
+                        title: '私聊记录'
                     }
                 },
                 {
@@ -1614,19 +1623,35 @@ const router = new Router({
                     },
                 },
                 {
-                    path: '/metaSwitchEdit',
-                    name: 'MetaSwitchEdit',
-                    component: () => import(/* webpackChunkName: "metaSwitchEdit" */ '@/views/MetaSwitchEdit.vue'),
+                    path: '/metaWebsocketSwitchEdit',
+                    name: 'MetaWebsocketSwitchEdit',
+                    component: () => import(/* webpackChunkName: "metaWebsocketSwitchEdit" */ '@/views/MetaWebsocketSwitchEdit.vue'),
+                    meta: {
+                        title: '元宇宙Websocket开关配置编辑',
+                    },
+                },
+                {
+                    path: '/metaWebsocketSwitchList',
+                    name: 'MetaWebsocketSwitchList',
+                    component: () => import(/* webpackChunkName: "metaWebsocketSwitchList" */ '@/views/MetaWebsocketSwitchList.vue'),
                     meta: {
-                        title: '元宇宙开关配置编辑',
+                        title: '元宇宙Websocket开关配置',
                     },
                 },
                 {
-                    path: '/metaSwitchList',
-                    name: 'MetaSwitchList',
-                    component: () => import(/* webpackChunkName: "metaSwitchList" */ '@/views/MetaSwitchList.vue'),
+                    path: '/metaCommonSwitchEdit',
+                    name: 'MetaCommonSwitchEdit',
+                    component: () => import(/* webpackChunkName: "metaCommonSwitchEdit" */ '@/views/MetaCommonSwitchEdit.vue'),
                     meta: {
-                        title: '元宇宙开关配置',
+                        title: '元宇宙Common开关配置编辑',
+                    },
+                },
+                {
+                    path: '/metaCommonSwitchList',
+                    name: 'MetaCommonSwitchList',
+                    component: () => import(/* webpackChunkName: "metaCommonSwitchList" */ '@/views/MetaCommonSwitchList.vue'),
+                    meta: {
+                        title: '元宇宙Common开关配置',
                     },
                 },
                 {
@@ -1668,7 +1693,47 @@ const router = new Router({
                     meta: {
                         title: '易拍订单',
                     },
-                }
+                },
+                {
+                    path: '/metaPropEdit',
+                    name: 'MetaPropEdit',
+                    component: () => import(/* webpackChunkName: "metaPropEdit" */ '@/views/MetaPropEdit.vue'),
+                    meta: {
+                        title: '元宇宙道具编辑',
+                    },
+                },
+                {
+                    path: '/metaPropList',
+                    name: 'MetaPropList',
+                    component: () => import(/* webpackChunkName: "metaPropList" */ '@/views/MetaPropList.vue'),
+                    meta: {
+                        title: '元宇宙道具',
+                    },
+                },
+                {
+                    path: '/metaLuckyDrawEdit',
+                    name: 'MetaLuckyDrawEdit',
+                    component: () => import(/* webpackChunkName: "metaLuckyDrawEdit" */ '@/views/MetaLuckyDrawEdit.vue'),
+                    meta: {
+                       title: '抽奖活动管理编辑',
+                    },
+                },
+                {
+                    path: '/metaLuckyDrawList',
+                    name: 'MetaLuckyDrawList',
+                    component: () => import(/* webpackChunkName: "metaLuckyDrawList" */ '@/views/MetaLuckyDrawList.vue'),
+                    meta: {
+                       title: '抽奖活动管理',
+                    },
+               },
+                {
+                    path: '/metaLuckyDrawAwardReceiveRecordList',
+                    name: 'MetaLuckyDrawAwardReceiveRecordList',
+                    component: () => import(/* webpackChunkName: "metaLuckyDrawAwardReceiveRecordList" */ '@/views/MetaLuckyDrawAwardReceiveRecordList.vue'),
+                    meta: {
+                       title: '抽奖活动奖励记录',
+                    },
+               }
                 /**INSERT_LOCATION**/
             ]
         },
@@ -1728,4 +1793,4 @@ router.beforeEach((to, from, next) => {
     }
 });
 
-export default router;
+export default router;

+ 5 - 0
src/main/vue/src/util/regRules.js

@@ -0,0 +1,5 @@
+const reg = new RegExp(/^[1-9]\d*$/)
+
+export {
+    reg
+}

+ 178 - 162
src/main/vue/src/views/DomainOrderList.vue

@@ -1,22 +1,24 @@
 <template>
-    <div  class="list-view">
+    <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>
+            <!--            <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"
+                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-button @click="getData" slot="append" icon="el-icon-search"></el-button>
             </el-input>
         </div>
         <el-table :data="tableData" row-key="id" ref="table"
@@ -29,46 +31,45 @@
             </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="userName" label="用户名"
->
-                    </el-table-column>
-                    <el-table-column prop="userAvatar" label="用户头像"
->
-                    </el-table-column>
-                    <el-table-column prop="picName" label="图片名称"
->
-                    </el-table-column>
-                    <el-table-column prop="domainName" label="图片名称"
->
-                    </el-table-column>
-                    <el-table-column prop="status" label="状态"
-                            :formatter="statusFormatter"
-                        >
-                    </el-table-column>
-                    <el-table-column prop="auditResult" label="审核结果"
->
-                    </el-table-column>
-                    <el-table-column prop="orderStatus" label="orderStatus"
-                            :formatter="orderStatusFormatter"
-                        >
-                    </el-table-column>
-                    <el-table-column prop="payMethod" label="payMethod"
-                            :formatter="payMethodFormatter"
-                        >
-                    </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 prop="userId" label="用户id"
+            >
+            </el-table-column>
+            <el-table-column prop="userName" label="用户名"
+            >
+            </el-table-column>
+            <!--            <el-table-column prop="userAvatar" label="用户头像"-->
+            <!--            >-->
+            <!--            </el-table-column>-->
+            <el-table-column prop="picName" label="域名名称"
+            >
+            </el-table-column>
+            <el-table-column prop="domainName" label="域名(包含后缀)"
+            >
+            </el-table-column>
+            <el-table-column prop="transactionId" label="交易流水号"
+            >
             </el-table-column>
+            <el-table-column prop="auditResult" label="审核结果"
+            >
+            </el-table-column>
+            <el-table-column prop="orderStatus" label="订单状态"
+                             :formatter="orderStatusFormatter"
+            >
+            </el-table-column>
+            <el-table-column prop="payMethod" label="支付方式"
+                             :formatter="payMethodFormatter"
+            >
+            </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">
@@ -90,125 +91,140 @@
     </div>
 </template>
 <script>
-    import { mapState } from "vuex";
-    import pageableTable from "@/mixins/pageableTable";
+import {mapState} from "vuex";
+import pageableTable from "@/mixins/pageableTable";
 
-    export default {
-        name: 'DomainOrderList',
-        mixins: [pageableTable],
-        data() {
-            return {
-                multipleMode: false,
-                search: "",
-                url: "/domainOrder/all",
-                downloading: false,
-                        statusOptions:[{"label":"审核中","value":"PENDING"},{"label":"通过","value":"SUCCESS"},{"label":"失败","value":"FAIL"}],
-                        orderStatusOptions:[{"label":"未支付","value":"NOT_PAID"},{"label":"已支付,处理中","value":"PROCESSING"},{"label":"已完成","value":"FINISH"},{"label":"已取消","value":"CANCELLED"}],
-                        payMethodOptions:[{"label":"微信","value":"WEIXIN"},{"label":"支付宝","value":"ALIPAY"},{"label":"无GAS费","value":"FREE"},{"label":"衫德支付","value":"SANDPAY"},{"label":"河马支付","value":"HMPAY"},{"label":"首信易","value":"PAYEASE"},{"label":"余额支付","value":"BALANCE"},{"label":"销毁藏品","value":"DESTROY"}],
+export default {
+    name: 'DomainOrderList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: "",
+            url: "/domainOrder/all",
+            downloading: false,
+            statusOptions: [{"label": "审核中", "value": "PENDING"}, {"label": "通过", "value": "SUCCESS"}, {
+                "label": "失败",
+                "value": "FAIL"
+            }],
+            orderStatusOptions: [{"label": "未支付", "value": "NOT_PAID"}, {
+                "label": "已支付,处理中",
+                "value": "PROCESSING"
+            }, {"label": "已完成", "value": "FINISH"}, {"label": "已取消", "value": "CANCELLED"}],
+            payMethodOptions: [{"label": "微信", "value": "WEIXIN"}, {
+                "label": "支付宝",
+                "value": "ALIPAY"
+            }, {"label": "无GAS费", "value": "FREE"}, {"label": "衫德支付", "value": "SANDPAY"}, {
+                "label": "河马支付",
+                "value": "HMPAY"
+            }, {"label": "首信易", "value": "PAYEASE"}, {"label": "余额支付", "value": "BALANCE"}, {
+                "label": "销毁藏品",
+                "value": "DESTROY"
+            }],
+        }
+    },
+    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 '';
+        },
+        orderStatusFormatter(row, column, cellValue, index) {
+            let selectedOption = this.orderStatusOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
             }
+            return '';
         },
-        computed: {
-            selection() {
-                return this.$refs.table.selection.map(i => i.id);
+        payMethodFormatter(row, column, cellValue, index) {
+            let selectedOption = this.payMethodOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
             }
+            return '';
+        },
+        beforeGetData() {
+            return {search: this.search, query: {orderStatus: 'FINISH', del: false}};
         },
-        methods: {
-                    statusFormatter(row, column, cellValue, index) {
-                        let selectedOption = this.statusOptions.find(i => i.value === cellValue);
-                        if (selectedOption) {
-                            return selectedOption.label;
-                        }
-                        return '';
-                    },
-                    orderStatusFormatter(row, column, cellValue, index) {
-                        let selectedOption = this.orderStatusOptions.find(i => i.value === cellValue);
-                        if (selectedOption) {
-                            return selectedOption.label;
-                        }
-                        return '';
-                    },
-                    payMethodFormatter(row, column, cellValue, index) {
-                        let selectedOption = this.payMethodOptions.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();
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: "/domainOrderEdit",
+                query: {
+                    ...this.$route.query
                 }
-            },
-            addRow() {
-                this.$router.push({
-                    path: "/domainOrderEdit",
-                    query: {
-                        ...this.$route.query
-                    }
-                });
-            },
-            editRow(row) {
-                this.$router.push({
-                    path: "/domainOrderEdit",
-                    query: {
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: "/domainOrderEdit",
+                query: {
                     id: row.id
-                    }
-                });
-            },
-            download() {
-                this.downloading = true;
-                this.$axios
-                    .get("/domainOrder/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(`/domainOrder/del/${row.id}`)
-                }).then(() => {
-                    this.$message.success('删除成功');
-                    this.getData();
-                }).catch(e => {
-                    if (e !== 'cancel') {
-                        this.$message.error(e.error);
-                    }
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get("/domainOrder/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(`/domainOrder/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>

+ 2 - 1
src/main/vue/src/views/MetaAccessoriesEdit.vue

@@ -64,6 +64,7 @@
     </div>
 </template>
 <script>
+import { reg } from '../util/regRules';
 export default {
     name: 'MetaAccessoriesEdit',
     created() {
@@ -81,7 +82,7 @@ export default {
     },
     data() {
         return {
-            reg: /^[1-9]\d*$/,
+            reg,
             saving: false,
             formData: {},
             rules: {

+ 165 - 125
src/main/vue/src/views/MetaAdvertRecordEdit.vue

@@ -1,134 +1,174 @@
 <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="82px"
-                    label-position="right"
-                    size="small"
-                    style="max-width: 500px"
-                >
-                    <el-form-item prop="pic" label="图片">
-                        <single-upload v-model="formData.pic"> </single-upload>
-                    </el-form-item>
-                    <el-form-item prop="link" label="地址">
-                        <el-input v-model="formData.link"> </el-input>
-                    </el-form-item>
-                    <el-form-item prop="application" label="用途">
-                        <el-input-number type="application" v-model="formData.application"> </el-input-number>
-                    </el-form-item>
-                    <el-form-item prop="publish" label="是否发布">
-                        <el-switch v-model="formData.publish"> </el-switch>
-                    </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>
+	<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="82px"
+					label-position="right"
+					size="small"
+					style="max-width: 500px"
+				>
+					<el-form-item prop="name" label="名称">
+						<el-input v-model="formData.name"> </el-input>
+					</el-form-item>
+					<el-form-item prop="detail" label="详情">
+						<el-input type="textarea" v-model="formData.detail"> </el-input>
+					</el-form-item>
+					<el-form-item prop="pic" label="图片">
+						<single-upload v-model="formData.pic"> </single-upload>
+					</el-form-item>
+					<el-form-item prop="link" label="地址">
+						<el-input v-model="formData.link"> </el-input>
+					</el-form-item>
+					<el-form-item prop="price" label="价格">
+						<el-input-number type="number" v-model="formData.price"> </el-input-number>
+					</el-form-item>
+					<el-form-item prop="region" label="区域">
+						<el-input-number type="region" v-model="formData.region" :step="1" :min="1"> </el-input-number>
+						<div class="tip">输入规则:正整数且最小为1</div>
+					</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 { reg } from '../util/regRules';
 export default {
-    name: 'MetaAdvertRecordEdit',
-    created() {
-        if (this.$route.query.id) {
-            this.$http
-                .get('metaAdvertRecord/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: {
-                pic: [
-                    {
-                        required: true,
-                        message: '请输入图片',
-                        trigger: 'blur'
+	name: 'MetaAdvertRecordEdit',
+	created() {
+		if (this.$route.query.id) {
+			this.$http
+				.get('metaAdvertRecord/get/' + this.$route.query.id)
+				.then(res => {
+					this.formData = res;
+				})
+				.catch(e => {
+					console.log(e);
+					this.$message.error(e.error);
+				});
+		}
+	},
+	data() {
+		return {
+            reg,
+			saving: false,
+			formData: {},
+			rules: {
+				pic: [
+					{
+						required: true,
+						message: '请上传图片',
+						trigger: 'blur'
+					}
+				],
+				link: [
+					{
+						required: true,
+						message: '请输入地址',
+						trigger: 'blur'
+					}
+				],
+				name: [
+					{
+						required: true,
+						message: '请输入名称',
+						trigger: 'blur'
+					}
+				],
+				detail: [
+					{
+						required: true,
+						message: '请输入详情',
+						trigger: 'blur'
+					}
+				],
+				price: [
+					{
+						required: true,
+						message: '请输入价格',
+						trigger: 'blur'
+					}
+				],
+				region: [
+					{
+						required: true,
+						message: '请输入区域',
+						trigger: 'blur'
+					},
+					{
+                        validator: (rule, value, callback) => {
+                            if (!this.reg.test(value)) {
+                                callback(new Error('区域必须为大于1的整数'));
+                                return;
+                            } else {
+                                callback();
+                            }
+                        }
                     }
-                ],
-                link: [
-                    {
-                        required: true,
-                        message: '请输入地址',
-                        trigger: 'blur'
-                    }
-                ],
-                application: [
-                    {
-                        required: true,
-                        message: '请输入用途',
-                        trigger: 'blur'
-                    }
-                ]
-            }
-        };
-    },
-    methods: {
-        onSave() {
-            this.$refs.form.validate(valid => {
-                if (valid) {
-                    this.submit();
-                } else {
-                    return false;
-                }
-            });
-        },
-        submit() {
-            let data = { ...this.formData };
+				]
+			}
+		};
+	},
+	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('/metaAdvertRecord/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(`/metaAdvertRecord/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 || '删除失败');
-                    }
-                });
-        }
-    }
+			this.saving = true;
+			this.$http
+				.post('/metaAdvertRecord/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(`/metaAdvertRecord/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>

+ 8 - 10
src/main/vue/src/views/MetaAdvertRecordList.vue

@@ -34,9 +34,13 @@
             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="createdAt" label="时间"> </el-table-column>
-            <el-table-column prop="pic" label="图片">
+            <el-table-column prop="createdAt" align="center" label="时间"> </el-table-column>
+            <el-table-column prop="name" align="center" label="名称"> </el-table-column>
+            <el-table-column prop="detail" align="center" label="详情"> </el-table-column>
+            <el-table-column prop="price" align="center" label="价格"> </el-table-column>
+            <el-table-column prop="region" align="center" label="区域"> </el-table-column>
+            <el-table-column prop="number" align="center" label="编号"> </el-table-column>
+            <el-table-column prop="pic" align="center" label="图片">
                 <template slot-scope="{ row }">
                     <el-image
                         style="width: 30px; height: 30px"
@@ -47,13 +51,7 @@
                     </el-image>
                 </template>
             </el-table-column>
-            <el-table-column prop="link" label="链接地址"> </el-table-column>
-            <el-table-column prop="application" label="用途"> </el-table-column>
-            <el-table-column prop="publish" label="是否发布">
-                <template slot-scope="{ row }">
-                    <el-tag :type="row.publish ? '' : 'info'"> {{ row.publish }} </el-tag>
-                </template>
-            </el-table-column>
+            <el-table-column prop="link" align="center" 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>

+ 2 - 2
src/main/vue/src/views/MetaSwitchEdit.vue → src/main/vue/src/views/MetaCommonSwitchEdit.vue

@@ -36,7 +36,7 @@
 </template>
 <script>
 export default {
-	name: 'MetaSwitchEdit',
+	name: 'MetaCommonSwitchEdit',
 	created() {
 		if (this.$route.query.id) {
 			this.$http
@@ -89,7 +89,7 @@ export default {
 		},
 		submit() {
 			let data = { ...this.formData };
-
+			data.switchType = 'COMMON'
 			this.saving = true;
 			this.$http
 				.post('/metaSwitch/save', data, { body: 'json' })

+ 206 - 0
src/main/vue/src/views/MetaCommonSwitchList.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" align="center" label="ID" width="100"> </el-table-column>
+			<el-table-column prop="name" align="center" label="名称" width="250"> </el-table-column>
+			<el-table-column prop="description" align="center" label="描述"> </el-table-column>
+			<el-table-column prop="status" align="center" label="状态" width="100">
+				<template v-slot="{ row }">
+					<template>
+						{{ row.status ? '开' : '关' }}
+					</template>
+				</template>
+			</el-table-column>
+			<el-table-column label="操作" align="center" fixed="right" width="300">
+				<template slot-scope="{ row }">
+					<el-button @click="editRow(row)" type="primary" size="mini" plain> 编辑 </el-button>
+					<el-button @click="deleteRow(row)" type="danger" size="mini" plain> 删除 </el-button>
+					<el-button
+						@click="updateStatus(row, true)"
+						type="primary"
+						size="mini"
+						plain
+						v-if="row && !row.status"
+					>
+						开
+					</el-button>
+					<el-button
+						@click="updateStatus(row, false)"
+						type="danger"
+						size="mini"
+						plain
+						v-if="row && row.status"
+					>
+						关
+					</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
+		<div class="pagination-wrapper">
+			<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: 'MetaCommonSwitchList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/metaSwitch/all',
+			downloading: false
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		beforeGetData() {
+			return { search: this.search, query: { del: false, switchType: 'COMMON' } };
+		},
+		toggleMultipleMode(multipleMode) {
+			this.multipleMode = multipleMode;
+			if (!multipleMode) {
+				this.$refs.table.clearSelection();
+			}
+		},
+		addRow() {
+			this.$router.push({
+				path: '/metaCommonSwitchEdit',
+				query: {
+					...this.$route.query
+				}
+			});
+		},
+		editRow(row) {
+			this.$router.push({
+				path: '/metaCommonSwitchEdit',
+				query: {
+					id: row.id
+				}
+			});
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/metaSwitch/excel', {
+					responseType: 'blob',
+					params: { size: 10000 , query: { del: false, switchType: 'COMMON' } }
+				})
+				.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);
+				});
+		},
+		deleteRow(row) {
+			this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+				.then(() => {
+					return this.$http.post(`/metaSwitch/del/${row.id}`);
+				})
+				.then(() => {
+					this.$message.success('删除成功');
+					this.getData();
+				})
+				.catch(e => {
+					if (e !== 'cancel') {
+						this.$message.error(e.error);
+					}
+				});
+		},
+		updateStatus(row, status) {
+			this.$alert(status ? '确定要打开此开关吗?' : '确定要关闭此开关吗?', status ? '提示' : '警告', {
+				type: status ? 'info' : 'error'
+			})
+				.then(() => {
+					this.$http.post(`/metaSwitch/updateStatus/${row.id}/${status}`);
+					return;
+				})
+				.then(() => {
+					setTimeout(() => {
+						this.$message.success('操作成功');
+						this.getData();
+					}, 1000);
+				})
+				.catch(e => {
+					if (e !== 'cancel') {
+						this.$message.error(e.error);
+					}
+				});
+		},
+	}
+};
+</script>
+<style lang="less" scoped>
+
+</style>

+ 2 - 1
src/main/vue/src/views/MetaGameCopyEdit.vue

@@ -181,6 +181,7 @@
     </div>
 </template>
 <script>
+import { reg } from '../util/regRules';
 export default {
     name: 'MetaGameCopyEdit',
     created() {
@@ -217,7 +218,7 @@ export default {
         return {
             nft: false,
             gold: false,
-            reg: /^[1-9]\d*$/,
+            reg,
             awardType: [],
             metaZombie: [],
             tags: [],

+ 2 - 2
src/main/vue/src/views/MetaGameCopyList.vue

@@ -51,7 +51,7 @@
             <el-table-column prop="entryModeType" align="center" label="入场方式" :formatter="entryModeTypeFormatter">
             </el-table-column>
             <el-table-column prop="goldNum" align="center" label="所需金币数量"> </el-table-column>
-            <el-table-column prop="rule" label="藏品规则">
+            <el-table-column prop="rule" align="center" label="藏品规则">
                 <template v-slot="{ row }">
                     <template v-if="row.rule">
                         <div v-for="item in row.rule.tags" :key="item.id">
@@ -62,7 +62,7 @@
             </el-table-column>
             <el-table-column prop="audit" label="审核" width="80" align="center">
                 <template v-slot="{ row }">
-                    <template v-if="row.entryModeType === 'NFT'">
+                    <template v-if="row.entryModeType != 'GOLD'">
                         <el-tag type="warning" v-if="row.audit"> 人工 </el-tag>
                         <el-tag type="success" v-else> 自动 </el-tag>
                     </template>

+ 2 - 1
src/main/vue/src/views/MetaGameStageAwardEdit.vue

@@ -47,6 +47,7 @@
     </div>
 </template>
 <script>
+import { reg } from '../util/regRules';
 export default {
     name: 'MetaGameStageAwardEdit',
     created() {
@@ -64,7 +65,7 @@ export default {
     },
     data() {
         return {
-            reg: /^[1-9]\d*$/,
+            reg,
             saving: false,
             formData: {},
             rules: {

+ 118 - 0
src/main/vue/src/views/MetaLuckyDrawAwardReceiveRecordList.vue

@@ -0,0 +1,118 @@
+<template>
+	<div class="list-view">
+		<page-title>
+			<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="metaLuckyDrawId" label="抽奖活动id"> </el-table-column>
+			<el-table-column prop="metaLuckDrawAwardName" label="奖励名称"> </el-table-column>
+			<el-table-column prop="metaLuckDrawAward" label="玩家抽奖获得的奖励"> </el-table-column>
+			<el-table-column prop="finishTime" label="奖励获得时间"> </el-table-column>
+		</el-table>
+		<div class="pagination-wrapper">
+			<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: 'MetaLuckyDrawAwardReceiveRecordList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/metaLuckyDrawAwardReceiveRecord/all',
+			downloading: false
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		beforeGetData() {
+			return { search: this.search, query: { del: false } };
+		},
+		toggleMultipleMode(multipleMode) {
+			this.multipleMode = multipleMode;
+			if (!multipleMode) {
+				this.$refs.table.clearSelection();
+			}
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/metaLuckyDrawAwardReceiveRecord/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);
+				});
+		}
+	}
+};
+</script>
+<style lang="less" scoped>
+
+</style>

+ 491 - 0
src/main/vue/src/views/MetaLuckyDrawEdit.vue

@@ -0,0 +1,491 @@
+<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="108px"
+                    label-position="right"
+                    size="small"
+                    style="max-width: 500px"
+                >
+                    <el-form-item prop="name" label="活动名称">
+                        <el-input v-model="formData.name"> </el-input>
+                    </el-form-item>
+                    <el-form-item label="金币入场配置">
+                        <el-checkbox v-model="gold" @change="goldChange"> </el-checkbox>
+                    </el-form-item>
+                    <el-form-item prop="goldNum" label="所需金币数量" v-if="gold">
+                        <el-input-number type="goldNum" v-model="formData.goldNum" :step="1" :min="1">
+                        </el-input-number>
+                        <div class="tip">输入规则:正整数,最小为1</div>
+                    </el-form-item>
+                    <el-form-item label="NFT入场配置">
+                        <el-checkbox v-model="nft" @change="nftChange"> </el-checkbox>
+                    </el-form-item>
+                    <el-form-item prop="audit" label="是否需要审核" v-if="nft">
+                        <el-radio-group v-model="formData.audit">
+                            <el-radio :label="true"> 人工审核 </el-radio>
+                            <el-radio :label="false"> 自动匹配 </el-radio>
+                        </el-radio-group>
+                    </el-form-item>
+                    <el-form-item prop="collectionName" label="藏品名称" v-if="nft && formData.audit === true">
+                        <el-input v-model="formData.collectionName" class="width"> </el-input>
+                    </el-form-item>
+                    <el-form-item prop="rule" label="匹配规则设置" v-if="nft && formData.audit === false">
+                        <template v-if="formData.rule && formData.rule.and">
+                            <div v-for="(item, i) in formData.rule.and" class="rule-item">
+                                <el-select v-model="item.detail.tag" value-key="id" size="mini">
+                                    <el-option v-for="item in tags" :key="item.id" :value="item" :label="item.name">
+                                    </el-option>
+                                </el-select>
+                                <span style="padding: 0 10px; color: #606266; font-weight: bold"> ×&nbsp;1 </span>
+                                <i @click="delRule(i)" class="el-icon-delete icon-del"> </i>
+                            </div>
+                        </template>
+                        <el-button size="mini" @click="addRule"> 添加 </el-button>
+                    </el-form-item>
+                    <el-form-item prop="num" label="所需nft数量" v-if="nft">
+                        <el-input-number
+                            type="number"
+                            v-model="formData.num"
+                            :step="1"
+                            :min="0"
+                            class="width1"
+                        >
+                        </el-input-number>
+                        <div class="tip">0表示不限</div>
+                    </el-form-item>
+                    <el-form-item prop="detail" label="规则详情" style="width: calc(100vw - 450px)">
+                        <el-input
+                            v-model="formData.detail"
+                            type="textarea"
+                            :autosize="{ minRows: 3, maxRows: 20 }"
+                            placeholder="请输入规则详情"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        prop="metaLuckDrawAwards"
+                        label="奖励配置"
+                        style="width: calc(100vw - 450px)"
+                        size="mini"
+                    >
+                        <el-table :data="formData.metaLuckDrawAwards">
+                            <el-table-column prop="name" label="奖励名称" align="center">
+                                <template v-slot="{ row }">
+                                    <el-input v-model="row.name" style="width: 150px" placeholder="请输入奖励名称">
+                                    </el-input>
+                                    <div class="tip">奖励名称不可重复</div>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="awardType" label="奖励类型" align="center">
+                                <template v-slot="{ row }">
+                                    <el-select
+                                        v-model="row.awardType"
+                                        clearable
+                                        filterable
+                                        placeholder="请选择"
+                                        style="width: 150px"
+                                        @change="changeAwardType(row)"
+                                    >
+                                        <el-option
+                                            v-for="item in entryModeTypeOptions"
+                                            :key="item.value"
+                                            :label="item.label"
+                                            :value="item.value"
+                                        >
+                                        </el-option>
+                                    </el-select>
+                                    <div class="tip">请选择奖励类型</div>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="config" label="配置值(金币-数量/NFT-NFT图片)" align="center">
+                                <template v-slot="{ row }">
+                                    <template v-if="row && row.awardType === 'GOLD'">
+                                        <el-input-number v-model="row.config" :step="1" :min="1"> </el-input-number>
+                                        <div class="tip">输入规则:正整数,最小为1</div>
+                                    </template>
+                                    <template v-if="row && row.awardType === 'NFT'">
+                                        <single-upload v-model="row.config"> </single-upload>
+                                    </template>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="limitNum" label="是否限制数量" align="center">
+                                <template v-slot="{ row }">
+                                    <template v-if="row && row.awardType === 'NFT'">
+                                        <el-switch v-model="row.limitNum" @change="changeLimitNum(row)"> </el-switch>
+                                        <div class="tip">仅限NFT奖励</div>
+                                    </template>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="maxNum" label="最大领取数量" align="center">
+                                <template v-slot="{ row }">
+                                    <template v-if="row && row.awardType === 'NFT' && row.limitNum">
+                                        <el-input-number v-model="row.maxNum" style="width: 150px" :step="1" :min="1">
+                                        </el-input-number>
+                                        <div class="tip">输入规则:正整数,最小为1</div>
+                                    </template>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="probability" label="概率(%)" align="center">
+                                <template v-slot="{ row }">
+                                    <el-input-number
+                                        v-model="row.probability"
+                                        :step="1"
+                                        :min="1"
+                                        :max="100"
+                                        style="width: 150px"
+                                    >
+                                    </el-input-number>
+                                    <div class="tip">输入规则:正整数,最小为1,最大为100</div>
+                                </template>
+                            </el-table-column>
+                            <el-table-column width="100" align="center">
+                                <template v-slot="{ row, $index }">
+                                    <el-button type="danger" plain size="mini" @click="delAward($index)">
+                                        删除
+                                    </el-button>
+                                </template>
+                            </el-table-column>
+                        </el-table>
+                    </el-form-item>
+                    <el-form-item>
+                        <el-button size="mini" @click="addAward"> 添加配置 </el-button>
+                    </el-form-item>
+                    <el-form-item prop="publish" label="是否发布">
+                        <el-switch v-model="formData.publish"> </el-switch>
+                    </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 { reg } from '../util/regRules';
+export default {
+    name: 'MetaLuckyDrawEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('metaLuckyDraw/get/' + this.$route.query.id)
+                .then(res => {
+                    res.metaLuckDrawAwards = res.metaLuckDrawAwards || [];
+                    this.formData = res;
+                    if (res.entryModeType === 'GOLD') {
+                        this.gold = true;
+                        this.nft = false;
+                    } else if (res.entryModeType === 'NFT') {
+                        this.gold = false;
+                        this.nft = true;
+                    } else if (res.entryModeType === 'GOLD_OR_NFT') {
+                        this.gold = true;
+                        this.nft = true;
+                    }
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+        this.$http.post('/tag/all', { size: 10000 }, { body: 'json' }).then(res => {
+            this.tags = res.content;
+        });
+    },
+    data() {
+        return {
+            nft: false,
+            gold: false,
+            reg,
+            awardType: [],
+            tags: [],
+            saving: false,
+            formData: {
+                metaLuckDrawAwards: []
+            },
+            rules: {
+                name: [
+                    {
+                        required: true,
+                        message: '请输入活动名称',
+                        trigger: 'blur'
+                    }
+                ],
+                detail: [
+                    {
+                        required: true,
+                        message: '请输入规则详情',
+                        trigger: 'blur'
+                    }
+                ],
+                goldNum: [
+                    {
+                        required: true,
+                        message: '请输入所需金币数量',
+                        trigger: 'blur'
+                    },
+                    {
+                        validator: (rule, value, callback) => {
+                            if (!this.reg.test(value)) {
+                                callback(new Error('所需金币数量必须为大于1的整数'));
+                                return;
+                            } else {
+                                callback();
+                            }
+                        }
+                    }
+                ],
+                rule: [
+                    { required: true, message: '请选择规则', trigger: 'blur' },
+                    {
+                        validator: (rule, value, callback) => {
+                            if (!this.formData.audit) {
+                                if (!this.formData.rule) {
+                                    callback(new Error('请填写规则'));
+                                } else if (!this.formData.rule.and) {
+                                    callback(new Error('请填写规则'));
+                                } else if (!this.formData.rule.and.length) {
+                                    callback(new Error('请填写规则'));
+                                } else {
+                                    for (let i = 0; i < this.formData.rule.and.length; i++) {
+                                        if (
+                                            !(this.formData.rule.and[i].detail && this.formData.rule.and[i].detail.tag)
+                                        ) {
+                                            callback(new Error('请选择'));
+                                            callback = null;
+                                            break;
+                                        }
+                                    }
+                                    if (callback) {
+                                        callback();
+                                    }
+                                }
+                            } else {
+                                callback();
+                            }
+                        }
+                    }
+                ],
+                metaLuckDrawAwards: [
+                    {
+                        validator: (rule, value, callback) => {
+                            if (value) {
+                                if (!(value instanceof Array)) {
+                                    callback(new Error('metaLuckDrawAwards must be array!'));
+                                    return;
+                                } else {
+                                    for (let i = 0; i < value.length; i++) {
+                                        if (
+                                            value[i].name === '' ||
+                                            value[i].name === undefined ||
+                                            value[i].name === null
+                                        ) {
+                                            callback(new Error('请填写奖励名称'));
+                                            return;
+                                        }
+                                        if (
+                                            value[i].awardType === '' ||
+                                            value[i].awardType === undefined ||
+                                            value[i].awardType === null
+                                        ) {
+                                            callback(new Error('请选择奖励类型'));
+                                            return;
+                                        }
+                                        if (
+                                            value[i].config === '' ||
+                                            value[i].config === undefined ||
+                                            value[i].config === null
+                                        ) {
+                                            callback(new Error('请填写奖励配置'));
+                                            return;
+                                        }
+                                        if (value[i].awardType === 'GOLD' && !this.reg.test(value[i].config)) {
+                                            callback(new Error('奖励金币数量必须为大于1的整数'));
+                                            return;
+                                        }
+                                        if (value[i].awardType === 'NFT' && value[i].limitNum) {
+                                            if (
+                                                value[i].maxNum === '' ||
+                                                value[i].maxNum === undefined ||
+                                                value[i].maxNum === null
+                                            ) {
+                                                callback(new Error('请填写NFT奖励最大领取数量'));
+                                                return;
+                                            }
+
+                                            if (!this.reg.test(value[i].maxNum)) {
+                                                callback(new Error('NFT奖励最大领取数量必须为大于1的整数'));
+                                                return;
+                                            }
+                                        }
+                                        if (
+                                            value[i].probability === '' ||
+                                            value[i].probability === undefined ||
+                                            value[i].probability === null
+                                        ) {
+                                            callback(new Error('请填写奖励概率'));
+                                            return;
+                                        }
+                                        if (!this.reg.test(value[i].probability)) {
+                                            callback(new Error('奖励概率必须为大于1的整数'));
+                                            return;
+                                        }
+                                    }
+                                }
+                            }
+                            callback();
+                        },
+                        trigger: 'blur'
+                    }
+                ],
+                audit: [
+                    {
+                        required: true,
+                        message: '请选择是否审核',
+                        trigger: 'blur'
+                    }
+                ],
+                collectionName: [
+                    {
+                        required: true,
+                        message: '请输入藏品名称',
+                        trigger: 'blur'
+                    }
+                ],
+                num: [
+                    {
+                        required: true,
+                        message: '请输入所需nft数量',
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            entryModeTypeOptions: [
+                { label: 'NFT', value: 'NFT' },
+                { label: '金币', value: 'GOLD' }
+            ]
+        };
+    },
+    methods: {
+        goldChange() {
+            this.$delete(this.formData, 'goldNum');
+        },
+        nftChange() {
+            this.$delete(this.formData, 'audit');
+            this.$delete(this.formData, 'collectionName');
+            this.$delete(this.formData, 'rule');
+            this.$delete(this.formData, 'num');
+        },
+        changeAwardType(row) {
+            row.config = '';
+			row.limitNum = false;
+        },
+		changeLimitNum(row) {
+            row.maxNum = '';
+        },
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+            if (this.gold && !this.nft) {
+                data.entryModeType = 'GOLD';
+            } else if (!this.gold && this.nft) {
+                data.entryModeType = 'NFT';
+            } else if (this.gold && this.nft) {
+                data.entryModeType = 'GOLD_OR_NFT';
+            } else if (!this.gold && !this.nft) {
+                this.$message.error('请最少配置一个入场方式');
+                return;
+            }
+            this.saving = true;
+            this.$http
+                .post('/metaLuckyDraw/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(`/metaLuckyDraw/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 || '删除失败');
+                    }
+                });
+        },
+        addRule() {
+            if (!(this.formData.rule && this.formData.rule.and)) {
+                this.$set(this.formData, 'rule', { and: [] });
+            }
+            this.formData.rule.and.push({ detail: { tag: null, num: 1 } });
+        },
+        delRule(i) {
+            this.formData.rule.and.splice(i, 1);
+        },
+        addAward() {
+            this.formData.metaLuckDrawAwards.push({
+                awardType: '',
+                config: '',
+                probability: '',
+				limitNum:'',
+				maxNum:''
+            });
+        },
+        delAward(i) {
+            this.formData.metaLuckDrawAwards.splice(i, 1);
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+.width1 {
+	width: 150px;
+}
+
+.rule-item {
+	display: flex;
+	align-items: center;
+	margin-bottom: 10px;
+
+	.icon-del {
+		color: #f56c6c;
+		cursor: pointer;
+		font-size: 18px;
+	}
+}
+</style>

+ 209 - 0
src/main/vue/src/views/MetaLuckyDrawList.vue

@@ -0,0 +1,209 @@
+<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" align="center" label="ID" width="100"> </el-table-column>
+			<el-table-column prop="name" align="center" label="名称"> </el-table-column>
+            <el-table-column prop="entryModeType" align="center" label="入场方式" :formatter="entryModeTypeFormatter">
+            </el-table-column>
+            <el-table-column prop="goldNum" align="center" label="所需金币数量"> </el-table-column>
+            <el-table-column prop="rule" align="center" label="藏品规则">
+                <template v-slot="{ row }">
+                    <template v-if="row.rule">
+                        <div v-for="item in row.rule.tags" :key="item.id">
+                            {{ item.name }}
+                        </div>
+                    </template>
+                </template>
+            </el-table-column>
+            <el-table-column prop="audit" label="审核" width="80" align="center">
+                <template v-slot="{ row }">
+                    <template v-if="row.entryModeType != 'GOLD'">
+                        <el-tag type="warning" v-if="row.audit"> 人工 </el-tag>
+                        <el-tag type="success" v-else> 自动 </el-tag>
+                    </template>
+                </template>
+            </el-table-column>
+            <el-table-column prop="collectionName" align="center" label="藏品名称"> </el-table-column>
+            <el-table-column prop="num" align="center" label="所需nft数量"> </el-table-column>
+            <el-table-column prop="detail" align="center" label="规则详情"> </el-table-column>
+            <el-table-column prop="publish" align="center" label="是否发布">
+                <template slot-scope="{ row }">
+                    <el-tag :type="row.publish ? '' : 'info'"> {{ row.publish }} </el-tag>
+                </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" width="350">
+                <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">
+			<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: 'MetaLuckyDrawList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/metaLuckyDraw/all',
+			downloading: false,
+			entryModeTypeOptions: [
+				{ label: 'NFT', value: 'NFT' },
+				{ label: '金币', value: 'GOLD' },
+				{ label: '金币或NFT', value: 'GOLD_OR_NFT' }
+			]
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		entryModeTypeFormatter(row, column, cellValue, index) {
+			let selectedOption = this.entryModeTypeOptions.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: '/metaLuckyDrawEdit',
+				query: {
+					...this.$route.query
+				}
+			});
+		},
+		editRow(row) {
+			this.$router.push({
+				path: '/metaLuckyDrawEdit',
+				query: {
+					id: row.id
+				}
+			});
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/metaLuckyDraw/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(`/metaLuckyDraw/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>

+ 232 - 0
src/main/vue/src/views/MetaPropEdit.vue

@@ -0,0 +1,232 @@
+<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="108px"
+                    label-position="right"
+                    size="small"
+                    style="max-width: 500px"
+                >
+                    <el-form-item prop="propType" label="道具类型">
+                        <el-select v-model="formData.propType" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in propTypeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item prop="name" label="名称">
+                        <el-input v-model="formData.name"> </el-input>
+                    </el-form-item>
+                    <el-form-item prop="introduction" label="介绍">
+                        <el-input type="textarea" v-model="formData.introduction"> </el-input>
+                    </el-form-item>
+                    <el-form-item prop="pic" label="图片">
+                        <single-upload v-model="formData.pic"> </single-upload>
+                    </el-form-item>
+
+                    <el-form-item prop="usedType" label="使用类型">
+                        <el-select v-model="formData.usedType" clearable filterable placeholder="请选择" @change="changeUsedType()">
+                            <el-option
+                                v-for="item in usedTypeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item prop="triggerType" label="触发类型">
+                        <el-select v-model="formData.triggerType" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in triggerTypeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+
+                    <el-form-item prop="times" label="时间(h)" v-if="formData.usedType && formData.usedType === 'LIMITED'">
+                        <el-input-number type="number" v-model="formData.times" :step="1" :min="1"> </el-input-number>
+                        <div class="tip">时间单位为小时,输入规则:正整数且最小为1</div>
+                    </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 { reg } from '../util/regRules';
+export default {
+    name: 'MetaPropEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('metaProp/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+			reg,
+            saving: false,
+            formData: {},
+            rules: {
+                name: [
+                    {
+                        required: true,
+                        message: '请输入名称',
+                        trigger: 'blur'
+                    }
+                ],
+                introduction: [
+                    {
+                        required: true,
+                        message: '请输入介绍',
+                        trigger: 'blur'
+                    }
+                ],
+                pic: [
+                    {
+                        required: true,
+                        message: '请输入图片',
+                        trigger: 'blur'
+                    }
+                ],
+                propType: [
+                    {
+                        required: true,
+                        message: '请选择道具类型',
+                        trigger: 'blur'
+                    }
+                ],
+                usedType: [
+                    {
+                        required: true,
+                        message: '请选择使用类型',
+                        trigger: 'blur'
+                    }
+                ],
+                times: [
+                    {
+                        required: true,
+                        message: '请输入时间',
+                        trigger: 'blur'
+                    },
+					{
+                        validator: (rule, value, callback) => {
+                            if (!this.reg.test(value)) {
+                                callback(new Error('时间必须为大于1的整数'));
+                                return;
+                            } else {
+                                callback();
+                            }
+                        }
+                    }
+                ],
+                triggerType: [
+                    {
+                        required: true,
+                        message: '请选择触发类型',
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            propTypeOptions: [
+                { label: '配饰', value: 'ACCESSORIES' },
+                { label: '载具', value: 'VEHICLE' },
+                { label: '材料', value: 'MATERIAL' },
+                { label: '体验卡', value: 'EXPERIENCE' },
+                { label: '宠物', value: 'PETS' },
+                { label: '其他', value: 'OTHER' }
+            ],
+            usedTypeOptions: [
+                { label: '永久', value: 'PERMANENT' },
+                { label: '一次性', value: 'DISPOSABLE' },
+                { label: '限时', value: 'LIMITED' }
+            ],
+            triggerTypeOptions: [
+                { label: '主动', value: 'ACTIVE' },
+                { label: '被动', value: 'PASSIVE' }
+            ]
+        };
+    },
+    methods: {
+        changeUsedType() {
+            this.$delete(this.formData, 'times');
+        },
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/metaProp/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(`/metaProp/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>

+ 223 - 0
src/main/vue/src/views/MetaPropList.vue

@@ -0,0 +1,223 @@
+<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" align="center" label="ID" width="100"> </el-table-column>
+			<el-table-column prop="name" align="center" label="名称"> </el-table-column>
+			<el-table-column prop="introduction" align="center" label="介绍"> </el-table-column>
+			<el-table-column prop="pic" align="center" label="图片">
+				<template slot-scope="{ row }">
+					<el-image
+						style="width: 30px; height: 30px"
+						:src="row.pic"
+						fit="cover"
+						:preview-src-list="[row.pic]"
+					>
+					</el-image>
+				</template>
+			</el-table-column>
+			<el-table-column prop="propType" align="center" label="道具类型" :formatter="propTypeFormatter"> </el-table-column>
+			<el-table-column prop="usedType" align="center" label="使用类型" :formatter="usedTypeFormatter"> </el-table-column>
+            <el-table-column prop="triggerType" align="center" label="触发类型" :formatter="triggerTypeFormatter"> </el-table-column>
+			<el-table-column prop="times" align="center" label="时间(h)"> </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">
+			<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: 'MetaPropList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/metaProp/all',
+			downloading: false,
+			propTypeOptions: [
+				{ label: '配饰', value: 'ACCESSORIES' },
+				{ label: '载具', value: 'VEHICLE' },
+				{ label: '材料', value: 'MATERIAL' },
+                { label: '体验卡', value: 'EXPERIENCE' },
+                { label: '宠物', value: 'PETS' },
+                { label: '其他', value: 'OTHER' }
+			],
+            usedTypeOptions: [
+				{ label: '永久', value: 'PERMANENT' },
+				{ label: '一次性', value: 'DISPOSABLE' },
+				{ label: '限时', value: 'LIMITED' }
+			],
+			triggerTypeOptions: [
+				{ label: '主动', value: 'ACTIVE' },
+				{ label: '被动', value: 'PASSIVE' }
+			]
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		propTypeFormatter(row, column, cellValue, index) {
+			let selectedOption = this.propTypeOptions.find(i => i.value === cellValue);
+			if (selectedOption) {
+				return selectedOption.label;
+			}
+			return '';
+		},
+		usedTypeFormatter(row, column, cellValue, index) {
+			let selectedOption = this.usedTypeOptions.find(i => i.value === cellValue);
+			if (selectedOption) {
+				return selectedOption.label;
+			}
+			return '';
+		},
+        triggerTypeFormatter(row, column, cellValue, index) {
+			let selectedOption = this.triggerTypeOptions.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: '/metaPropEdit',
+				query: {
+					...this.$route.query
+				}
+			});
+		},
+		editRow(row) {
+			this.$router.push({
+				path: '/metaPropEdit',
+				query: {
+					id: row.id
+				}
+			});
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/metaProp/excel', {
+					responseType: 'blob',
+					params: { size: 10000 , query: { del: false } }
+				})
+				.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(`/metaProp/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>

+ 2 - 1
src/main/vue/src/views/MetaSignAwardEdit.vue

@@ -88,6 +88,7 @@
 	</div>
 </template>
 <script>
+import { reg } from '../util/regRules';
 export default {
 	name: 'MetaSignAwardEdit',
 	created() {
@@ -110,7 +111,7 @@ export default {
 		return {
 			metaAccessories: [],
 			saving: false,
-			reg: /^[1-9]\d*$/,
+			reg,
 			formData: {},
 			rules: {
 				date: [

+ 2 - 1
src/main/vue/src/views/MetaSignEdit.vue

@@ -94,6 +94,7 @@
 	</div>
 </template>
 <script>
+import { reg } from '../util/regRules';
 export default {
 	name: 'MetaSignEdit',
 	created() {
@@ -116,7 +117,7 @@ export default {
 		return {
 			metaAccessories: [],
 			saving: false,
-			reg: /^[1-9]\d*$/,
+			reg,
 			formData: {},
 			rules: {
 				date: [

+ 2 - 1
src/main/vue/src/views/MetaSpatialInfoEdit.vue

@@ -68,6 +68,7 @@
     </div>
 </template>
 <script>
+import { reg } from '../util/regRules';
 export default {
     name: 'MetaSpatialInfoEdit',
     created() {
@@ -92,7 +93,7 @@ export default {
         return {
             saving: false,
             formData: {},
-            reg: /^[1-9]\d*$/,
+            reg,
             rules: {
                 type: [
                     {

+ 2 - 1
src/main/vue/src/views/MetaTaskEdit.vue

@@ -124,6 +124,7 @@
 	</div>
 </template>
 <script>
+import { reg } from '../util/regRules';
 export default {
 	name: 'MetaTaskEdit',
 	created() {
@@ -141,7 +142,7 @@ export default {
 	},
 	data() {
 		return {
-			reg: /^[1-9]\d*$/,
+			reg,
 			saving: false,
 			formData: {},
 			rules: {

+ 128 - 0
src/main/vue/src/views/MetaWebsocketSwitchEdit.vue

@@ -0,0 +1,128 @@
+<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="52px"
+					label-position="right"
+					size="small"
+					style="max-width: 500px"
+				>
+					<el-form-item prop="name" label="名称">
+						<el-input v-model="formData.name" :disabled="!canEdit"> </el-input>
+					</el-form-item>
+					<el-form-item prop="description" label="描述">
+						<el-input v-model="formData.description"> </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: 'MetaWebsocketSwitchEdit',
+	created() {
+		if (this.$route.query.id) {
+			this.$http
+				.get('metaSwitch/get/' + this.$route.query.id)
+				.then(res => {
+					this.formData = res;
+				})
+				.catch(e => {
+					console.log(e);
+					this.$message.error(e.error);
+				});
+		}
+	},
+	computed: {
+		canEdit() {
+			return !!!this.$route.query.id;
+		}
+	},
+	data() {
+		return {
+			saving: false,
+			formData: {},
+			rules: {
+				name: [
+					{
+						required: true,
+						message: '请输入名称',
+						trigger: 'blur'
+					}
+				],
+				description: [
+					{
+						required: true,
+						message: '请输入描述',
+						trigger: 'blur'
+					}
+				]
+			}
+		};
+	},
+	methods: {
+		onSave() {
+			this.$refs.form.validate(valid => {
+				if (valid) {
+					this.submit();
+				} else {
+					return false;
+				}
+			});
+		},
+		submit() {
+			let data = { ...this.formData };
+			data.switchType = 'WEBSOCKET'
+			this.saving = true;
+			this.$http
+				.post('/metaSwitch/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(`/metaSwitch/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>

+ 5 - 5
src/main/vue/src/views/MetaSwitchList.vue → src/main/vue/src/views/MetaWebsocketSwitchList.vue

@@ -98,7 +98,7 @@ import { mapState } from 'vuex';
 import pageableTable from '@/mixins/pageableTable';
 
 export default {
-	name: 'MetaSwitchList',
+	name: 'MetaWebsocketSwitchList',
 	mixins: [pageableTable],
 	data() {
 		return {
@@ -126,7 +126,7 @@ export default {
 	},
 	methods: {
 		beforeGetData() {
-			return { search: this.search, query: { del: false } };
+			return { search: this.search, query: { del: false, switchType: 'WEBSOCKET' } };
 		},
 		toggleMultipleMode(multipleMode) {
 			this.multipleMode = multipleMode;
@@ -136,7 +136,7 @@ export default {
 		},
 		addRow() {
 			this.$router.push({
-				path: '/metaSwitchEdit',
+				path: '/metaWebsocketSwitchEdit',
 				query: {
 					...this.$route.query
 				}
@@ -144,7 +144,7 @@ export default {
 		},
 		editRow(row) {
 			this.$router.push({
-				path: '/metaSwitchEdit',
+				path: '/metaWebsocketSwitchEdit',
 				query: {
 					id: row.id
 				}
@@ -155,7 +155,7 @@ export default {
 			this.$axios
 				.get('/metaSwitch/excel', {
 					responseType: 'blob',
-					params: { size: 10000 }
+					params: { size: 10000 , query: { del: false, switchType: 'WEBSOCKET' } }
 				})
 				.then(res => {
 					console.log(res);

+ 158 - 0
src/main/vue/src/views/PrivateScreenChatList.vue

@@ -0,0 +1,158 @@
+<template>
+    <div class="list-view">
+        <page-title>
+            <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="userId" align="center" label="发送方用户id"> </el-table-column>
+            <el-table-column prop="nickname" align="center" label="发送方昵称"> </el-table-column>
+            <el-table-column prop="avatar" align="center" label="发送方头像">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.avatar"
+                        fit="cover"
+                        :preview-src-list="[row.avatar]"
+                    >
+                    </el-image>
+                </template>
+            </el-table-column>
+            <el-table-column prop="time" align="center" width="200" label="消息发送时间"> </el-table-column>
+            <el-table-column prop="chatType" align="center" label="类型" :formatter="typeFormatter"> </el-table-column>
+            <el-table-column prop="messageInfo" align="center" width="800" label="消息内容"> </el-table-column>
+            <el-table-column prop="toUserId" align="center" label="接收方用户id"> </el-table-column>
+            <el-table-column prop="toUserNickname" align="center" label="接收方昵称"> </el-table-column>
+            <el-table-column prop="toUserAvatar" align="center" label="接收方头像">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.avatar"
+                        fit="cover"
+                        :preview-src-list="[row.avatar]"
+                    >
+                    </el-image>
+                </template>
+            </el-table-column>
+            <el-table-column prop="illegal" align="center" label="是否合法">
+                <template slot-scope="{ row }">
+                    <el-tag :type="row.illegal ? '' : 'info'"> {{ row.illegal }} </el-tag>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <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: 'PrivateScreenChatList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            typeOptions: [
+                { label: '默认', value: 'DEFAULT' },
+                { label: '公频聊天', value: 'PUBLIC' },
+                { label: '私聊', value: 'PRIVATE' }
+            ],
+            multipleMode: false,
+            search: '',
+            url: '/publicScreenChat/all',
+            downloading: false
+        };
+    },
+    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, type: 1, chatType: 'PRIVATE' } };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/publicScreenChat/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000, query: { del: false, type: 1, chatType: 'PRIVATE' } }
+                })
+                .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);
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+
+</style>

+ 133 - 123
src/main/vue/src/views/PublicScreenChatList.vue

@@ -1,133 +1,143 @@
 <template>
-	<div class="list-view">
-		<page-title>
-			<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="nickname" align="center" label="用户昵称"> </el-table-column>
-			<el-table-column prop="userId" align="center" label="用户id"> </el-table-column>
-			<el-table-column prop="avatar" align="center" label="头像">
-				<template slot-scope="{ row }">
-					<el-image
-						style="width: 30px; height: 30px"
-						:src="row.avatar"
-						fit="cover"
-						:preview-src-list="[row.avatar]"
-					>
-					</el-image>
-				</template>
-			</el-table-column>
-			<el-table-column prop="level" align="center" label="等级"> </el-table-column>
-			<el-table-column prop="realm" align="center" label="境界"> </el-table-column>
-			<el-table-column prop="title" align="center" label="头衔"> </el-table-column>
-			<el-table-column prop="messageInfo" align="center" width="800" label="消息内容"> </el-table-column>
-			<el-table-column prop="illegal" align="center" label="是否合法">
-				<template slot-scope="{ row }">
-					<el-tag :type="row.illegal ? '' : 'info'"> {{ row.illegal }} </el-tag>
-				</template>
-			</el-table-column>
-			<el-table-column prop="time" align="center" width="200" label="消息发送时间"> </el-table-column>
-		</el-table>
-		<div class="pagination-wrapper">
-			<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>
+    <div class="list-view">
+        <page-title>
+            <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="userId" align="center" width="150" label="发送方用户id"> </el-table-column>
+            <el-table-column prop="nickname" align="center" width="150" label="发送方昵称"> </el-table-column>
+            <el-table-column prop="avatar" align="center" width="150" label="发送方头像">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.avatar"
+                        fit="cover"
+                        :preview-src-list="[row.avatar]"
+                    >
+                    </el-image>
+                </template>
+            </el-table-column>
+            <el-table-column prop="time" align="center" width="200" label="消息发送时间"> </el-table-column>
+            <el-table-column prop="chatType" align="center" label="类型" :formatter="typeFormatter"> </el-table-column>
+            <el-table-column prop="messageInfo" align="center" width="800" label="消息内容"> </el-table-column>
+            <el-table-column prop="illegal" align="center" label="是否合法">
+                <template slot-scope="{ row }">
+                    <el-tag :type="row.illegal ? '' : 'info'"> {{ row.illegal }} </el-tag>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <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: 'PublicScreenChatList',
-	mixins: [pageableTable],
-	data() {
-		return {
-			multipleMode: false,
-			search: '',
-			url: '/publicScreenChat/all',
-			downloading: false
-		};
-	},
-	computed: {
-		selection() {
-			return this.$refs.table.selection.map(i => i.id);
-		}
-	},
-	methods: {
-		beforeGetData() {
-			return { search: this.search, query: { del: false, type: 1 } };
-		},
-		toggleMultipleMode(multipleMode) {
-			this.multipleMode = multipleMode;
-			if (!multipleMode) {
-				this.$refs.table.clearSelection();
-			}
-		},
-		download() {
-			this.downloading = true;
-			this.$axios
-				.get('/publicScreenChat/excel', {
-					responseType: 'blob',
-					params: { size: 10000 ,query: { del: false }},
-				})
-				.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);
-				});
-		}
-	}
+    name: 'PublicScreenChatList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            typeOptions: [
+                { label: '默认', value: 'DEFAULT' },
+                { label: '公频聊天', value: 'PUBLIC' },
+                { label: '私聊', value: 'PRIVATE' }
+            ],
+            multipleMode: false,
+            search: '',
+            url: '/publicScreenChat/all',
+            downloading: false
+        };
+    },
+    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, type: 1, chatType: 'PUBLIC' } };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/publicScreenChat/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000, query: { del: false, type: 1, chatType: 'PUBLIC' } }
+                })
+                .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);
+                });
+        }
+    }
 };
 </script>
 <style lang="less" scoped>

+ 20 - 5
src/test/java/com/izouma/nineth/service/UserServiceTest.java

@@ -2,13 +2,14 @@ package com.izouma.nineth.service;
 
 import com.alibaba.fastjson.JSONObject;
 import com.alipay.api.AlipayApiException;
+import com.google.zxing.WriterException;
 import com.huifu.adapay.core.exception.BaseAdaPayException;
 import com.izouma.nineth.ApplicationTests;
 import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.domain.IdentityAuth;
-import com.izouma.nineth.domain.User;
 import com.izouma.nineth.domain.netease.NeteaseMessage;
 import com.izouma.nineth.domain.netease.Team;
+import com.izouma.nineth.domain.User;
 import com.izouma.nineth.dto.BankValidate;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.dto.UserBankCard;
@@ -27,11 +28,11 @@ import com.izouma.nineth.utils.BankUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
 
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -39,6 +40,12 @@ import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 
+import org.springframework.boot.test.context.SpringBootTest;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.imageio.ImageIO;
+
 @Slf4j
 @ExtendWith(SpringExtension.class)
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@@ -60,6 +67,8 @@ public class UserServiceTest extends ApplicationTests {
     private TeamService           teamService;
     @Autowired
     private NeteaseMessageService neteaseMessageService;
+    @Autowired
+    private DomainOrderService    domainOrderService;
 
     @Test
     public void findByUsernameAndDelFalse1() {
@@ -259,6 +268,12 @@ public class UserServiceTest extends ApplicationTests {
 
     @Test
     public void tTe() {
-        teamService.getUnreadCount("9850","7807550605");
+        teamService.getUnreadCount("9850", "7807550605");
+    }
+
+    @Test
+    public void shareImg() throws IOException, FontFormatException, WriterException {
+        BufferedImage img = domainOrderService.domainImg("thomeboydontkill.nft");
+        ImageIO.write(img, "jpg", new File("1.jpg"));
     }
 }

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels