Selaa lähdekoodia

Merge branch 'dev'

xiongzhu 3 vuotta sitten
vanhempi
commit
7e133109bf
62 muutettua tiedostoa jossa 2405 lisäystä ja 207 poistoa
  1. 10 9
      src/main/java/com/izouma/nineth/config/Constants.java
  2. 29 0
      src/main/java/com/izouma/nineth/converter/CoordinateConverter.java
  3. 11 0
      src/main/java/com/izouma/nineth/domain/Asset.java
  4. 3 0
      src/main/java/com/izouma/nineth/domain/Banner.java
  5. 6 0
      src/main/java/com/izouma/nineth/domain/BlindBoxItem.java
  6. 10 0
      src/main/java/com/izouma/nineth/domain/Collection.java
  7. 27 0
      src/main/java/com/izouma/nineth/domain/Company.java
  8. 27 0
      src/main/java/com/izouma/nineth/domain/MetaBonusScene.java
  9. 4 2
      src/main/java/com/izouma/nineth/domain/MetaPlayerInfo.java
  10. 29 0
      src/main/java/com/izouma/nineth/domain/MetaPlayerOfflineInfo.java
  11. 32 0
      src/main/java/com/izouma/nineth/domain/MetaPlayerWear.java
  12. 37 0
      src/main/java/com/izouma/nineth/domain/MetaPlayerWearCount.java
  13. 41 0
      src/main/java/com/izouma/nineth/domain/MetaSpatialInfo.java
  14. 3 0
      src/main/java/com/izouma/nineth/domain/Order.java
  15. 5 1
      src/main/java/com/izouma/nineth/domain/SpaceObjectsInfo.java
  16. 3 0
      src/main/java/com/izouma/nineth/dto/BuildingDTO.java
  17. 6 2
      src/main/java/com/izouma/nineth/dto/BuildingPosDTO.java
  18. 20 0
      src/main/java/com/izouma/nineth/dto/CoordinateDTO.java
  19. 22 0
      src/main/java/com/izouma/nineth/enums/MetaBonusSceneEnum.java
  20. 22 0
      src/main/java/com/izouma/nineth/enums/MetaPlayerWearEnum.java
  21. 25 0
      src/main/java/com/izouma/nineth/enums/MetaRegionEnum.java
  22. 16 0
      src/main/java/com/izouma/nineth/repo/MetaBonusSceneRepo.java
  23. 10 0
      src/main/java/com/izouma/nineth/repo/MetaPlayerOfflineInfoRepo.java
  24. 22 0
      src/main/java/com/izouma/nineth/repo/MetaPlayerWearRepo.java
  25. 26 0
      src/main/java/com/izouma/nineth/repo/MetaSpatialInfoRepo.java
  26. 6 2
      src/main/java/com/izouma/nineth/repo/SpaceObjectsInfoRepo.java
  27. 8 1
      src/main/java/com/izouma/nineth/security/WebSecurityConfig.java
  28. 2 0
      src/main/java/com/izouma/nineth/service/AssetMintService.java
  29. 3 15
      src/main/java/com/izouma/nineth/service/AssetService.java
  30. 23 18
      src/main/java/com/izouma/nineth/service/CollectionService.java
  31. 0 40
      src/main/java/com/izouma/nineth/service/GiftOrderService.java
  32. 100 31
      src/main/java/com/izouma/nineth/service/MetaPlayerInfoService.java
  33. 20 0
      src/main/java/com/izouma/nineth/service/MetaPlayerWearService.java
  34. 20 0
      src/main/java/com/izouma/nineth/service/MetaSpatialInfoService.java
  35. 142 0
      src/main/java/com/izouma/nineth/service/OrderPayService.java
  36. 1 40
      src/main/java/com/izouma/nineth/service/OrderService.java
  37. 91 0
      src/main/java/com/izouma/nineth/web/AlipayNotifyController.java
  38. 0 6
      src/main/java/com/izouma/nineth/web/AssetController.java
  39. 11 0
      src/main/java/com/izouma/nineth/web/AuctionOrderController.java
  40. 10 8
      src/main/java/com/izouma/nineth/web/CollectionController.java
  41. 12 0
      src/main/java/com/izouma/nineth/web/GiftOrderController.java
  42. 43 0
      src/main/java/com/izouma/nineth/web/MetaBonusSceneController.java
  43. 31 17
      src/main/java/com/izouma/nineth/web/MetaPlayerInfoController.java
  44. 100 0
      src/main/java/com/izouma/nineth/web/MetaPlayerWearController.java
  45. 87 0
      src/main/java/com/izouma/nineth/web/MetaSpatialInfoController.java
  46. 28 2
      src/main/java/com/izouma/nineth/web/OnOffController.java
  47. 30 0
      src/main/java/com/izouma/nineth/web/OrderPayControllerV2.java
  48. 35 0
      src/main/java/com/izouma/nineth/web/RechargeOrderController.java
  49. 8 4
      src/main/resources/application.yaml
  50. 1 0
      src/main/resources/genjson/MetaPlayerWear.json
  51. 1 0
      src/main/resources/genjson/MetaSpatialInfo.json
  52. 198 0
      src/main/resources/static/wx_alipay_bridge.html
  53. 94 0
      src/main/vue/src/components/ColorSelect.vue
  54. 33 1
      src/main/vue/src/router.js
  55. 22 0
      src/main/vue/src/views/BannerEdit.vue
  56. 17 4
      src/main/vue/src/views/BlindBoxEdit.vue
  57. 9 2
      src/main/vue/src/views/CollectionEdit.vue
  58. 179 0
      src/main/vue/src/views/MetaPlayerWearEdit.vue
  59. 193 0
      src/main/vue/src/views/MetaPlayerWearList.vue
  60. 192 0
      src/main/vue/src/views/MetaSpatialInfoEdit.vue
  61. 206 0
      src/main/vue/src/views/MetaSpatialInfoList.vue
  62. 3 2
      src/test/java/com/izouma/nineth/service/CollectionServiceTest.java

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

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

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

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

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

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

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

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

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

@@ -100,4 +100,10 @@ public class BlindBoxItem extends BaseEntity {
 
 //    @ApiModelProperty("持有几天")
 //    private Integer holdDays;
+
+    @Column(columnDefinition = "bigint default 1 not null")
+    private Long companyId;
+
+    @Transient
+    private Collection collection;
 }

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

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

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

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

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

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

+ 4 - 2
src/main/java/com/izouma/nineth/domain/MetaPlayerInfo.java

@@ -7,6 +7,8 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.util.List;
+
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
@@ -22,7 +24,7 @@ public class MetaPlayerInfo {
     @ApiModelProperty("勋章等级")
     private int level;
 
-//    @ApiModelProperty("服饰")
-//    private MetaClothes metaClothes;
+    @ApiModelProperty("玩家上次离线的服饰信息")
+    private List<MetaPlayerWear> metaPlayerWearList;
 
 }

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

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

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

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

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

@@ -0,0 +1,37 @@
+package com.izouma.nineth.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("元宇宙服饰相关")
+public class MetaPlayerWearCount {
+
+    @ApiModelProperty("用户id")
+    private Long userId;
+
+    @ApiModelProperty("头饰")
+    private List<MetaPlayerWear> headdressList;
+
+    @ApiModelProperty("衣服")
+    private List<MetaPlayerWear> clothesList;
+
+    @ApiModelProperty("裤子")
+    private List<MetaPlayerWear> trousersList;
+
+    @ApiModelProperty("鞋子")
+    private List<MetaPlayerWear> shoesList;
+
+    @ApiModelProperty("动作")
+    private List<MetaPlayerWear> actionList;
+
+    @ApiModelProperty("皮肤")
+    private List<MetaPlayerWear> skinList;
+}

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

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

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

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

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

@@ -33,7 +33,11 @@ public class SpaceObjectsInfo extends BaseEntity{
     @ApiModelProperty("空间中已编辑物品信息")
     private List<BuildingPosDTO> buildingPosList;
 
-    @ApiModelProperty("空间中物品信息统计")
+    @ApiModelProperty("空间中剩余物品信息统计")
     @Transient
     private List<BuildingDTO> buildingList;
+
+    @ApiModelProperty("是否个人空间")
+    @Transient
+    private boolean personal;
 }

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

@@ -16,6 +16,9 @@ public class BuildingDTO {
     @ApiModelProperty("物品id")
     private Long itmeId;
 
+    @ApiModelProperty("物品名称")
+    private String name;
+
     @ApiModelProperty("物品数量")
     private int count;
 }

+ 6 - 2
src/main/java/com/izouma/nineth/dto/BuildingPosDTO.java

@@ -1,12 +1,15 @@
 package com.izouma.nineth.dto;
 
 
+import com.izouma.nineth.converter.CoordinateConverter;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import javax.persistence.Convert;
+
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
@@ -17,8 +20,9 @@ public class BuildingPosDTO {
     private Long itemId;
 
     @ApiModelProperty("位置信息")
-    private String pos;
+    @Convert(converter = CoordinateConverter.class)
+    private CoordinateDTO coordinate;
 
     @ApiModelProperty("旋转值")
-    private String rotateCount;
+    private int rotateCount;
 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 6 - 2
src/main/java/com/izouma/nineth/repo/SpaceObjectsInfoRepo.java

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

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

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

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

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

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

@@ -200,6 +200,7 @@ public class AssetService {
                 .orElseThrow(new BusinessException("盲盒不存在"));
         Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
                 .orElseThrow(new BusinessException("藏品不存在"));
+        winItem.setCollection(collection);
         Asset asset = Asset.create(winItem, user, holdDays);
         asset.setTokenId(TokenUtils.genTokenId());
         asset.setNumber(number);
@@ -287,6 +288,7 @@ public class AssetService {
                 .number(asset.getNumber())
                 .tags(new HashSet<>())
                 .prefixName(asset.getPrefixName())
+                .companyId(asset.getCompanyId())
                 .build();
         if (asset.getTags() != null) {
             collection.getTags().addAll(asset.getTags());
@@ -399,6 +401,7 @@ public class AssetService {
                 .number(asset.getNumber())
                 .tags(new HashSet<>())
                 .prefixName(asset.getPrefixName())
+                .companyId(asset.getCompanyId())
                 .build();
         if (asset.getTags() != null) {
             collection.getTags().addAll(asset.getTags());
@@ -949,21 +952,6 @@ public class AssetService {
                 .build());
     }
 
-    @Async
-    public void giveBonus() {
-        List<TokenHistory> list = tokenHistoryRepo.findByOperationAndCreatedAtBefore("销毁", LocalDateTime.of(2022, 7, 23, 21, 0, 0));
-        list.parallelStream().forEach(tokenHistory -> {
-            String name = assetRepo.findFirstByTokenId(tokenHistory.getTokenId()).getName();
-            if (Pattern.matches(".*僵尸动物园SSR #.*", name)) {
-                log.info("SSR奖励 {}", name);
-                userBalanceService.modifyBalance(tokenHistory.getFromUserId(), new BigDecimal("1000"), BalanceType.BONUS, null, false, null);
-            } else if (Pattern.matches(".*僵尸动物园SR #.*", name)) {
-                log.info("SR奖励 {}", name);
-                userBalanceService.modifyBalance(tokenHistory.getFromUserId(), new BigDecimal("25"), BalanceType.BONUS, null, false, null);
-            }
-        });
-    }
-
     public List<MetaPlayerRole> metaPlayerRole(Long userId) {
         List<MetaPlayerRole> metaPlayerRoles = new ArrayList<>();
         metaPlayerRoles.add(build(userId, "艾弗森", 1L));

+ 23 - 18
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -46,6 +46,8 @@ import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -370,7 +372,7 @@ public class CollectionService {
     }
 
     @Transactional
-    public Collection createBlindBox(CreateBlindBox createBlindBox) {
+    public Collection createBlindBox(CreateBlindBox createBlindBox) throws Exception {
         Collection blindBox = createBlindBox.getBlindBox();
         if (blindBox.getId() != null) {
             throw new BusinessException("无法完成此操作");
@@ -398,23 +400,26 @@ public class CollectionService {
         blindBox.setStock(blindBox.getTotal());
         blindBox.setSale(0);
         collectionRepo.save(blindBox);
-        createBlindBox.getItems().stream().parallel().forEach(item -> {
-            Collection collection = list.stream().filter(i -> i.getId().equals(item.getCollectionId())).findAny()
-                    .orElseThrow(new BusinessException("所选藏品不存在"));
-            decreaseStock(collection.getId(), item.getTotal());
-            BlindBoxItem blindBoxItem = new BlindBoxItem();
-            BeanUtils.copyProperties(collection, blindBoxItem);
-            blindBoxItem.setId(null);
-            blindBoxItem.setOasisId(collection.getOasisId());
-            blindBoxItem.setCollectionId(item.getCollectionId());
-            blindBoxItem.setSale(0);
-            blindBoxItem.setTotal(item.getTotal());
-            blindBoxItem.setStock(item.getTotal());
-            blindBoxItem.setRare(item.isRare());
-            blindBoxItem.setBlindBoxId(blindBox.getId());
-            blindBoxItemRepo.saveAndFlush(blindBoxItem);
-            log.info("createBlindBoxItemSuccess" + blindBoxItem.getId());
-        });
+        new ForkJoinPool(128).submit(() -> {
+            createBlindBox.getItems().stream().parallel().forEach(item -> {
+                Collection collection = list.stream().filter(i -> i.getId().equals(item.getCollectionId()))
+                        .findFirst().get();
+                decreaseStock(collection.getId(), item.getTotal());
+                BlindBoxItem blindBoxItem = new BlindBoxItem();
+                BeanUtils.copyProperties(collection, blindBoxItem);
+                blindBoxItem.setId(null);
+                blindBoxItem.setOasisId(collection.getOasisId());
+                blindBoxItem.setCollectionId(item.getCollectionId());
+                blindBoxItem.setSale(0);
+                blindBoxItem.setTotal(item.getTotal());
+                blindBoxItem.setStock(item.getTotal());
+                blindBoxItem.setRare(item.isRare());
+                blindBoxItem.setBlindBoxId(blindBox.getId());
+                blindBoxItem.setCompanyId(collection.getCompanyId());
+                blindBoxItemRepo.saveAndFlush(blindBoxItem);
+                log.info("createBlindBoxItemSuccess" + blindBoxItem.getId());
+            });
+        }).get();
         return blindBox;
     }
 

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

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

+ 100 - 31
src/main/java/com/izouma/nineth/service/MetaPlayerInfoService.java

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

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

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

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

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

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

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

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

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

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

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

+ 0 - 6
src/main/java/com/izouma/nineth/web/AssetController.java

@@ -231,12 +231,6 @@ public class AssetController extends BaseController {
         assetService.lockAsset(SecurityUtils.getAuthenticatedUser().getId(), assetId, duration);
     }
 
-    @PreAuthorize("hasRole('ADMIN')")
-    @GetMapping("/giveBonus")
-    public void giveBonus() {
-        assetService.giveBonus();
-    }
-
     @GetMapping("/{userId}/metaPlayerRole")
     public List<MetaPlayerRole> MetaPlayerRoles(@PathVariable Long userId) {
         return assetService.metaPlayerRole(userId);

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

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

+ 10 - 8
src/main/java/com/izouma/nineth/web/CollectionController.java

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

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

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

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

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

+ 31 - 17
src/main/java/com/izouma/nineth/web/MetaPlayerInfoController.java

@@ -1,14 +1,16 @@
 package com.izouma.nineth.web;
 
-import com.izouma.nineth.domain.MetaPlayerInfo;
-import com.izouma.nineth.domain.SpaceObjectsInfo;
-import com.izouma.nineth.domain.User;
+import cn.hutool.core.collection.CollectionUtil;
+import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.MetaRestResult;
+import com.izouma.nineth.repo.MetaPlayerOfflineInfoRepo;
+import com.izouma.nineth.repo.MetaPlayerWearRepo;
 import com.izouma.nineth.repo.UserRepo;
 import com.izouma.nineth.service.MetaPlayerInfoService;
 import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.List;
 import java.util.Objects;
 
 @RestController
@@ -18,10 +20,12 @@ public class MetaPlayerInfoController {
 
     private UserRepo userRepo;
 
-//    private MetaClothesRepo metaClothesRepo;
-
     private MetaPlayerInfoService metaPlayerInfoService;
 
+    private MetaPlayerOfflineInfoRepo metaPlayerOfflineInfoRepo;
+
+    private MetaPlayerWearRepo metaPlayerWearRepo;
+
     @GetMapping("/{userId}/detail")
     public MetaRestResult<MetaPlayerInfo> findMetaPlayerInfo(@PathVariable Long userId) {
         User user = userRepo.findById(userId).orElse(null);
@@ -32,25 +36,35 @@ public class MetaPlayerInfoController {
         metaPlayerInfo.setAvatar(user.getAvatar());
         metaPlayerInfo.setLevel(user.getLevel());
         metaPlayerInfo.setNickname(user.getNickname());
-//        MetaClothes metaClothes = metaClothesRepo.findByUserIdAndOfflineAndDel(userId, true, false);
-//        if (Objects.nonNull(metaClothes)) {
-//            metaPlayerInfo.setMetaClothes(metaClothes);
-//        }
+        MetaPlayerOfflineInfo metaPlayerOfflineInfo = metaPlayerOfflineInfoRepo.findLastByUserId(userId);
+        if (Objects.isNull(metaPlayerOfflineInfo)) {
+            return MetaRestResult.returnSuccess(metaPlayerInfo);
+        }
+        List<Long> clothesId = metaPlayerOfflineInfo.getClothesId();
+        if (CollectionUtil.isEmpty(clothesId)) {
+            return MetaRestResult.returnSuccess(metaPlayerInfo);
+        }
+        List<MetaPlayerWear> metaPlayerWears = metaPlayerWearRepo.findAllByIdIn(clothesId);
+        metaPlayerInfo.setMetaPlayerWearList(metaPlayerWears);
         return MetaRestResult.returnSuccess(metaPlayerInfo);
     }
 
-    @GetMapping("/{userId}/metaPersonalSpace")
-    public MetaRestResult<SpaceObjectsInfo> metaPersonalSpace(@PathVariable Long userId) {
-        return metaPlayerInfoService.metaPersonalSpace(userId);
-    }
-
-    @GetMapping("/{spaceId}/metaNonPersonalSpace")
-    public MetaRestResult<SpaceObjectsInfo> metaNonPersonalSpace(@PathVariable Long spaceId) {
-        return metaPlayerInfoService.metaNonPersonalSpace(spaceId);
+    @GetMapping("/{userId}/{spaceId}/metaSpaceInfo")
+    public MetaRestResult<SpaceObjectsInfo> metaSpaceInfo(@PathVariable Long userId, @PathVariable Long spaceId) {
+        return metaPlayerInfoService.metaSpaceInfo(userId, spaceId);
     }
 
     @PostMapping("/updateMetaPersonalSpace")
     public MetaRestResult<SpaceObjectsInfo> updateMetaPersonalSpace(@RequestBody SpaceObjectsInfo spaceObjectsInfo) {
         return metaPlayerInfoService.updateMetaPersonalSpace(spaceObjectsInfo);
     }
+
+    @PostMapping("/saveOffline")
+    public MetaRestResult<MetaPlayerOfflineInfo> saveOffline(@RequestBody MetaPlayerOfflineInfo metaPlayerOfflineInfo) {
+        if (Objects.isNull(metaPlayerOfflineInfo.getUserId())) {
+            return MetaRestResult.returnError("参数错误,缺少用户id");
+        }
+        MetaPlayerOfflineInfo save = metaPlayerOfflineInfoRepo.save(metaPlayerOfflineInfo);
+        return MetaRestResult.returnSuccess(save);
+    }
 }

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

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

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

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

+ 28 - 2
src/main/java/com/izouma/nineth/web/OnOffController.java

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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