Jelajahi Sumber

Merge branch 'pai_mai' into hb_dev_pai

# Conflicts:
#	src/main/java/com/izouma/nineth/repo/AssetRepo.java
#	src/main/java/com/izouma/nineth/security/WebSecurityConfig.java
#	src/main/java/com/izouma/nineth/service/CacheService.java
#	src/main/java/com/izouma/nineth/service/OrderCancelService.java
#	src/main/java/com/izouma/nineth/service/OrderPayService.java
#	src/main/java/com/izouma/nineth/service/OrderService.java
#	src/main/java/com/izouma/nineth/service/SandPayService.java
#	src/main/java/com/izouma/nineth/service/UserService.java
#	src/main/java/com/izouma/nineth/web/HmPayController.java
#	src/main/java/com/izouma/nineth/web/PayEaseController.java
#	src/main/java/com/izouma/nineth/web/SandPayController.java
#	src/test/java/com/izouma/nineth/repo/UserPropertyRepoTest.java
#	src/test/java/com/izouma/nineth/service/AirDropServiceTest.java
licailing 3 tahun lalu
induk
melakukan
db09d8c82b
92 mengubah file dengan 4921 tambahan dan 154 penghapusan
  1. 7 0
      run_java.sh
  2. 2 0
      src/main/java/com/izouma/nineth/config/Constants.java
  3. 5 0
      src/main/java/com/izouma/nineth/config/RedisKeys.java
  4. 3 0
      src/main/java/com/izouma/nineth/domain/Asset.java
  5. 99 0
      src/main/java/com/izouma/nineth/domain/AuctionActivity.java
  6. 107 0
      src/main/java/com/izouma/nineth/domain/AuctionOrder.java
  7. 25 0
      src/main/java/com/izouma/nineth/domain/AuctionPassRecord.java
  8. 52 0
      src/main/java/com/izouma/nineth/domain/AuctionRecord.java
  9. 2 0
      src/main/java/com/izouma/nineth/domain/BlindBoxItem.java
  10. 2 0
      src/main/java/com/izouma/nineth/domain/Collection.java
  11. 2 0
      src/main/java/com/izouma/nineth/domain/CompanyCollection.java
  12. 3 1
      src/main/java/com/izouma/nineth/domain/NewsLike.java
  13. 4 0
      src/main/java/com/izouma/nineth/domain/UserAddress.java
  14. 31 0
      src/main/java/com/izouma/nineth/dto/auction/AuctionInputDTO.java
  15. 48 0
      src/main/java/com/izouma/nineth/dto/auction/AuctionRecordDTO.java
  16. 27 0
      src/main/java/com/izouma/nineth/dto/oasis/OasisDistrictCollectionDTO.java
  17. 45 0
      src/main/java/com/izouma/nineth/dto/oasis/OasisDistrictDTO.java
  18. 17 0
      src/main/java/com/izouma/nineth/dto/oasis/OasisLoginDTO.java
  19. 4 1
      src/main/java/com/izouma/nineth/enums/AssetStatus.java
  20. 22 0
      src/main/java/com/izouma/nineth/enums/AuctionOrderStatus.java
  21. 17 0
      src/main/java/com/izouma/nineth/enums/AuctionPaymentType.java
  22. 17 0
      src/main/java/com/izouma/nineth/enums/AuctionRecordType.java
  23. 16 0
      src/main/java/com/izouma/nineth/enums/AuctionSource.java
  24. 20 0
      src/main/java/com/izouma/nineth/enums/AuctionStatus.java
  25. 16 0
      src/main/java/com/izouma/nineth/enums/AuctionType.java
  26. 1 0
      src/main/java/com/izouma/nineth/enums/BannerType.java
  27. 1 0
      src/main/java/com/izouma/nineth/enums/TransferReason.java
  28. 1 1
      src/main/java/com/izouma/nineth/listener/CreateOrderListener.java
  29. 1 1
      src/main/java/com/izouma/nineth/listener/RegisterListener.java
  30. 1 1
      src/main/java/com/izouma/nineth/listener/UpdateActivityStockListener.java
  31. 1 1
      src/main/java/com/izouma/nineth/listener/UpdateQuotaListener.java
  32. 1 1
      src/main/java/com/izouma/nineth/listener/UpdateSaleListener.java
  33. 1 1
      src/main/java/com/izouma/nineth/listener/UpdateStockListener.java
  34. 9 0
      src/main/java/com/izouma/nineth/repo/AssetRepo.java
  35. 56 0
      src/main/java/com/izouma/nineth/repo/AuctionActivityRepo.java
  36. 35 0
      src/main/java/com/izouma/nineth/repo/AuctionOrderRepo.java
  37. 21 0
      src/main/java/com/izouma/nineth/repo/AuctionPassRecordRepo.java
  38. 31 0
      src/main/java/com/izouma/nineth/repo/AuctionRecordRepo.java
  39. 4 1
      src/main/java/com/izouma/nineth/repo/CollectionRepo.java
  40. 4 0
      src/main/java/com/izouma/nineth/repo/CompanyCollectionRepo.java
  41. 2 0
      src/main/java/com/izouma/nineth/repo/NewsLikeRepo.java
  42. 3 0
      src/main/java/com/izouma/nineth/repo/UserBalanceRepo.java
  43. 5 0
      src/main/java/com/izouma/nineth/repo/UserRepo.java
  44. 7 0
      src/main/java/com/izouma/nineth/security/WebSecurityConfig.java
  45. 18 4
      src/main/java/com/izouma/nineth/service/AssetService.java
  46. 302 0
      src/main/java/com/izouma/nineth/service/AuctionActivityService.java
  47. 519 0
      src/main/java/com/izouma/nineth/service/AuctionOrderService.java
  48. 209 0
      src/main/java/com/izouma/nineth/service/AuctionRecordService.java
  49. 5 0
      src/main/java/com/izouma/nineth/service/CacheService.java
  50. 1 1
      src/main/java/com/izouma/nineth/service/CaptchaService.java
  51. 37 3
      src/main/java/com/izouma/nineth/service/CollectionService.java
  52. 106 1
      src/main/java/com/izouma/nineth/service/CompanyCollectionService.java
  53. 1 3
      src/main/java/com/izouma/nineth/service/MintOrderService.java
  54. 25 4
      src/main/java/com/izouma/nineth/service/NewsLikeService.java
  55. 20 14
      src/main/java/com/izouma/nineth/service/OrderCancelService.java
  56. 82 18
      src/main/java/com/izouma/nineth/service/OrderPayService.java
  57. 1 2
      src/main/java/com/izouma/nineth/service/OrderService.java
  58. 2 2
      src/main/java/com/izouma/nineth/service/SandPayService.java
  59. 51 0
      src/main/java/com/izouma/nineth/service/UserService.java
  60. 77 0
      src/main/java/com/izouma/nineth/web/AuctionActivityController.java
  61. 124 0
      src/main/java/com/izouma/nineth/web/AuctionOrderController.java
  62. 86 0
      src/main/java/com/izouma/nineth/web/AuctionRecordController.java
  63. 18 0
      src/main/java/com/izouma/nineth/web/AuthenticationController.java
  64. 12 0
      src/main/java/com/izouma/nineth/web/CollectionController.java
  65. 63 4
      src/main/java/com/izouma/nineth/web/CompanyCollectionController.java
  66. 5 0
      src/main/java/com/izouma/nineth/web/HmPayController.java
  67. 14 1
      src/main/java/com/izouma/nineth/web/NewsLikeController.java
  68. 6 4
      src/main/java/com/izouma/nineth/web/OrderPayController.java
  69. 31 0
      src/main/java/com/izouma/nineth/web/OrderPayControllerV2.java
  70. 10 0
      src/main/java/com/izouma/nineth/web/PayEaseController.java
  71. 5 0
      src/main/java/com/izouma/nineth/web/SandPayController.java
  72. 5 5
      src/main/java/com/izouma/nineth/web/SmsController.java
  73. 0 0
      src/main/resources/genjson/AuctionOrder.json
  74. 69 0
      src/main/vue/src/router.js
  75. 410 0
      src/main/vue/src/views/AuctionActivityEdit.vue
  76. 259 0
      src/main/vue/src/views/AuctionActivityList.vue
  77. 270 0
      src/main/vue/src/views/AuctionOrderEdit.vue
  78. 289 0
      src/main/vue/src/views/AuctionOrderList.vue
  79. 289 0
      src/main/vue/src/views/AuctionOrderUsedList.vue
  80. 132 0
      src/main/vue/src/views/AuctionRecordEdit.vue
  81. 196 0
      src/main/vue/src/views/AuctionRecordList.vue
  82. 260 0
      src/main/vue/src/views/AuctionUsedList.vue
  83. 2 0
      src/main/vue/src/views/BannerEdit.vue
  84. 1 0
      src/main/vue/src/views/BannerList.vue
  85. 1 1
      src/main/vue/src/views/MintOrderList.vue
  86. 18 0
      src/test/java/com/izouma/nineth/repo/AuctionRecordRepoTest.java
  87. 24 73
      src/test/java/com/izouma/nineth/repo/UserPropertyRepoTest.java
  88. 10 4
      src/test/java/com/izouma/nineth/repo/UserRepoTest.java
  89. 44 0
      src/test/java/com/izouma/nineth/service/AuctionActivityServiceTest.java
  90. 16 0
      src/test/java/com/izouma/nineth/service/AuctionOrderServiceTest.java
  91. 17 0
      src/test/java/com/izouma/nineth/service/AuctionRecordServiceTest.java
  92. 1 1
      src/test/java/com/izouma/nineth/service/OrderServiceTest.java

+ 7 - 0
run_java.sh

@@ -0,0 +1,7 @@
+cd /usr/local/src/raex_back_dev
+git pull
+sh install-jar.sh
+mvn clean package
+systemctl stop 9th_test
+mv -f target/9th-0.0.1-SNAPSHOT.jar /var/www/9th_test/9th-0.0.1-SNAPSHOT.jar
+systemctl start 9th_test

+ 2 - 0
src/main/java/com/izouma/nineth/config/Constants.java

@@ -37,5 +37,7 @@ public interface Constants {
         String GIFT     = "gift";
         String MINT     = "mintOrder";
         String RECHARGE = "recharge";
+
+        String AUCTION = "auctionOrder";
     }
 }

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

@@ -40,4 +40,9 @@ public class RedisKeys {
     public static final String BIND_CARD = "bindCard::";
 
     public static final String PAY_TMP = "payTmp::";
+
+    public static final String AUCTION_STATUS = "auctionStatus::";
+
+    public static final String AUCTION_ORDER_LOCK = "auctionOrderLock::";
+
 }

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

@@ -170,6 +170,9 @@ public class Asset extends CollectionBaseEntity {
 
     private Long fromAssetId;
 
+    //地块ID
+    private Long oasisId;
+
     @ApiModelProperty("类型")
     @Enumerated(EnumType.STRING)
     private CollectionType type;

+ 99 - 0
src/main/java/com/izouma/nineth/domain/AuctionActivity.java

@@ -0,0 +1,99 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.annotations.Searchable;
+import com.izouma.nineth.converter.FileObjectConverter;
+import com.izouma.nineth.converter.FileObjectListConverter;
+import com.izouma.nineth.enums.AuctionSource;
+import com.izouma.nineth.enums.AuctionStatus;
+import com.izouma.nineth.enums.AuctionType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+@ApiModel("拍卖藏品")
+public class AuctionActivity extends BaseEntity {
+    @ApiModelProperty("起拍人ID")
+    private Long             sellerId;
+    @ApiModelProperty("起拍人昵称")
+    private String           seller;
+    @ApiModelProperty("起拍人头像")
+    private String           sellerAvatar;
+    @ApiModelProperty("拍卖名称")
+    @Searchable
+    private String           name;
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty("拍卖类型")
+    private AuctionType      auctionType;
+    @ApiModelProperty("铸造者")
+    private String           minter;
+    @ApiModelProperty("藏品ID")
+    private Long             assetId;
+    @ApiModelProperty("图片")
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = FileObjectListConverter.class)
+    private List<FileObject> pic;
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = FileObjectConverter.class)
+    private FileObject       model3d;
+    @ApiModelProperty("详情")
+    @Column(columnDefinition = "TEXT")
+    private String           detail;
+    @ApiModelProperty("分类")
+    private String           category;
+    @ApiModelProperty("起拍价")
+    private BigDecimal       startingPrice;
+    @ApiModelProperty("保证金")
+    private BigDecimal       deposit;
+    @ApiModelProperty("一口价")
+    private BigDecimal       fixedPrice;
+    @ApiModelProperty("开始时间")
+    private LocalDateTime    startTime;
+    @ApiModelProperty("加价幅度")
+    private BigDecimal       increment;
+    @ApiModelProperty("截止时间")
+    private LocalDateTime    endTime;
+    //每次出价实时更新
+    @ApiModelProperty("成交价")
+    private BigDecimal       purchasePrice;
+    @ApiModelProperty("买家id")
+    private Long             purchaserId;
+    @ApiModelProperty("当前竞价id")
+    private Long             recordId;
+    @ApiModelProperty("买家")
+    private String           purchaser;
+    @ApiModelProperty("状态")
+    @Enumerated(EnumType.STRING)
+    private AuctionStatus    status;
+    @ApiModelProperty("来源")
+    private AuctionSource    source;
+    @ApiModelProperty("版税比例")
+    private int              royalties;
+    @ApiModelProperty("手续费比例")
+    private int              serviceCharge;
+    @Column(columnDefinition = "int(11) default 0")
+    @ApiModelProperty("围观次数")
+    private int              likes;
+    @Column(columnDefinition = "int(11) default 0")
+    @ApiModelProperty("出价次数")
+    private int              bids;
+
+    private boolean hasFixedPrice;
+    private boolean liked;
+
+    @ApiModelProperty("上架")
+    @Column(columnDefinition = "tinyint unsigned default 0")
+    private boolean onShelf = false;
+}

+ 107 - 0
src/main/java/com/izouma/nineth/domain/AuctionOrder.java

@@ -0,0 +1,107 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.annotations.Searchable;
+import com.izouma.nineth.annotations.SearchableOne;
+import com.izouma.nineth.converter.FileObjectListConverter;
+import com.izouma.nineth.enums.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AuctionOrder extends BaseEntityNoID {
+    @Id
+    private Long id;
+
+    @ApiModelProperty("用户ID")
+    @SearchableOne
+    private Long userId;
+
+    private String nickname;
+
+    @ApiModelProperty("拍卖ID")
+    private Long auctionId;
+
+    @ApiModelProperty("名称")
+    @SearchableOne
+    private String name;
+
+    @ApiModelProperty("图片")
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = FileObjectListConverter.class)
+    private List<FileObject> pic;
+
+    @ApiModelProperty("版税比例")
+    private int royalties;
+
+    @ApiModelProperty("手续费比例")
+    private int serviceCharge;
+
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty("拍卖类型")
+    private AuctionSource source;
+
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty("支付类型")
+    private AuctionPaymentType paymentType;
+
+    @ApiModelProperty("价格")
+    @Column(precision = 10, scale = 2)
+    private BigDecimal price;
+
+//    @ApiModelProperty("gas费")
+//    @Column(precision = 10, scale = 2)
+//    private BigDecimal gasPrice;
+
+    @ApiModelProperty("总价")
+    @Column(precision = 10, scale = 2)
+    private BigDecimal totalPrice;
+
+    @ApiModelProperty("状态")
+    @Enumerated(EnumType.STRING)
+    private AuctionOrderStatus status;
+
+    @ApiModelProperty("支付方式")
+    @Enumerated(EnumType.STRING)
+    private PayMethod payMethod;
+
+    @ApiModelProperty("交易ID")
+    @Searchable
+    @Column(length = 90)
+    private String transactionId;
+
+    @ApiModelProperty("支付时间")
+    private LocalDateTime payTime;
+
+    @ApiModelProperty("取消时间")
+    private LocalDateTime cancelTime;
+
+    @ApiModelProperty("退款时间")
+    private LocalDateTime refundTime;
+
+    @ApiModelProperty("收货人")
+    private String contactName;
+
+    @ApiModelProperty("收货电话")
+    private String contactPhone;
+
+    @ApiModelProperty("收货地址")
+    private String address;
+
+    @ApiModelProperty("拍卖记录id")
+    private Long auctionRecordId;
+
+    @ApiModelProperty("拍卖记录id")
+    private String courierId;
+}

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

@@ -0,0 +1,25 @@
+package com.izouma.nineth.domain;
+
+import io.swagger.annotations.ApiModel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import java.math.BigDecimal;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+@ApiModel("流拍记录表")
+public class AuctionPassRecord extends BaseEntity {
+    private Long auctionId;
+
+    private Long userId;
+
+    private BigDecimal purchasePrice;
+
+}

+ 52 - 0
src/main/java/com/izouma/nineth/domain/AuctionRecord.java

@@ -0,0 +1,52 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.enums.AuctionRecordType;
+import com.izouma.nineth.enums.AuctionType;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import java.math.BigDecimal;
+
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AuctionRecord extends BaseEntity {
+    @ApiModelProperty("用户ID")
+    private Long              userId;
+    @ApiModelProperty("用户头像")
+    private String            avatar;
+    @ApiModelProperty("用户昵称")
+    private String            user;
+    @ApiModelProperty("拍卖活动ID")
+    private Long              auctionId;
+    @ApiModelProperty("拍卖活动")
+    private String            name;
+    @ApiModelProperty("拍卖活动图片")
+    private String            auctionPic;
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty("类型")
+    private AuctionRecordType type;
+    @ApiModelProperty("是否竞得")
+    private boolean           purchased;
+    @ApiModelProperty("出价")
+    private BigDecimal        bidderPrice;
+    @ApiModelProperty("是否支付保证金")
+    private boolean           payDeposit;
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty("拍卖类型")
+    private AuctionType       auctionType;
+//    @ApiModelProperty("收货人")
+//    private String            contactName;
+//    @ApiModelProperty("收货电话")
+//    private String            contactPhone;
+//    @ApiModelProperty("收货地址")
+//    private String            address;
+}

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

@@ -31,6 +31,8 @@ public class BlindBoxItem extends BaseEntity {
 
     private Long collectionId;
 
+    private Long oasisId;
+
     @ApiModelProperty("名称")
     @Searchable
     private String name;

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

@@ -104,6 +104,8 @@ public class Collection extends CollectionBaseEntity {
     @ApiModelProperty("持有者者ID")
     private Long ownerId;
 
+    private Long oasisId;
+
     @ApiModelProperty("铸造者头像")
     private String ownerAvatar;
 

+ 2 - 0
src/main/java/com/izouma/nineth/domain/CompanyCollection.java

@@ -66,6 +66,8 @@ public class CompanyCollection extends BaseEntity {
     @ApiModelProperty("限购识别码")
     private String countId;
 
+    private Long oasisId;
+
     @Column(columnDefinition = "TEXT")
     @Convert(converter = PropertyListConverter.class)
     @ApiModelProperty("特性")

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

@@ -13,7 +13,7 @@ import javax.persistence.Table;
 @Data
 @Entity
 @Table(name = "like_info", indexes =
-        {@Index(columnList = "userId"), @Index(columnList = "newsId"), @Index(columnList = "showroomId")})
+        {@Index(columnList = "userId"), @Index(columnList = "newsId"), @Index(columnList = "showroomId"), @Index(columnList = "auctionId")})
 @AllArgsConstructor
 @NoArgsConstructor
 @Builder
@@ -25,4 +25,6 @@ public class NewsLike extends BaseEntity {
     private Long newsId;
 
     private Long showroomId;
+
+    private Long auctionId;
 }

+ 4 - 0
src/main/java/com/izouma/nineth/domain/UserAddress.java

@@ -35,4 +35,8 @@ public class UserAddress extends BaseEntity {
     private String phone;
 
     private boolean def;
+
+    public String getDetail() {
+        return this.getProvinceName() + " " + this.getCityName() + " " + this.getDistrictName() + " " + this.getAddress();
+    }
 }

+ 31 - 0
src/main/java/com/izouma/nineth/dto/auction/AuctionInputDTO.java

@@ -0,0 +1,31 @@
+package com.izouma.nineth.dto.auction;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class AuctionInputDTO {
+    @ApiModelProperty("藏品id")
+    private Long          assetId;
+    @ApiModelProperty("起拍价")
+    private BigDecimal    startingPrice;
+    @ApiModelProperty("保证金")
+    private BigDecimal    deposit;
+    @ApiModelProperty("一口价")
+    private BigDecimal    fixedPrice;
+    @ApiModelProperty("开始时间")
+    private LocalDateTime startTime;
+    @ApiModelProperty("加价幅度")
+    private BigDecimal    increment;
+    @ApiModelProperty("截止时间")
+    private LocalDateTime endTime;
+
+    private String tradeCode;
+}

+ 48 - 0
src/main/java/com/izouma/nineth/dto/auction/AuctionRecordDTO.java

@@ -0,0 +1,48 @@
+package com.izouma.nineth.dto.auction;
+
+import com.izouma.nineth.domain.FileObject;
+import com.izouma.nineth.enums.*;
+import com.izouma.nineth.service.AuctionOrderService;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import net.bytebuddy.agent.builder.AgentBuilder;
+
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class AuctionRecordDTO {
+    @ApiModelProperty("用户ID")
+    private Long               userId;
+    @ApiModelProperty("创作者")
+    private String             minter;
+    @ApiModelProperty("拍卖活动ID")
+    private Long               auctionId;
+    @ApiModelProperty("拍卖活动")
+    private String             name;
+    @ApiModelProperty("拍卖活动图片")
+    private List<FileObject>   auctionPic;
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty("类型")
+    private AuctionRecordType  type;
+    @ApiModelProperty("是否竞得")
+    private boolean            purchased;
+    @ApiModelProperty("出价")
+    private BigDecimal         bidderPrice;
+    private Long               actPurchasedId;
+    private AuctionStatus      auctionStatus;
+    private Long               orderId;
+    private LocalDateTime      endTime;
+    private LocalDateTime      createdTime;
+    private BigDecimal         deposit;
+    private AuctionType        auctionType;
+    private AuctionOrderStatus orderStatus;
+    private AuctionOrderStatus depositStatus;
+}

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

@@ -0,0 +1,27 @@
+package com.izouma.nineth.dto.oasis;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OasisDistrictCollectionDTO {
+    private Long       oasisId;
+    private String     collectionName;
+    private BigDecimal originPrice;
+    private BigDecimal sellPrice;
+    private String     status;
+    private String     source;
+    private Long       ownerId;
+    private String     owner;
+    private String     ownerName;
+    private String     ownerAvatar;
+    private boolean    salable;
+    private boolean    scanOnly;
+    private Long       collectionId;
+    private String     url;
+}

+ 45 - 0
src/main/java/com/izouma/nineth/dto/oasis/OasisDistrictDTO.java

@@ -0,0 +1,45 @@
+package com.izouma.nineth.dto.oasis;
+
+import com.izouma.nineth.converter.FileObjectConverter;
+import com.izouma.nineth.converter.FileObjectListConverter;
+import com.izouma.nineth.converter.PrivilegeListConverter;
+import com.izouma.nineth.converter.PropertyListConverter;
+import com.izouma.nineth.domain.CollectionProperty;
+import com.izouma.nineth.domain.FileObject;
+import com.izouma.nineth.domain.Privilege;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Convert;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OasisDistrictDTO {
+    private Long                     id;
+    private Long                     oasisId;
+    private String                   name;
+    @Convert(converter = FileObjectListConverter.class)
+    private List<FileObject>         pic;
+    @Convert(converter = FileObjectConverter.class)
+    private FileObject               model3d;
+    private String                   detail;
+    private String                   category;
+    private int                      total;
+    private BigDecimal               price;
+    private BigDecimal               originalPrice;
+
+    private int                      maxCount;
+    @Convert(converter = PropertyListConverter.class)
+    @ApiModelProperty("特性")
+    private List<CollectionProperty> properties;
+    @Convert(converter = PrivilegeListConverter.class)
+    @ApiModelProperty("特权")
+    private List<Privilege>          privileges;
+    @ApiModelProperty("是否可转售")
+    private boolean                  canResale;
+}

+ 17 - 0
src/main/java/com/izouma/nineth/dto/oasis/OasisLoginDTO.java

@@ -0,0 +1,17 @@
+package com.izouma.nineth.dto.oasis;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OasisLoginDTO {
+    private Long   assetId;
+    private Long   oasisId;
+    private String url;
+    private String source;
+    private String token;
+    private Long   userId;
+}

+ 4 - 1
src/main/java/com/izouma/nineth/enums/AssetStatus.java

@@ -6,7 +6,10 @@ public enum AssetStatus {
     TRANSFERRED("已转让"),
     GIFTING("转赠中"),
     GIFTED("已转赠"),
-    MINTING("铸造中")
+    MINTING("铸造中"),
+    AUCTIONING("拍卖中"),
+    AUCTION_TRADING("拍卖中"),
+    AUCTIONED("已拍卖")
     ;
 
     private final String description;

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

@@ -0,0 +1,22 @@
+package com.izouma.nineth.enums;
+
+public enum AuctionOrderStatus {
+    NOT_PAID("未支付"),
+    FINISH("已完成"),
+    CANCELLED("已取消"),
+    REFUNDING("退款中"),
+    REFUNDED("已退款"),
+    DELIVERY("待发货"),
+    RECEIVE("待收货"),
+    AIR_DROP("待空投");
+
+    private final String description;
+
+    AuctionOrderStatus(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

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

@@ -0,0 +1,17 @@
+package com.izouma.nineth.enums;
+
+public enum AuctionPaymentType {
+    DEPOSIT("保证金"),
+    PURCHASE_PRICE("成交金"),
+    FIXED_PRICE("一口价");
+
+    private final String description;
+
+    AuctionPaymentType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

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

@@ -0,0 +1,17 @@
+package com.izouma.nineth.enums;
+
+public enum AuctionRecordType {
+    DEPOSIT("保证金"),
+    BIDDER("竞拍"),
+    FIXEDPRICE("一口价");
+
+    private final String description;
+
+    AuctionRecordType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 16 - 0
src/main/java/com/izouma/nineth/enums/AuctionSource.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.enums;
+
+public enum AuctionSource {
+    OFFICIAL("官方拍卖"),
+    TRANSFER("转让拍卖");
+
+    private final String description;
+
+    AuctionSource(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

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

@@ -0,0 +1,20 @@
+package com.izouma.nineth.enums;
+
+public enum AuctionStatus {
+    NOTSTARTED("未开始"),
+    ONGOING("进行中"),
+    PURCHASED("成交"),
+    FIXED_PRICE_PURCHASED("一口价成交"),
+    PASS("流拍"),
+    FINISH("完成");
+
+    private final String description;
+
+    AuctionStatus(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 16 - 0
src/main/java/com/izouma/nineth/enums/AuctionType.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.enums;
+
+public enum AuctionType {
+    NFT("虚拟藏品"),
+    ENTITY("实物");
+
+    private final String description;
+
+    AuctionType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 1 - 0
src/main/java/com/izouma/nineth/enums/BannerType.java

@@ -5,6 +5,7 @@ public enum BannerType {
     DISCOVER("探索"),
     MINTER("铸造者"),
     MARKET("市场"),
+    AUCTION("拍卖"),
     PC_ACT("PC官方活动"),
     PC_TITLE("PC首页大图");
 

+ 1 - 0
src/main/java/com/izouma/nineth/enums/TransferReason.java

@@ -3,6 +3,7 @@ package com.izouma.nineth.enums;
 public enum TransferReason {
     TRANSFER("转让"),
     GIFT("转赠"),
+    AUCTION("拍卖")
 
     ;
 

+ 1 - 1
src/main/java/com/izouma/nineth/listener/CreateOrderListener.java

@@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit;
         consumerGroup = "${general.create-order-group}",
         topic = "${general.create-order-topic}",
         consumeMode = ConsumeMode.ORDERLY)
-@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
 public class CreateOrderListener implements RocketMQListener<CreateOrderEvent> {
     private OrderService                  orderService;
     private RedisTemplate<String, Object> redisTemplate;

+ 1 - 1
src/main/java/com/izouma/nineth/listener/RegisterListener.java

@@ -26,7 +26,7 @@ import java.util.regex.Pattern;
         consumerGroup = "${general.register-group}",
         topic = "${general.register-topic}",
         consumeMode = ConsumeMode.ORDERLY)
-@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
 public class RegisterListener implements RocketMQListener<RegisterEvent> {
     private       UserService                   userService;
     private       RedisTemplate<String, Object> redisTemplate;

+ 1 - 1
src/main/java/com/izouma/nineth/listener/UpdateActivityStockListener.java

@@ -16,7 +16,7 @@ import org.springframework.stereotype.Service;
         consumerGroup = "${general.update-activity-stock-group}",
         topic = "${general.update-activity-stock-topic}",
         consumeMode = ConsumeMode.ORDERLY)
-@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
 public class UpdateActivityStockListener implements RocketMQListener<Long> {
 
     private MintActivityService mintActivityService;

+ 1 - 1
src/main/java/com/izouma/nineth/listener/UpdateQuotaListener.java

@@ -16,7 +16,7 @@ import org.springframework.stereotype.Service;
         consumerGroup = "${general.update-quota-group}",
         topic = "${general.update-quota-topic}",
         consumeMode = ConsumeMode.ORDERLY)
-@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
 public class UpdateQuotaListener implements RocketMQListener<Long> {
 
     private CollectionService collectionService;

+ 1 - 1
src/main/java/com/izouma/nineth/listener/UpdateSaleListener.java

@@ -22,7 +22,7 @@ import java.util.Set;
         consumerGroup = "${general.update-sale-group}",
         topic = "${general.update-sale-topic}",
         consumeMode = ConsumeMode.ORDERLY)
-@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
 public class UpdateSaleListener implements RocketMQListener<Long> {
 
     private CollectionService             collectionService;

+ 1 - 1
src/main/java/com/izouma/nineth/listener/UpdateStockListener.java

@@ -22,7 +22,7 @@ import java.util.Set;
         consumerGroup = "${general.update-stock-group}",
         topic = "${general.update-stock-topic}",
         consumeMode = ConsumeMode.ORDERLY)
-@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
 public class UpdateStockListener implements RocketMQListener<Long> {
 
     private CollectionService             collectionService;

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

@@ -14,6 +14,7 @@ import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 
 public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationExecutor<Asset> {
     @Query("update Asset t set t.del = true where t.id = ?1")
@@ -27,6 +28,8 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
 
     List<Asset> findByCollectionIdInAndStatus(Iterable<Long> collectionId, AssetStatus status);
 
+    List<Asset> findAllByCollectionIdInAndStatusIn(List<Long> collectionId, Iterable<AssetStatus> statuses);
+
     List<Asset> findByCreatedAtBefore(LocalDateTime localDateTime);
 
     List<Asset> findByConsignmentTrue();
@@ -43,6 +46,8 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
     @Query("select a from Asset a join User u on a.userId = u.id where a.consignment = true and u.settleAccountId is null")
     List<Asset> findNoAccount();
 
+    Set<Asset> findAllByUserIdInAndCollectionId(List<Long> ids, Long collectionId);
+
     List<Asset> findByTxHash(String hash);
 
     List<Asset> findByIdIn(Iterable<Long> ids);
@@ -67,6 +72,8 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
     @Query(value = "select c.id, c.pic, c.model3d, c.minter_avatar, c.owner_avatar, c.detail from asset c", nativeQuery = true)
     List<List<String>> selectResource();
 
+    List<Asset> findAllByOwnerIdAndStatusAndOasisIdNotNull(Long userId, AssetStatus status);
+
     @Modifying
     @Transactional
     @Query(value = "update asset c set c.pic = ?2, c.model3d = ?3, c.minter_avatar = ?4, " +
@@ -76,5 +83,7 @@ public interface AssetRepo extends JpaRepository<Asset, Long>, JpaSpecificationE
 
     Page<Asset> findByUserIdAndStatusAndNameLike(Long userId, AssetStatus status, String name, Pageable pageable);
 
+    List<Asset> findAllByOasisIdInAndStatusIn(List<Long> oasisIds, List<AssetStatus> assetStatuses);
+
     List<Asset> findAllByUserIdAndCollectionIdAndStatus(Long userId, Long collectionId, AssetStatus status);
 }

+ 56 - 0
src/main/java/com/izouma/nineth/repo/AuctionActivityRepo.java

@@ -0,0 +1,56 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.AuctionActivity;
+import com.izouma.nineth.enums.AuctionStatus;
+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.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+public interface AuctionActivityRepo extends JpaRepository<AuctionActivity, Long>, JpaSpecificationExecutor<AuctionActivity> {
+    @Query("update AuctionActivity t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    @Query("select status from AuctionActivity where id = ?1")
+    String getStatus(Long id);
+
+    @Modifying
+    @Transactional
+    @Query("update AuctionActivity set status = ?2 where id = ?1")
+    void updateStatus(Long id, AuctionStatus status);
+
+    @Query("update AuctionActivity t set t.likes = t.likes + ?2 where t.id = ?1")
+    @Modifying
+    @Transactional
+    void addLike(Long id, int num);
+
+    @Transactional
+    @Modifying
+    @Query("update AuctionActivity c set c.status = ?2 where c.id = ?1")
+    void scheduleOnShelf(Long id, AuctionStatus status);
+
+    @Transactional
+    @Modifying
+    @Query("update AuctionActivity c set c.status = ?2 where c.id = ?1")
+    void scheduleOffShelf(Long id, AuctionStatus status);
+
+    List<AuctionActivity> findAllByStatus(AuctionStatus auctionStatus);
+
+    List<AuctionActivity> findByAssetId(Long assetId);
+
+    List<AuctionActivity> findByStartTimeBeforeAndStatusIn(LocalDateTime startTime, Collection<AuctionStatus> status);
+
+    List<AuctionActivity> findByStartTimeBeforeAndStatus(LocalDateTime startTime, AuctionStatus status);
+
+    @Modifying
+    @Transactional
+    @Query("update AuctionActivity set onShelf = ?2 where assetId = ?1 and onShelf <> ?2")
+    void updateOnShelf(Long assetId, boolean onShelf);
+}

+ 35 - 0
src/main/java/com/izouma/nineth/repo/AuctionOrderRepo.java

@@ -0,0 +1,35 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.AuctionOrder;
+import com.izouma.nineth.enums.AuctionPaymentType;
+import com.izouma.nineth.enums.AuctionOrderStatus;
+import com.izouma.nineth.enums.AuctionStatus;
+import com.izouma.nineth.service.AuctionOrderService;
+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.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+public interface AuctionOrderRepo extends JpaRepository<AuctionOrder, Long>, JpaSpecificationExecutor<AuctionOrder> {
+    @Query("update AuctionOrder t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    AuctionOrder findByUserIdAndAuctionIdAndPaymentTypeAndStatusIn(Long userId, Long auctionId, AuctionPaymentType paymentType, Collection<AuctionOrderStatus> status);
+
+    List<AuctionOrder> findAllByAuctionIdAndPaymentTypeAndStatus(Long auctionId, AuctionPaymentType paymentType, AuctionOrderStatus status);
+
+    List<AuctionOrder> findByStatusAndCreatedAtBeforeAndDelFalse(AuctionOrderStatus status, LocalDateTime createdAt);
+
+    AuctionOrder findFirstByAuctionRecordIdOrderByCreatedAt(Long recordId);
+
+    AuctionOrder findFirstByAuctionRecordIdOrderByIdDesc(Long recordId);
+
+    AuctionOrder findFirstByAuctionIdAndPaymentTypeAndStatusNotIn(Long auctionId, AuctionPaymentType type, Collection<AuctionOrderStatus> status);
+}

+ 21 - 0
src/main/java/com/izouma/nineth/repo/AuctionPassRecordRepo.java

@@ -0,0 +1,21 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.AuctionPassRecord;
+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 AuctionPassRecordRepo extends JpaRepository<AuctionPassRecord, Long>, JpaSpecificationExecutor<AuctionPassRecord> {
+    @Query("update AuctionPassRecord t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    @Query(nativeQuery = true, value = "select r.user_id  from auction_pass_record r " +
+            "GROUP BY r.user_id HAVING count(*) > 5")
+    List<Long> checkUserId();
+}

+ 31 - 0
src/main/java/com/izouma/nineth/repo/AuctionRecordRepo.java

@@ -0,0 +1,31 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.AuctionRecord;
+import com.izouma.nineth.enums.AuctionType;
+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 AuctionRecordRepo extends JpaRepository<AuctionRecord, Long>, JpaSpecificationExecutor<AuctionRecord> {
+    @Query("update AuctionRecord t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    AuctionRecord findTopByAuctionIdAndUserIdOrderByIdDesc(Long auctionId, Long userId);
+
+    @Modifying
+    @Transactional
+    void deleteAllByAuctionId(Long auctionId);
+
+    @Query(nativeQuery = true, value = "WITH ranked_messages AS (" +
+            " SELECT m.*, ROW_NUMBER() OVER (PARTITION BY auction_id ORDER BY id DESC) AS rn" +
+            "    FROM auction_record AS m where user_id = ?1 and auction_type = ?2" +
+            ")" +
+            " SELECT * FROM ranked_messages WHERE rn = 1;")
+    List<AuctionRecord> findByUserId(Long userId, String type);
+}

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

@@ -1,6 +1,5 @@
 package com.izouma.nineth.repo;
 
-import com.fasterxml.jackson.annotation.JsonView;
 import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.dto.CollectionInfoDTO;
 import com.izouma.nineth.dto.CollectionStockAndSale;
@@ -164,4 +163,8 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
             "and c.salable = true " +
             "and c.stock > 0 ", nativeQuery = true)
     String lowestPrice(String search);
+
+    Collection findFirstByOnShelfAndAssetId(boolean onShelf, Long assetId);
+
+    List<Collection> findAllByOasisIdInAndSourceAndSaleLessThan(List<Long> oasisIds, CollectionSource source, int sale);
 }

+ 4 - 0
src/main/java/com/izouma/nineth/repo/CompanyCollectionRepo.java

@@ -1,16 +1,20 @@
 package com.izouma.nineth.repo;
 
 import com.izouma.nineth.domain.CompanyCollection;
+import com.izouma.nineth.enums.CollectionStatus;
 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 CompanyCollectionRepo extends JpaRepository<CompanyCollection, Long>, JpaSpecificationExecutor<CompanyCollection> {
     @Query("update CompanyCollection t set t.del = true where t.id = ?1")
     @Modifying
     @Transactional
     void softDelete(Long id);
+
+    List<CompanyCollection> findAllByOasisIdInAndStatusNot(List<Long> ids, CollectionStatus status);
 }

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

@@ -20,5 +20,7 @@ public interface NewsLikeRepo extends JpaRepository<NewsLike, Long>, JpaSpecific
 
     List<NewsLike> findByUserIdAndShowroomId(Long userId, Long showroomId);
 
+    List<NewsLike> findByUserIdAndAuctionId(Long userId, Long auctionId);
+
     List<NewsLike> findByUserIdAndShowroomIdIn(Long userId, Collection<Long> showroomId);
 }

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

@@ -9,6 +9,7 @@ import org.springframework.data.jpa.repository.Query;
 import javax.transaction.Transactional;
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.Optional;
 
 public interface UserBalanceRepo extends JpaRepository<UserBalance, Long>, JpaSpecificationExecutor<UserBalance> {
 
@@ -21,4 +22,6 @@ public interface UserBalanceRepo extends JpaRepository<UserBalance, Long>, JpaSp
     @Transactional
     int unlock(Long userId);
 
+    Optional<UserBalance> findByUserId(Long userId);
+
 }

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

@@ -24,6 +24,11 @@ public interface UserRepo extends JpaRepository<User, Long>, JpaSpecificationExe
     @Query("update User u set u.del = true where u.id = ?1")
     void softDelete(Long id);
 
+    @Transactional
+    @Modifying
+    @Query("update User u set u.del = true where u.del = false and u.id in ?1")
+    void softDeleteIn(Collection<Long> id);
+
     Optional<User> findByUsernameAndDelFalse(String username);
 
     List<User> findAllByAuthoritiesContainsAndDelFalse(Authority authority);

+ 7 - 0
src/main/java/com/izouma/nineth/security/WebSecurityConfig.java

@@ -122,9 +122,16 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/hmpay/**").permitAll()
                 .antMatchers("/payease/**").permitAll()
                 .antMatchers("/order/calcSettle").permitAll()
+                .antMatchers("/auctionOrder/all").permitAll()
+                .antMatchers("/auctionOrder/get/**").permitAll()
+                .antMatchers("/auctionActivity/all").permitAll()
+                .antMatchers("/auctionActivity/get/**").permitAll()
+                .antMatchers("/auctionRecord/all").permitAll()
                 .antMatchers("/ossNotify").permitAll()
                 .antMatchers("/priceList/list").permitAll()
                 .antMatchers("/user/collectionInvitorList").permitAll()
+                .antMatchers("/auth/oasisLogin").permitAll()
+                .antMatchers("/auth/oasisLoginPhone").permitAll()
                 .antMatchers("/payOrder/v2/**/sandQuick").permitAll()
                 .antMatchers("/pay/v2/**/sandQuick").permitAll()
                 // all other requests need to be authenticated

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

@@ -87,6 +87,7 @@ public class AssetService {
         Asset asset = Asset.create(collection, user);
         asset.setTokenId(TokenUtils.genTokenId());
         asset.setNumber(number);
+        asset.setOasisId(collection.getOasisId());
         asset.setOrderId(orderId);
         asset.setPrice(price);
         assetRepo.saveAndFlush(asset);
@@ -115,6 +116,7 @@ public class AssetService {
         Asset asset = Asset.create(winItem, user, holdDays);
         asset.setTokenId(TokenUtils.genTokenId());
         asset.setNumber(number);
+        asset.setOasisId(winItem.getOasisId());
         asset.setOrderId(orderId);
         asset.setPrice(price);
         asset.setTags(new HashSet<>());
@@ -161,6 +163,7 @@ public class AssetService {
                 .minterId(asset.getMinterId())
                 .minterAvatar(asset.getMinterAvatar())
                 .owner(owner.getNickname())
+                .oasisId(asset.getOasisId())
                 .ownerId(owner.getId())
                 .ownerAvatar(owner.getAvatar())
                 .detail(asset.getDetail())
@@ -237,6 +240,7 @@ public class AssetService {
                 .minterAvatar(asset.getMinterAvatar())
                 .owner(owner.getNickname())
                 .ownerId(owner.getId())
+                .oasisId(asset.getOasisId())
                 .ownerAvatar(owner.getAvatar())
                 .detail(asset.getDetail())
                 .type(CollectionType.DEFAULT)
@@ -349,6 +353,7 @@ public class AssetService {
         newAsset.setPrice(price);
         newAsset.setSellPrice(null);
         newAsset.setOrderId(orderId);
+        newAsset.setOasisId(asset.getOasisId());
         newAsset.setFromAssetId(asset.getId());
         newAsset.setType(CollectionType.DEFAULT);
         newAsset.setSource(TransferReason.GIFT == reason ? AssetSource.GIFT : AssetSource.TRANSFER);
@@ -370,7 +375,16 @@ public class AssetService {
         asset.setPublicShow(false);
         asset.setConsignment(false);
         asset.setPublicCollectionId(null);
-        asset.setStatus(TransferReason.GIFT == reason ? AssetStatus.GIFTED : AssetStatus.TRANSFERRED);
+        switch (reason) {
+            case GIFT:
+                asset.setStatus(AssetStatus.GIFTED);
+                break;
+            case AUCTION:
+                asset.setStatus(AssetStatus.AUCTIONED);
+                break;
+            case TRANSFER:
+                asset.setStatus(AssetStatus.TRANSFERRED);
+        }
         asset.setOwner(toUser.getNickname());
         asset.setOwnerId(toUser.getId());
         asset.setOwnerAvatar(toUser.getAvatar());
@@ -598,7 +612,7 @@ public class AssetService {
         assetRepo.save(asset);
     }
 
-//    @Cacheable(cacheNames = "fmaa", key = "#userId+'#'+#mintActivityId+'#'+#pageable.hashCode()")
+    //    @Cacheable(cacheNames = "fmaa", key = "#userId+'#'+#mintActivityId+'#'+#pageable.hashCode()")
     public PageWrapper<Asset> findMintActivityAssetsWrap(Long userId, Long mintActivityId, Pageable pageable) {
         return PageWrapper.of(findMintActivityAssets(userId, mintActivityId, pageable));
     }
@@ -612,8 +626,8 @@ public class AssetService {
             if (tags.isEmpty()) return new PageImpl<>(Collections.emptyList());
             return assetRepo.findAll((Specification<Asset>) (root, query, criteriaBuilder) ->
                     query.distinct(true).where(criteriaBuilder.equal(root.get("userId"), userId),
-                            criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
-                            root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
+                                    criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
+                                    root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
                             .getRestriction(), pageable);
         } else {
             return assetRepo.findByUserIdAndStatusAndNameLike(userId, AssetStatus.NORMAL,

+ 302 - 0
src/main/java/com/izouma/nineth/service/AuctionActivityService.java

@@ -0,0 +1,302 @@
+package com.izouma.nineth.service;
+
+import cn.hutool.core.collection.CollUtil;
+import com.izouma.nineth.annotations.Debounce;
+import com.izouma.nineth.config.RedisKeys;
+import com.izouma.nineth.domain.Asset;
+import com.izouma.nineth.domain.AuctionActivity;
+import com.izouma.nineth.domain.User;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.dto.auction.AuctionInputDTO;
+import com.izouma.nineth.enums.*;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.AssetRepo;
+import com.izouma.nineth.repo.AuctionActivityRepo;
+import com.izouma.nineth.repo.TokenHistoryRepo;
+import com.izouma.nineth.repo.UserRepo;
+import com.izouma.nineth.utils.JpaUtils;
+import com.izouma.nineth.utils.SecurityUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.core.env.Environment;
+import org.springframework.data.domain.Page;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class AuctionActivityService {
+
+    private final AuctionActivityRepo           auctionActivityRepo;
+    private final AssetRepo                     assetRepo;
+    private final UserRepo                      userRepo;
+    private final PasswordEncoder               passwordEncoder;
+    private final RedisTemplate<String, Object> redisTemplate;
+    private final CacheService                  cacheService;
+    private final TaskScheduler                 taskScheduler;
+    private final Environment                   env;
+    private final TokenHistoryRepo              tokenHistoryRepo;
+    private final SysConfigService              sysConfigService;
+
+    private final Map<Long, ScheduledFuture<?>> tasks = new HashMap<>();
+
+    @PostConstruct
+    public void init() {
+        if (Arrays.asList(env.getActiveProfiles()).contains("dev")) {
+            return;
+        }
+        List<AuctionActivity> activities = auctionActivityRepo.findByStartTimeBeforeAndStatus(LocalDateTime.now(),
+                AuctionStatus.NOTSTARTED);
+        for (AuctionActivity activity : activities) {
+            onShelfTask(activity);
+        }
+    }
+
+    public Page<AuctionActivity> all(PageQuery pageQuery) {
+        return auctionActivityRepo
+                .findAll(JpaUtils.toSpecification(pageQuery, AuctionActivity.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+    public AuctionActivity createFromAsset(AuctionInputDTO dto) {
+        Asset asset = assetRepo.findById(dto.getAssetId()).orElseThrow(new BusinessException("暂无"));
+
+        //拍卖周期
+        int auctionCycle = sysConfigService.getInt("auction_cycle");
+
+        AuctionActivity auctionActivity = new AuctionActivity();
+        auctionActivity.setAuctionType(AuctionType.NFT);
+        auctionActivity.setAssetId(dto.getAssetId());
+        auctionActivity.setStatus(AuctionStatus.ONGOING);
+        auctionActivity.setBids(0);
+        auctionActivity.setCategory(asset.getCategory());
+        auctionActivity.setEndTime(LocalDateTime.now().plusDays(auctionCycle));
+        auctionActivity.setDeposit(dto.getDeposit());
+        if (Arrays.asList(env.getActiveProfiles()).contains("staging")) {
+            auctionActivity.setEndTime(LocalDateTime.now().plusMinutes(8));
+        }
+        auctionActivity.setDetail(asset.getDetail());
+        auctionActivity.setFixedPrice(dto.getFixedPrice());
+        auctionActivity.setIncrement(dto.getIncrement());
+        auctionActivity.setMinter(asset.getMinter());
+        auctionActivity.setPic(asset.getPic());
+        auctionActivity.setModel3d(asset.getModel3d());
+        auctionActivity.setName(asset.getName());
+        auctionActivity.setSeller(asset.getOwner());
+        auctionActivity.setSellerId(asset.getOwnerId());
+        auctionActivity.setStartTime(LocalDateTime.now());
+        auctionActivity.setSource(AuctionSource.TRANSFER);
+        //固定值 or 资产值
+        auctionActivity.setServiceCharge(asset.getServiceCharge());
+        auctionActivity.setRoyalties(asset.getRoyalties());
+
+        auctionActivity.setStartingPrice(dto.getStartingPrice());
+        auctionActivity.setHasFixedPrice(auctionActivity.getFixedPrice() != null);
+        return save(auctionActivity, dto.getTradeCode());
+    }
+
+
+    public AuctionActivity save(AuctionActivity record, String tradeCode) {
+        if (record.getSource().equals(AuctionSource.OFFICIAL)) {
+            record.setStatus(AuctionStatus.NOTSTARTED);
+        }
+        if (record.getSource().equals(AuctionSource.TRANSFER) && !record.getSellerId().equals(9859L)) {
+            User user = userRepo.findById(record.getSellerId()).orElseThrow(new BusinessException("无用户信息"));
+            if (!passwordEncoder.matches(tradeCode, user.getTradeCode())) {
+                throw new BusinessException("交易密码错误");
+            }
+            if (!AuthStatus.SUCCESS.equals(user.getAuthStatus())) {
+                throw new BusinessException("未实名或实名未通过");
+            }
+            BigDecimal userBuy = tokenHistoryRepo.userBuy(record.getSellerId());
+            BigDecimal num = sysConfigService.getBigDecimal("auction_lvzhoushi_num");
+            if (userBuy.compareTo(num) < 0) {
+                throw new BusinessException("绿洲石不足");
+            }
+
+            Asset asset = assetRepo.findById(record.getAssetId()).orElseThrow(new BusinessException("未找到该藏品"));
+            if (!asset.getOwnerId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
+                throw new BusinessException("非本人藏品,无法操作.");
+            }
+            if (!asset.getStatus().equals(AssetStatus.NORMAL)) {
+                throw new BusinessException("藏品状态异常,无法操作.");
+            }
+            if (asset.isPublicShow() || asset.isConsignment()) {
+//                throw new BusinessException("藏品已寄售,取消寄售后再申请拍卖。");
+                throw new BusinessException("请先下架藏品");
+            }
+
+            //是否二次拍卖
+            List<AuctionActivity> activity = auctionActivityRepo.findByAssetId(asset.getId());
+            if (CollUtil.isNotEmpty(activity)) {
+                if (activity.stream().anyMatch(ac -> !AuctionStatus.PASS.equals(ac.getStatus()))) {
+                    throw new BusinessException("已有拍卖");
+                }
+                log.info("下架流拍拍卖:assetId-{}", asset.getId());
+                auctionActivityRepo.updateOnShelf(asset.getId(), false);
+            }
+
+            asset.setStatus(AssetStatus.AUCTIONING);
+            assetRepo.save(asset);
+        }
+
+        //上架
+        record.setOnShelf(true);
+        AuctionActivity saved = auctionActivityRepo.save(record);
+        if (saved.getStatus().equals(AuctionStatus.NOTSTARTED)) {
+            onShelfTask(saved);
+        }
+        return saved;
+    }
+
+    public synchronized String changeStatus(Long id, AuctionStatus status) {
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_STATUS + id);
+        if (ops.get() == null) {
+            Boolean success = ops.setIfAbsent(Optional.ofNullable(auctionActivityRepo.getStatus(id))
+                    .orElse(AuctionStatus.NOTSTARTED.toString()), 7, TimeUnit.DAYS);
+            log.info("创建redis拍卖活动状态:{}", success);
+        }
+        String stock = (String) ops.getAndSet(status.toString());
+        syncStatus(id);
+        return stock;
+    }
+
+    @Debounce(key = "#id", delay = 500)
+    public void syncStatus(Long id) {
+        String stock = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + id);
+        if (stock != null) {
+            log.info("同步拍卖活动状态信息{}", id);
+            auctionActivityRepo.updateStatus(id, AuctionStatus.valueOf(stock));
+            cacheService.clearAuction(id);
+        }
+    }
+
+
+    private void onShelfTask(AuctionActivity record) {
+        ScheduledFuture<?> task = tasks.get(record.getId());
+        if (task != null) {
+            if (!task.cancel(true)) {
+                return;
+            }
+        }
+        if (record.getStatus().equals(AuctionStatus.NOTSTARTED)) {
+            if (record.getStartTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
+                Date date = Date.from(record.getStartTime().atZone(ZoneId.systemDefault()).toInstant());
+                ScheduledFuture<?> future = taskScheduler.schedule(() -> {
+//                    AuctionActivity recordNew1 = auctionActivityRepo.findById(record.getId())
+//                            .orElseThrow(new BusinessException("无数据"));
+                    this.changeStatus(record.getId(), AuctionStatus.ONGOING);
+                    tasks.remove(record.getId());
+//                    offShelfTask(auctionActivityRepo.findById(record.getId()).orElseThrow(new BusinessException("无数据")));
+                }, date);
+                tasks.put(record.getId(), future);
+            } else {
+                this.changeStatus(record.getId(), AuctionStatus.ONGOING);
+//                offShelfTask(auctionActivityRepo.findById(record.getId()).orElseThrow(new BusinessException("无数据")));
+            }
+        }
+    }
+
+    public void offShelfTask(AuctionActivity record) {
+        Long id = record.getId();
+        ScheduledFuture<?> task = tasks.get(id);
+        if (task != null) {
+            if (!task.cancel(true)) {
+                return;
+            }
+        }
+//        AuctionActivity recordNew = auctionActivityRepo.findById(id)
+//                .orElseThrow(new BusinessException("无数据"));
+        if (record.getStatus().equals(AuctionStatus.ONGOING)) {
+            if (record.getEndTime().minusSeconds(2).isAfter(LocalDateTime.now())) {
+                Date date = Date.from(record.getEndTime().atZone(ZoneId.systemDefault()).toInstant());
+                ScheduledFuture<?> future = taskScheduler.schedule(() -> {
+                    AuctionActivity recordNew1 = auctionActivityRepo.findById(id)
+                            .orElseThrow(new BusinessException("无数据"));
+
+                    if (ObjectUtils.isNotEmpty(recordNew1.getPurchasePrice())) {
+                        log.info("拍卖成交{}", recordNew1.getId());
+                        this.changeStatus(recordNew1.getId(), AuctionStatus.PURCHASED);
+                    } else {
+                        //没有成交价,无人出价过
+                        log.info("拍卖流拍Task-else{}", recordNew1.getId());
+                        auctionActivityRepo.scheduleOffShelf(recordNew1.getId(), AuctionStatus.PASS);
+
+                        if (AuctionSource.TRANSFER.equals(recordNew1.getSource())) {
+                            Asset asset = assetRepo.findById(recordNew1.getAssetId())
+                                    .orElseThrow(new BusinessException("暂无"));
+                            asset.setStatus(AssetStatus.NORMAL);
+                            asset.setConsignment(false);
+                            asset.setPublicShow(false);
+                            assetRepo.save(asset);
+                        }
+                    }
+                    tasks.remove(id);
+                }, date);
+                tasks.put(id, future);
+            } else {
+                AuctionActivity recordNew1 = auctionActivityRepo.findById(id)
+                        .orElseThrow(new BusinessException("无数据"));
+                if (ObjectUtils.isNotEmpty(recordNew1.getPurchasePrice())) {
+                    log.info("拍卖成交{}", id);
+                    auctionActivityRepo.scheduleOffShelf(id, AuctionStatus.PURCHASED);
+                } else {
+                    log.info("拍卖流拍Task-else-else{}", id);
+                    auctionActivityRepo.scheduleOffShelf(id, AuctionStatus.PASS);
+
+                    if (AuctionSource.TRANSFER.equals(recordNew1.getSource())) {
+                        Asset asset = assetRepo.findById(recordNew1.getAssetId())
+                                .orElseThrow(new BusinessException("暂无"));
+                        asset.setStatus(AssetStatus.NORMAL);
+                        asset.setConsignment(false);
+                        asset.setPublicShow(false);
+                        assetRepo.save(asset);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 定时下架拍卖
+     * (每隔1分钟执行一次)
+     */
+    @Scheduled(cron = "0 */1 * * * ?")
+    public void passAuction() {
+        List<AuctionActivity> activities = auctionActivityRepo.findAllByStatus(AuctionStatus.ONGOING);
+        activities.forEach(activity -> {
+            if (activity.getEndTime().isBefore(LocalDateTime.now())) {
+                if (ObjectUtils.isNotEmpty(activity.getPurchasePrice())) {
+                    log.info("拍卖成交{}", activity.getId());
+                    this.changeStatus(activity.getId(), AuctionStatus.PURCHASED);
+                } else {
+                    //没有成交价,无人出价过
+                    log.info("拍卖流拍Task-else-else{}", activity.getId());
+                    this.changeStatus(activity.getId(), AuctionStatus.PASS);
+
+                    if (AuctionSource.TRANSFER.equals(activity.getSource())) {
+                        Asset asset = assetRepo.findById(activity.getAssetId())
+                                .orElseThrow(new BusinessException("暂无"));
+                        asset.setStatus(AssetStatus.NORMAL);
+                        asset.setConsignment(false);
+                        asset.setPublicShow(false);
+                        assetRepo.save(asset);
+                    }
+                }
+            }
+        });
+    }
+}

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

@@ -0,0 +1,519 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.config.Constants;
+import com.izouma.nineth.config.RedisKeys;
+import com.izouma.nineth.domain.*;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.*;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.*;
+import com.izouma.nineth.service.sms.SmsService;
+import com.izouma.nineth.utils.JpaUtils;
+import com.izouma.nineth.utils.SecurityUtils;
+import com.izouma.nineth.utils.SnowflakeIdWorker;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.data.domain.Page;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import javax.persistence.Transient;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Service
+public class AuctionOrderService {
+
+    @Autowired
+    private AuctionOrderRepo              auctionOrderRepo;
+    @Autowired
+    private SysConfigService              sysConfigService;
+    @Autowired
+    private UserRepo                      userRepo;
+    @Autowired
+    private AssetService                  assetService;
+    @Autowired
+    private AuctionActivityRepo           auctionActivityRepo;
+    @Autowired
+    private AuctionRecordRepo             auctionRecordRepo;
+    @Autowired
+    private AssetRepo                     assetRepo;
+    @Autowired
+    private UserAddressRepo               userAddressRepo;
+    @Autowired
+    private AuctionActivityService        auctionActivityService;
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+    @Autowired
+    private SnowflakeIdWorker             snowflakeIdWorker;
+    @Autowired
+    private AuctionPassRecordRepo         auctionPassRecordRepo;
+    @Lazy
+    @Autowired
+    private OrderPayService               orderPayService;
+    @Autowired
+    private SmsService                    smsService;
+    @Autowired
+    private UserBalanceRepo               userBalanceRepo;
+    @Autowired
+    private BalanceRecordRepo             balanceRecordRepo;
+
+    public Page<AuctionOrder> all(PageQuery pageQuery) {
+        return auctionOrderRepo
+                .findAll(JpaUtils.toSpecification(pageQuery, AuctionOrder.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+    public AuctionOrder create(Long userId, Long auctionId, Long addressId, Long auctionRecordId, AuctionPaymentType type) {
+        User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));
+
+        AuctionActivity auction = auctionActivityRepo.findById(auctionId)
+                .orElseThrow(new BusinessException("无拍卖信息"));
+
+        if (!auction.isOnShelf()) {
+            throw new BusinessException("拍卖已结束");
+        }
+
+        String status = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + auctionId);
+        if (status == null)
+            status = auction.getStatus().toString();
+        switch (AuctionStatus.valueOf(status)) {
+            case NOTSTARTED:
+                throw new BusinessException("拍卖还未开始");
+//            case PURCHASED:
+//                throw new BusinessException("拍卖成交中");
+            case PASS:
+                throw new BusinessException("已经流拍");
+            case FINISH:
+                throw new BusinessException("拍卖已结束");
+            case FIXED_PRICE_PURCHASED:
+                if (AuctionPaymentType.FIXED_PRICE.equals(type)) {
+                    throw new BusinessException("一口价成交中");
+                }
+        }
+
+        if (user.getId().equals(auction.getSellerId())) {
+            throw new BusinessException("不可自己出价自己");
+        }
+
+        if (AuctionPaymentType.PURCHASE_PRICE.equals(type)) {
+            if (auction.getEndTime().isAfter(LocalDateTime.now())) {
+                throw new BusinessException("拍卖还未结束");
+            }
+            AuctionRecord record = auctionRecordRepo.findTopByAuctionIdAndUserIdOrderByIdDesc(auctionId, userId);
+            if (ObjectUtils.isEmpty(record) || !record.isPayDeposit()) {
+                throw new BusinessException("未支付保证金");
+            }
+            if (record.getBidderPrice().compareTo(auction.getPurchasePrice()) != 0) {
+                throw new BusinessException("与成交价不否");
+            }
+            int time = sysConfigService.getInt("auction_cancel_time");
+            if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
+                throw new BusinessException("超过支付时长");
+            }
+        } else {
+            if (auction.getEndTime().isBefore(LocalDateTime.now())) {
+                throw new BusinessException("拍卖已结束");
+            }
+            if (AuctionPaymentType.DEPOSIT.equals(type)) {
+                return this.createDeposit(user, auction);
+            }
+        }
+
+        UserAddress userAddress = null;
+        if (addressId != null) {
+            userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
+        }
+
+        try {
+
+            auctionActivityService.changeStatus(auctionId, AuctionPaymentType.FIXED_PRICE
+                    .equals(type) ? AuctionStatus.FIXED_PRICE_PURCHASED : AuctionStatus.PURCHASED);
+
+            if (AuctionSource.TRANSFER.equals(auction.getSource())) {
+                Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
+                asset.setStatus(AssetStatus.AUCTION_TRADING);
+                assetRepo.save(asset);
+            }
+
+            BigDecimal price = AuctionPaymentType.FIXED_PRICE.equals(type) ? auction.getFixedPrice() : auction
+                    .getPurchasePrice();
+
+            AuctionOrder order = AuctionOrder.builder()
+                    .id(snowflakeIdWorker.nextId())
+                    .auctionId(auction.getId())
+                    .userId(user.getId())
+                    .nickname(user.getNickname())
+                    .paymentType(type)
+                    .name(auction.getName())
+                    .pic(auction.getPic())
+                    .serviceCharge(auction.getServiceCharge())
+                    .royalties(auction.getRoyalties())
+                    .source(auction.getSource())
+                    .price(price)
+                    .totalPrice(price)
+                    .auctionRecordId(auctionRecordId)
+                    .status(AuctionOrderStatus.NOT_PAID)
+                    .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
+                    .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
+                    .address(Optional.ofNullable(userAddress).map(UserAddress::getDetail).orElse(null))
+                    .build();
+
+            return auctionOrderRepo.save(order);
+        } catch (Exception e) {
+            auctionActivityService.changeStatus(auctionId, AuctionStatus.ONGOING);
+            throw e;
+        }
+    }
+
+
+    public AuctionOrder createDeposit(User user, AuctionActivity auction) {
+        if (user.getId().equals(auction.getSellerId())) {
+            throw new BusinessException("不可自己出价自己的");
+        }
+
+        //保证金
+//        BigDecimal deposit = sysConfigService.getBigDecimal("deposit");
+        AuctionOrder order = auctionOrderRepo
+                .findByUserIdAndAuctionIdAndPaymentTypeAndStatusIn(user.getId(), auction.getId(),
+                        AuctionPaymentType.DEPOSIT, Arrays
+                                .asList(AuctionOrderStatus.NOT_PAID, AuctionOrderStatus.FINISH));
+        if (ObjectUtils.isNotEmpty(order)) {
+            if (AuctionOrderStatus.FINISH.equals(order.getStatus())) {
+                throw new BusinessException("保证金已交过,无需再交");
+            }
+            throw new BusinessException("保证金未支付,取消后再重新出价");
+        }
+
+        AuctionRecord auctionRecord = AuctionRecord.builder()
+                .auctionId(auction.getId())
+                .type(AuctionRecordType.DEPOSIT)
+                .bidderPrice(auction.getDeposit())
+                .auctionPic(null)
+                .userId(SecurityUtils.getAuthenticatedUser().getId())
+                .avatar(SecurityUtils.getAuthenticatedUser().getAvatar())
+                .name(auction.getName())
+                .purchased(false)
+                .auctionType(auction.getAuctionType())
+                .build();
+        AuctionRecord record = auctionRecordRepo.save(auctionRecord);
+
+        order = AuctionOrder.builder()
+                .id(snowflakeIdWorker.nextId())
+                .auctionId(auction.getId())
+                .userId(user.getId())
+                .nickname(user.getNickname())
+                .paymentType(AuctionPaymentType.DEPOSIT)
+                .name(auction.getName())
+                .pic(auction.getPic())
+                .serviceCharge(auction.getServiceCharge())
+                .royalties(auction.getRoyalties())
+                .source(auction.getSource())
+                .price(auction.getDeposit())
+                .totalPrice(auction.getDeposit())
+                .auctionRecordId(record.getId())
+                .status(AuctionOrderStatus.NOT_PAID)
+                .build();
+
+
+        return auctionOrderRepo.save(order);
+
+    }
+
+    @Transient
+    public void notify(Long id, PayMethod payMethod, String transactionId) {
+        AuctionOrder order = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+        if (!order.getStatus().equals(AuctionOrderStatus.NOT_PAID)) {
+            throw new BusinessException("订单已处理");
+        }
+        AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
+                .orElseThrow(new BusinessException("无拍卖活动"));
+
+        if (auction.getAuctionType().equals(AuctionType.ENTITY)) {
+            order.setStatus(AuctionOrderStatus.DELIVERY);
+        } else {
+            order.setStatus(AuctionOrderStatus.FINISH);
+        }
+        order.setPayMethod(payMethod);
+        order.setTransactionId(transactionId);
+        order.setPayTime(LocalDateTime.now());
+        //存订单
+        auctionOrderRepo.save(order);
+
+        if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
+            //改出价记录表
+            AuctionRecord record = auctionRecordRepo.findById(order.getAuctionRecordId())
+                    .orElseThrow(new BusinessException("无出价记录"));
+            record.setPayDeposit(true);
+            auctionRecordRepo.save(record);
+            return;
+        }
+
+        //此拍卖结束
+        auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.FINISH);
+
+        //修改买家和成交价
+        auction.setPurchaserId(order.getUserId());
+        auction.setPurchasePrice(order.getTotalPrice());
+        auctionActivityRepo.save(auction);
+
+        if (AuctionSource.TRANSFER.equals(order.getSource())) {
+            Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
+            if (asset.isPublicShow()) {
+                //取消公开展示
+                assetService.cancelPublic(asset);
+            }
+            User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("无用户"));
+            //转让流程
+            assetService.transfer(asset, order.getTotalPrice(), user, TransferReason.AUCTION, order.getId());
+
+            // 发送短信提醒用户转让成功
+            if (asset.getUserId() != null) {
+                smsService.sellOut(userRepo.findPhoneById(asset.getUserId()));
+            }
+
+            //用户冲余额
+            UserBalance userBalance = userBalanceRepo.findByUserId(asset.getOwnerId())
+                    .orElse(UserBalance.builder()
+                            .balance(BigDecimal.ZERO)
+                            .lastBalance(BigDecimal.ZERO)
+                            .userId(asset.getOwnerId())
+                            .build());
+
+            BigDecimal amount = order.getTotalPrice()
+                    .multiply(BigDecimal.valueOf(100 - order.getRoyalties() - order.getServiceCharge()))
+                    .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
+
+            userBalance.setLastBalance(userBalance.getBalance());
+            userBalance.setBalance(userBalance.getBalance().add(amount));
+            userBalanceRepo.save(userBalance);
+            log.info("拍卖冲用户余额{},¥{}", asset.getOwnerId(), amount);
+
+            balanceRecordRepo.save(BalanceRecord.builder()
+                    .time(LocalDateTime.now())
+                    .userId(asset.getOwnerId())
+                    .orderId(order.getId())
+                    .amount(amount)
+                    .balance(userBalance.getBalance())
+                    .lastBalance(userBalance.getLastBalance())
+                    .type(BalanceType.SELL)
+                    .build());
+
+
+        }
+
+        //改出价记录表为竞得(一口价无出价表)
+        auctionRecordRepo.findById(order.getAuctionRecordId())
+                .ifPresent(record -> {
+                    record.setPurchased(true);
+                    auctionRecordRepo.save(record);
+                });
+
+
+        //退保证金
+        List<AuctionOrder> orders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
+                AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
+        //退款
+        orders.forEach(this::refund);
+    }
+
+    public void cancel(AuctionOrder order) {
+        if (!getOrderLock(order.getId())) {
+            log.error("订单取消失败 {}, redis锁了", order.getId());
+            return;
+        }
+
+        boolean isRefund = false;
+        try {
+            AuctionActivity auction = auctionActivityRepo.findById(order.getAuctionId())
+                    .orElseThrow(new BusinessException("无记录"));
+
+            if (AuctionPaymentType.PURCHASE_PRICE.equals(order.getPaymentType())) {
+                //如果是拍卖,需获取取消订单的时长
+                int time = sysConfigService.getInt("auction_cancel_time");
+                if (LocalDateTime.now().isAfter(auction.getEndTime().plusMinutes(time))) {
+                    //超过支付时长
+                    log.info("取消订单流拍:{}", auction.getId());
+                    auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PASS);
+                    //添加到流拍记录表里
+                    auctionPassRecordRepo.save(AuctionPassRecord.builder()
+                            .auctionId(auction.getId())
+                            .userId(auction.getPurchaserId())
+                            .purchasePrice(auction.getPurchasePrice())
+                            .build());
+                    //流拍不退自己的保证金
+                    isRefund = true;
+
+                    if (AuctionSource.TRANSFER.equals(order.getSource())) {
+                        //改回资产状态
+                        Asset asset = assetRepo.findById(auction.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
+                        asset.setStatus(AssetStatus.NORMAL);
+                        assetRepo.save(asset);
+                    }
+
+                }
+            } else if (AuctionPaymentType.DEPOSIT.equals(order.getPaymentType())) {
+                //删除出价记录
+                auctionRecordRepo.softDelete(order.getAuctionRecordId());
+            } else {
+                //拍卖是否结束
+                if (LocalDateTime.now().isBefore(auction.getEndTime())) {
+                    //返回拍卖状态
+                    auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.ONGOING);
+                } else {
+                    //最后一个出价的人得
+                    auctionActivityService.changeStatus(order.getAuctionId(), AuctionStatus.PURCHASED);
+                }
+            }
+
+            order.setStatus(AuctionOrderStatus.CANCELLED);
+            order.setCancelTime(LocalDateTime.now());
+            auctionOrderRepo.save(order);
+            log.info("取消订单{}", order.getId());
+
+        } catch (Exception e) {
+            log.error("订单取消错误 orderId: " + order.getId(), e);
+        }
+
+        if (isRefund) {
+            //退其余保证金
+            List<AuctionOrder> orders = auctionOrderRepo
+                    .findAllByAuctionIdAndPaymentTypeAndStatus(order.getAuctionId(),
+                            AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
+            //退款
+            orders.stream()
+                    .filter(o -> !order.getUserId().equals(o.getUserId()))
+                    .forEach(this::refund);
+        }
+        releaseOrderLock(order.getId());
+    }
+
+    /**
+     * 退款方法
+     *
+     * @param order 订单
+     */
+    private void refund(AuctionOrder order) {
+        log.info("退款拍卖保证金订单{}", order.getId());
+        PayMethod payMethod = order.getPayMethod();
+        if (PayMethod.ALIPAY == payMethod) {
+            if (StringUtils.length(order.getTransactionId()) == 28) {
+                payMethod = PayMethod.HMPAY;
+            } else if (StringUtils.length(order.getTransactionId()) == 30) {
+                payMethod = PayMethod.SANDPAY;
+            }
+        }
+        try {
+            switch (payMethod) {
+                case HMPAY:
+                    orderPayService.refund(order.getId().toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.HM);
+                    log.info("退款成功{}", order.getId());
+                    break;
+                case SANDPAY:
+                    orderPayService.refund(order.getId().toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.SAND);
+                    log.info("退款成功{}", order.getId());
+                    break;
+                case PAYEASE:
+                    orderPayService.refund(order.getId().toString(), order.getTransactionId(), order.getTotalPrice(), Constants.PayChannel.PE);
+                    log.info("退款成功{}", order.getId());
+                    break;
+            }
+            order.setRefundTime(LocalDateTime.now());
+            order.setStatus(AuctionOrderStatus.REFUNDED);
+            auctionOrderRepo.save(order);
+        } catch (Exception e) {
+            log.error("拍卖保证金订单退款失败 {} ", order.getId(), e);
+            order.setRefundTime(LocalDateTime.now());
+            order.setStatus(AuctionOrderStatus.REFUNDING);
+            auctionOrderRepo.save(order);
+        }
+    }
+
+    public boolean getOrderLock(Long orderId) {
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.AUCTION_ORDER_LOCK + orderId);
+        Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.DAYS);
+        return Boolean.TRUE.equals(flag);
+    }
+
+    public void releaseOrderLock(Long orderId) {
+        redisTemplate.delete(RedisKeys.AUCTION_ORDER_LOCK + orderId);
+    }
+
+    @Scheduled(cron = "0 0/10 * * * ?")
+    public void passOverTimeAuction() {
+        List<AuctionActivity> purchased = auctionActivityRepo.findAllByStatus(AuctionStatus.PURCHASED);
+        if (purchased != null) {
+            int time = sysConfigService.getInt("auction_cancel_time");
+            purchased.forEach(act -> {
+                if (LocalDateTime.now().isAfter(act.getEndTime().plusMinutes(time))) {
+                    List<AuctionOrder> auctionOrders = auctionOrderRepo.findAllByAuctionIdAndPaymentTypeAndStatus(act
+                            .getId(), AuctionPaymentType.PURCHASE_PRICE, AuctionOrderStatus.NOT_PAID);
+//                    if (CollUtil.isNotEmpty(auctionOrders)) {
+                    auctionOrders.forEach(this::cancel);
+//                        return;
+//                    }
+
+                    auctionActivityService.changeStatus(act.getId(), AuctionStatus.PASS);
+                    log.info("拍卖定时任务流拍{}", act.getId());
+
+                    if (AuctionSource.TRANSFER.equals(act.getSource())) {
+                        //改回资产状态
+                        Asset asset = assetRepo.findById(act.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
+                        asset.setStatus(AssetStatus.NORMAL);
+                        assetRepo.save(asset);
+                    }
+
+                    //退其余保证金
+                    List<AuctionOrder> orders = auctionOrderRepo
+                            .findAllByAuctionIdAndPaymentTypeAndStatus(act.getId(),
+                                    AuctionPaymentType.DEPOSIT, AuctionOrderStatus.FINISH);
+                    //退款
+                    orders.stream()
+                            .filter(o -> !act.getPurchaserId().equals(o.getUserId()))
+                            .forEach(this::refund);
+
+                    //添加到流拍记录表里
+                    auctionPassRecordRepo.save(AuctionPassRecord.builder()
+                            .auctionId(act.getId())
+                            .userId(act.getPurchaserId())
+                            .purchasePrice(act.getPurchasePrice())
+                            .build());
+                }
+            });
+        }
+    }
+
+    /**
+     * 发货
+     *
+     * @param id        编号
+     * @param courierId 快递单号
+     */
+    public void dispatch(Long id, String courierId) {
+        AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
+        auctionOrder.setStatus(AuctionOrderStatus.RECEIVE);
+        auctionOrder.setCourierId(courierId);
+        auctionOrderRepo.save(auctionOrder);
+    }
+
+    /**
+     * 订单
+     *
+     * @param id 编号
+     */
+    public void finish(Long id) {
+        AuctionOrder auctionOrder = auctionOrderRepo.findById(id).orElseThrow(new BusinessException("铸造订单不存在"));
+        auctionOrder.setStatus(AuctionOrderStatus.FINISH);
+        auctionOrderRepo.save(auctionOrder);
+    }
+}

+ 209 - 0
src/main/java/com/izouma/nineth/service/AuctionRecordService.java

@@ -0,0 +1,209 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.config.RedisKeys;
+import com.izouma.nineth.domain.*;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.dto.auction.AuctionRecordDTO;
+import com.izouma.nineth.enums.*;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.AuctionActivityRepo;
+import com.izouma.nineth.repo.AuctionOrderRepo;
+import com.izouma.nineth.repo.AuctionRecordRepo;
+import com.izouma.nineth.repo.UserRepo;
+import com.izouma.nineth.utils.JpaUtils;
+import com.izouma.nineth.utils.SecurityUtils;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Service
+@AllArgsConstructor
+public class AuctionRecordService {
+
+    private AuctionRecordRepo             auctionRecordRepo;
+    private AuctionActivityRepo           auctionActivityRepo;
+    private AuctionOrderRepo              auctionOrderRepo;
+    private RedisTemplate<String, Object> redisTemplate;
+    private UserRepo                      userRepo;
+
+    public Page<AuctionRecord> all(PageQuery pageQuery) {
+        return auctionRecordRepo
+                .findAll(JpaUtils.toSpecification(pageQuery, AuctionRecord.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+    public Page<AuctionRecordDTO> recordInfos(PageQuery pageQuery) {
+
+        Page<AuctionRecord> records = auctionRecordRepo
+                .findAll(JpaUtils.toSpecification(pageQuery, AuctionRecord.class), JpaUtils.toPageRequest(pageQuery));
+
+        List<AuctionRecordDTO> auctionRecordDTOS = new ArrayList<>();
+        records.getContent().forEach(record -> {
+            AuctionActivity auctionActivity = auctionActivityRepo.findById(record.getAuctionId())
+                    .orElse(null);
+            if (auctionActivity == null) {
+                return;
+            }
+            AuctionRecordDTO auctionRecordDTO = new AuctionRecordDTO();
+            auctionRecordDTO.setAuctionId(record.getAuctionId());
+            auctionRecordDTO.setAuctionPic(auctionActivity.getPic());
+            auctionRecordDTO.setMinter(auctionActivity.getMinter());
+            auctionRecordDTO.setBidderPrice(record.getBidderPrice());
+            auctionRecordDTO.setActPurchasedId((auctionActivity.getPurchaserId()));
+            auctionRecordDTO.setName(record.getName());
+            auctionRecordDTO.setDeposit(auctionActivity.getDeposit());
+            auctionRecordDTO.setEndTime(auctionActivity.getEndTime());
+            auctionRecordDTO.setCreatedTime(record.getCreatedAt());
+            auctionRecordDTO.setUserId(record.getUserId());
+            auctionRecordDTO.setType(record.getType());
+            auctionRecordDTO.setPurchased(record.isPurchased());
+            auctionRecordDTO.setAuctionStatus(auctionActivity.getStatus());
+            auctionRecordDTO.setAuctionType(auctionActivity.getAuctionType());
+//            if (auctionActivity.getStatus().equals(AuctionStatus.PURCHASED) || auctionActivity.getStatus()
+//                    .equals(AuctionStatus.FINISH)) {
+//                if (auctionActivity.getRecordId() != null) {
+//                    if (auctionActivity.getRecordId().equals(record.getId())) {
+//                        auctionRecordDTO.setPurchased(true);
+//                    }
+//                }
+//            }
+            if (auctionRecordDTO.getType().equals(AuctionRecordType.DEPOSIT) || auctionRecordDTO.isPurchased()) {
+                AuctionOrder auctionOrder = auctionOrderRepo.findFirstByAuctionRecordIdOrderByCreatedAt(record.getId());
+                if (auctionOrder != null) {
+                    auctionRecordDTO.setOrderId(auctionOrder.getId());
+                    auctionRecordDTO.setOrderStatus(auctionOrder.getStatus());
+                }
+            }
+            Set<AuctionOrderStatus> auctionOrderStatuses = new HashSet<>();
+            auctionOrderStatuses.add(AuctionOrderStatus.NOT_PAID);
+            auctionOrderStatuses.add(AuctionOrderStatus.CANCELLED);
+            AuctionOrder depositOrder = auctionOrderRepo.findFirstByAuctionIdAndPaymentTypeAndStatusNotIn(record
+                    .getAuctionId(), AuctionPaymentType.DEPOSIT, auctionOrderStatuses);
+            if (depositOrder != null) {
+                auctionRecordDTO.setDepositStatus(depositOrder.getStatus());
+            } else {
+                auctionRecordDTO.setDepositStatus(AuctionOrderStatus.FINISH);
+            }
+            auctionRecordDTOS.add(auctionRecordDTO);
+        });
+        return new PageImpl<>(auctionRecordDTOS, records.getPageable(), records.getTotalElements());
+    }
+
+    public List<AuctionRecordDTO> userRecord(Long userId, String type) {
+        List<AuctionRecord> records = auctionRecordRepo.findByUserId(userId, type);
+        List<AuctionRecordDTO> auctionRecordDTOS = new ArrayList<>();
+        records.forEach(record -> {
+            AuctionActivity auctionActivity = auctionActivityRepo.findById(record.getAuctionId())
+                    .orElse(null);
+            if (auctionActivity == null) {
+                return;
+            }
+            AuctionRecordDTO auctionRecordDTO = new AuctionRecordDTO();
+            BeanUtils.copyProperties(record, auctionRecordDTO);
+            auctionRecordDTO.setAuctionPic(auctionActivity.getPic());
+            auctionRecordDTO.setMinter(auctionActivity.getMinter());
+            auctionRecordDTO.setAuctionPic(auctionActivity.getPic());
+            auctionRecordDTO.setActPurchasedId((auctionActivity.getPurchaserId()));
+            auctionRecordDTO.setDeposit(auctionActivity.getDeposit());
+            auctionRecordDTO.setEndTime(auctionActivity.getEndTime());
+            auctionRecordDTO.setAuctionType(auctionActivity.getAuctionType());
+            auctionRecordDTO.setAuctionStatus(auctionActivity.getStatus());
+            auctionRecordDTO.setCreatedTime(record.getCreatedAt());
+//            if (auctionRecordDTO.getType().equals(AuctionRecordType.DEPOSIT) || auctionRecordDTO.isPurchased()) {
+            AuctionOrder auctionOrder = auctionOrderRepo.findFirstByAuctionRecordIdOrderByIdDesc(record.getId());
+            if (auctionOrder != null) {
+                auctionRecordDTO.setOrderId(auctionOrder.getId());
+                auctionRecordDTO.setOrderStatus(auctionOrder.getStatus());
+            }
+//            }
+            Set<AuctionOrderStatus> auctionOrderStatuses = new HashSet<>();
+            auctionOrderStatuses.add(AuctionOrderStatus.NOT_PAID);
+            auctionOrderStatuses.add(AuctionOrderStatus.CANCELLED);
+            AuctionOrder depositOrder = auctionOrderRepo.findFirstByAuctionIdAndPaymentTypeAndStatusNotIn(record
+                    .getAuctionId(), AuctionPaymentType.DEPOSIT, auctionOrderStatuses);
+            if (depositOrder != null) {
+                auctionRecordDTO.setDepositStatus(depositOrder.getStatus());
+            } else {
+                auctionRecordDTO.setDepositStatus(AuctionOrderStatus.FINISH);
+            }
+            auctionRecordDTOS.add(auctionRecordDTO);
+        });
+        return auctionRecordDTOS;
+    }
+
+    public AuctionRecord create(Long userId, Long auctionId, BigDecimal amount) {
+        AuctionActivity auction = auctionActivityRepo.findById(auctionId)
+                .orElseThrow(new BusinessException("暂无"));
+
+        String status = (String) redisTemplate.opsForValue().get(RedisKeys.AUCTION_STATUS + auctionId);
+        if (status == null)
+            status = auction.getStatus().toString();
+        switch (AuctionStatus.valueOf(status)) {
+            case NOTSTARTED:
+                throw new BusinessException("拍卖还未开始");
+            case PURCHASED:
+                throw new BusinessException("拍卖成交中");
+            case PASS:
+            case FINISH:
+                throw new BusinessException("拍卖已结束");
+        }
+
+        AuctionRecord record = auctionRecordRepo.findTopByAuctionIdAndUserIdOrderByIdDesc(auctionId, userId);
+        if (ObjectUtils.isEmpty(record)) {
+            User user = userRepo.findById(userId).orElseThrow(new BusinessException("无用户"));
+
+            return auctionRecordRepo.save(AuctionRecord.builder()
+                    .auctionId(auctionId)
+                    .userId(userId)
+                    .avatar(user.getAvatar())
+                    .user(user.getNickname())
+                    .name(auction.getName())
+                    .auctionPic(null)
+                    .bidderPrice(amount)
+                    .purchased(false)
+                    .type(AuctionRecordType.DEPOSIT)
+                    .payDeposit(false)
+                    .auctionType(auction.getAuctionType())
+                    .build());
+        }
+
+        if (auction.getPurchasePrice() != null) {
+            if (auction.getPurchasePrice().compareTo(amount) >= 0) {
+                throw new BusinessException("出价低于当前竞拍价");
+            }
+        } else if (auction.getStartingPrice().compareTo(amount) >= 0) {
+
+            throw new BusinessException("出价低于当前起拍价");
+        }
+        if (SecurityUtils.getAuthenticatedUser().getId().equals(auction.getPurchaserId())) {
+            throw new BusinessException("请勿重复竞价");
+        }
+        AuctionRecord save = new AuctionRecord();
+        BeanUtils.copyProperties(record, save);
+        save.setId(null);
+        save.setUser(SecurityUtils.getAuthenticatedUser().getNickname());
+        save.setBidderPrice(amount);
+        save.setType(AuctionRecordType.BIDDER);
+        save = auctionRecordRepo.save(save);
+
+        if (record.isPayDeposit()) {
+            //保证金支付过,改成交价
+            auction.setPurchasePrice(amount);
+            auction.setPurchaser(SecurityUtils.getAuthenticatedUser().getNickname());
+            auction.setPurchaserId(SecurityUtils.getAuthenticatedUser().getId());
+            auction.setRecordId(save.getId());
+            auction.setBids(auction.getBids() + 1);
+            auctionActivityRepo.save(auction);
+        }
+        return save;
+    }
+}

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

@@ -112,4 +112,9 @@ public class CacheService {
     @CacheEvict(value = "sysConfigGet", allEntries = true)
     public void clearSysConfigGet() {
     }
+
+    @CacheEvict(value = "auction", key = "#id")
+    public void clearAuction(Long id) {
+    }
+
 }

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

@@ -32,7 +32,7 @@ public class CaptchaService {
 
         // 算术类型
         ArithmeticCaptcha captcha = new ArithmeticCaptcha(150, 48);
-        captcha.setFont(com.pig4cloud.captcha.base.Captcha.FONT_7, 15 * 2);
+        captcha.setFont(6, 15 * 2);
         captcha.setLen(2);  // 几位数运算,默认是两位
         captcha.getArithmeticString();  // 获取运算的公式:3+2=?
         captcha.supportAlgorithmSign(2); // 可设置支持的算法:2 表示只生成带加减法的公式

+ 37 - 3
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -22,6 +22,7 @@ import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.RandomUtils;
 import org.apache.commons.lang3.Range;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.client.consumer.LitePullConsumer;
 import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.beans.BeanUtils;
 import org.springframework.cache.annotation.Cacheable;
@@ -75,7 +76,8 @@ public class CollectionService {
         if (Arrays.asList(env.getActiveProfiles()).contains("dev")) {
             return;
         }
-        List<Collection> collections = collectionRepo.findByScheduleSaleTrueAndOnShelfFalseAndStartTimeBeforeAndDelFalse(LocalDateTime.now());
+        List<Collection> collections = collectionRepo
+                .findByScheduleSaleTrueAndOnShelfFalseAndStartTimeBeforeAndDelFalse(LocalDateTime.now());
         for (Collection collection : collections) {
             onShelfTask(collection);
         }
@@ -235,7 +237,9 @@ public class CollectionService {
                     collectionDTO.setAppointment(appointmentRepo.findFirstByBlindBoxId(collection.getId()).isPresent());
                 }
                 if (showVip && collection.getAssignment() > 0 && user.getVipPurchase() > 0) {
-                    int purchase = orderRepo.countByUserIdAndCollectionIdAndVipTrueAndStatusIn(user.getId(), collection.getId(), Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
+                    int purchase = orderRepo
+                            .countByUserIdAndCollectionIdAndVipTrueAndStatusIn(user.getId(), collection.getId(), Arrays
+                                    .asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING));
                     collectionDTO.setVipSurplus(user.getVipPurchase() - purchase);
                 }
             }
@@ -294,7 +298,8 @@ public class CollectionService {
                 collection.setLiked(likes.stream().anyMatch(l -> l.getCollectionId().equals(collection.getId())));
             }
             if (!appointments.isEmpty()) {
-                collection.setAppointment(appointments.stream().anyMatch(a -> a.getBlindBoxId().equals(collection.getId())));
+                collection.setAppointment(appointments.stream()
+                        .anyMatch(a -> a.getBlindBoxId().equals(collection.getId())));
             }
         });
     }
@@ -340,6 +345,7 @@ public class CollectionService {
             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());
@@ -615,4 +621,32 @@ public class CollectionService {
 
         return dtos;
     }
+
+    public List<Collection> setOasisScancode(List<Long> oasisIds) {
+        List<Collection> collections = collectionRepo
+                .findAllByOasisIdInAndSourceAndSaleLessThan(oasisIds, CollectionSource.COMPANY, 1);
+        List<Collection> result = new ArrayList<>();
+        collections.forEach(collection -> {
+            collection.setOnShelf(false);
+            collection.setScanCode(true);
+            collection.setSalable(true);
+            collectionRepo.save(collection);
+            result.add(collection);
+        });
+        return result;
+    }
+
+    public List<Collection> setOasisOnShelf(List<Long> oasisIds) {
+        List<Collection> collections = collectionRepo
+                .findAllByOasisIdInAndSourceAndSaleLessThan(oasisIds, CollectionSource.COMPANY, 1);
+        List<Collection> result = new ArrayList<>();
+        collections.forEach(collection -> {
+            collection.setOnShelf(true);
+            collection.setScanCode(false);
+            collection.setSalable(true);
+            collectionRepo.save(collection);
+            result.add(collection);
+        });
+        return result;
+    }
 }

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

@@ -1,7 +1,11 @@
 package com.izouma.nineth.service;
 
+import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.dto.oasis.OasisDistrictCollectionDTO;
+import com.izouma.nineth.dto.oasis.OasisDistrictDTO;
+import com.izouma.nineth.enums.AssetStatus;
 import com.izouma.nineth.enums.CollectionSource;
 import com.izouma.nineth.enums.CollectionStatus;
 import com.izouma.nineth.enums.CollectionType;
@@ -14,6 +18,11 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 @Service
 @AllArgsConstructor
 public class CompanyCollectionService {
@@ -23,9 +32,12 @@ public class CompanyCollectionService {
     private UserRepo                userRepo;
     private CollectionPrivilegeRepo collectionPrivilegeRepo;
     private SysConfigService        sysConfigService;
+    private AssetRepo               assetRepo;
+    private GeneralProperties       generalProperties;
 
     public Page<CompanyCollection> all(PageQuery pageQuery) {
-        return companyCollectionRepo.findAll(JpaUtils.toSpecification(pageQuery, CompanyCollection.class), JpaUtils.toPageRequest(pageQuery));
+        return companyCollectionRepo.findAll(JpaUtils.toSpecification(pageQuery, CompanyCollection.class), JpaUtils
+                .toPageRequest(pageQuery));
     }
 
     public void audit(Long id, CollectionStatus status, String reason) {
@@ -85,4 +97,97 @@ public class CompanyCollectionService {
         companyCollectionRepo.save(companyCollection);
     }
 
+
+    public List<OasisDistrictCollectionDTO> oasisSearch(List<Long> oasisIds) {
+        List<OasisDistrictCollectionDTO> oasisDistrictCollectionDTOS = new ArrayList<>();
+        List<AssetStatus> assetStatuses = new ArrayList<>();
+        assetStatuses.add(AssetStatus.NORMAL);
+        assetStatuses.add(AssetStatus.AUCTION_TRADING);
+        assetStatuses.add(AssetStatus.GIFTING);
+        assetStatuses.add(AssetStatus.TRADING);
+        assetStatuses.add(AssetStatus.MINTING);
+        assetStatuses.add(AssetStatus.AUCTIONING);
+        List<Asset> assets = assetRepo
+                .findAllByOasisIdInAndStatusIn(oasisIds, assetStatuses);
+        List<CompanyCollection> companyCollections = companyCollectionRepo
+                .findAllByOasisIdInAndStatusNot(oasisIds, CollectionStatus.SUCCESS);
+        List<Collection> officialCollections = collectionRepo
+                .findAllByOasisIdInAndSourceAndSaleLessThan(oasisIds, CollectionSource.COMPANY, 1);
+
+        assets.forEach(asset -> {
+            OasisDistrictCollectionDTO oasisDistrictCollectionDTO = new OasisDistrictCollectionDTO();
+            oasisDistrictCollectionDTO.setOasisId(asset.getOasisId());
+            oasisDistrictCollectionDTO.setCollectionName(asset.getName());
+            oasisDistrictCollectionDTO.setOriginPrice(asset.getPrice());
+            oasisDistrictCollectionDTO.setScanOnly(false);
+            oasisDistrictCollectionDTO.setOwnerId(asset.getOwnerId());
+            oasisDistrictCollectionDTO.setOwnerName(asset.getOwner());
+            oasisDistrictCollectionDTO.setOwnerAvatar(asset.getOwnerAvatar());
+            oasisDistrictCollectionDTO.setCollectionId(asset.getId());
+            if (!asset.getStatus().equals(AssetStatus.NORMAL)) {
+                oasisDistrictCollectionDTO.setStatus(asset.getStatus().getDescription());
+            } else {
+                if (asset.isPublicShow()) {
+                    Collection collection = collectionRepo.findFirstByOnShelfAndAssetId(true, asset.getId());
+                    oasisDistrictCollectionDTO.setUrl(
+                            generalProperties.getHost() + "/9th/productDetail/" + collection
+                                    .getId() + "?id=" + collection.getId());
+                    if (asset.isConsignment()) {
+                        oasisDistrictCollectionDTO.setStatus("寄售中");
+                    } else {
+                        oasisDistrictCollectionDTO.setStatus("仅展示");
+                    }
+                } else {
+                    oasisDistrictCollectionDTO.setStatus("未展示");
+                }
+            }
+            oasisDistrictCollectionDTO.setSource("二手");
+            oasisDistrictCollectionDTOS.add(oasisDistrictCollectionDTO);
+        });
+
+        officialCollections.forEach(collection -> {
+            OasisDistrictCollectionDTO oasisDistrictCollectionDTO = new OasisDistrictCollectionDTO();
+            oasisDistrictCollectionDTO.setOasisId(collection.getOasisId());
+            oasisDistrictCollectionDTO.setCollectionName(collection.getName());
+            oasisDistrictCollectionDTO.setOriginPrice(collection.getPrice());
+            oasisDistrictCollectionDTO.setSalable(collection.isSalable());
+            oasisDistrictCollectionDTO.setOwnerId(collection.getOwnerId());
+            oasisDistrictCollectionDTO.setOwnerName(collection.getOwner());
+            oasisDistrictCollectionDTO.setOwnerAvatar(collection.getOwnerAvatar());
+            oasisDistrictCollectionDTO.setCollectionId(collection.getId());
+            oasisDistrictCollectionDTO.setScanOnly(collection.isScanCode());
+            oasisDistrictCollectionDTO.setSource("官方");
+            if (collection.isOnShelf()) {
+                oasisDistrictCollectionDTO.setUrl(
+                        generalProperties.getHost() + "/9th/productDetail/" + collection
+                                .getId() + "?id=" + collection.getId());
+                if (collection.isSalable()) {
+                    oasisDistrictCollectionDTO.setStatus("在售");
+                    if (collection.getStock() == 0) {
+                        oasisDistrictCollectionDTO.setStatus("已有订单,订单处理中");
+                    }
+                } else {
+                    oasisDistrictCollectionDTO.setStatus("仅展示");
+                }
+            } else {
+                oasisDistrictCollectionDTO.setStatus("未上架");
+            }
+            oasisDistrictCollectionDTOS.add(oasisDistrictCollectionDTO);
+        });
+
+        companyCollections.forEach(companyCollection -> {
+            OasisDistrictCollectionDTO oasisDistrictCollectionDTO = new OasisDistrictCollectionDTO();
+            oasisDistrictCollectionDTO.setOasisId(companyCollection.getOasisId());
+            oasisDistrictCollectionDTO.setCollectionName(companyCollection.getName());
+            oasisDistrictCollectionDTO.setScanOnly(false);
+            oasisDistrictCollectionDTO.setOriginPrice(companyCollection.getPrice());
+            oasisDistrictCollectionDTO.setOwnerName(companyCollection.getMinter());
+            oasisDistrictCollectionDTO.setCollectionId(companyCollection.getId());
+            oasisDistrictCollectionDTO.setSalable(false);
+            oasisDistrictCollectionDTO.setStatus("审核中");
+            oasisDistrictCollectionDTO.setSource("官方");
+            oasisDistrictCollectionDTOS.add(oasisDistrictCollectionDTO);
+        });
+        return oasisDistrictCollectionDTOS;
+    }
 }

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

@@ -347,9 +347,7 @@ public class MintOrderService {
                     .mintActivityId(mintActivityId)
                     .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
                     .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
-                    .address(Optional.ofNullable(userAddress).map(u ->
-                                    u.getProvinceName() + " " + u.getCityName() + " " + u.getDistrictName() + " " + u.getAddress())
-                            .orElse(null))
+                    .address(Optional.ofNullable(userAddress).map(UserAddress::getDetail).orElse(null))
                     .build());
 
             // 铸造资产

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

@@ -2,6 +2,7 @@ package com.izouma.nineth.service;
 
 import com.izouma.nineth.domain.NewsLike;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.repo.AuctionActivityRepo;
 import com.izouma.nineth.repo.NewsLikeRepo;
 import com.izouma.nineth.repo.NewsRepo;
 import com.izouma.nineth.repo.ShowroomRepo;
@@ -16,12 +17,14 @@ import java.util.List;
 @AllArgsConstructor
 public class NewsLikeService {
 
-    private NewsLikeRepo newsLikeRepo;
-    private NewsRepo     newsRepo;
-    private ShowroomRepo showroomRepo;
+    private NewsLikeRepo        newsLikeRepo;
+    private NewsRepo            newsRepo;
+    private ShowroomRepo        showroomRepo;
+    private AuctionActivityRepo auctionActivityRepo;
 
     public Page<NewsLike> all(PageQuery pageQuery) {
-        return newsLikeRepo.findAll(JpaUtils.toSpecification(pageQuery, NewsLike.class), JpaUtils.toPageRequest(pageQuery));
+        return newsLikeRepo
+                .findAll(JpaUtils.toSpecification(pageQuery, NewsLike.class), JpaUtils.toPageRequest(pageQuery));
     }
 
     public void like(Long userId, Long newsId) {
@@ -59,4 +62,22 @@ public class NewsLikeService {
             showroomRepo.addLike(roomId, -list.size());
         }
     }
+
+    public void likeAuction(Long userId, Long auctionId) {
+        List<NewsLike> list = newsLikeRepo.findByUserIdAndAuctionId(userId, auctionId);
+        if (!list.isEmpty()) return;
+        newsLikeRepo.save(NewsLike.builder()
+                .userId(userId)
+                .auctionId(auctionId)
+                .build());
+        auctionActivityRepo.addLike(auctionId, 1);
+    }
+
+    public void unlikeAuction(Long userId, Long auctionId) {
+        List<NewsLike> list = newsLikeRepo.findByUserIdAndAuctionId(userId, auctionId);
+        if (!list.isEmpty()) {
+            newsLikeRepo.deleteAll(list);
+            auctionActivityRepo.addLike(auctionId, -list.size());
+        }
+    }
 }

+ 20 - 14
src/main/java/com/izouma/nineth/service/OrderCancelService.java

@@ -1,21 +1,11 @@
 package com.izouma.nineth.service;
 
-import com.izouma.nineth.annotations.RedisLock;
 import com.izouma.nineth.config.GeneralProperties;
-import com.izouma.nineth.config.RedisKeys;
-import com.izouma.nineth.domain.GiftOrder;
-import com.izouma.nineth.domain.MintOrder;
 import com.izouma.nineth.domain.Order;
 import com.izouma.nineth.domain.SysConfig;
-import com.izouma.nineth.dto.PayQuery;
-import com.izouma.nineth.enums.MintOrderStatus;
 import com.izouma.nineth.enums.OrderStatus;
-import com.izouma.nineth.enums.PayStatus;
 import com.izouma.nineth.exception.BusinessException;
-import com.izouma.nineth.repo.GiftOrderRepo;
-import com.izouma.nineth.repo.MintOrderRepo;
-import com.izouma.nineth.repo.OrderRepo;
-import com.izouma.nineth.repo.SysConfigRepo;
+import com.izouma.nineth.repo.*;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -25,10 +15,7 @@ import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
 import java.time.LocalDateTime;
-import java.util.Arrays;
 import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
 @Service
 @ConditionalOnProperty(value = "general.notify-server", havingValue = "true")
@@ -45,6 +32,8 @@ public class OrderCancelService {
     private final GiftOrderService              giftOrderService;
     private final OrderPayService               orderPayService;
     private final RedisTemplate<String, Object> redisTemplate;
+    private final AuctionOrderRepo              auctionOrderRepo;
+    private final AuctionOrderService           auctionOrderService;
 
     private static int orderCancelInterval = 210;
 
@@ -136,4 +125,21 @@ public class OrderCancelService {
         }
         return true;
     }
+
+    @Scheduled(fixedRate = 30000)
+    public void batchCancelledAuctionOrder() {
+        List<AuctionOrder> orders = auctionOrderRepo
+                .findByStatusAndCreatedAtBeforeAndDelFalse(AuctionOrderStatus.NOT_PAID,
+                        LocalDateTime.now().minusSeconds(orderCancelInterval));
+        orders.parallelStream().forEach(o -> {
+            try {
+                AuctionOrder order = auctionOrderRepo.findById(o.getId()).orElseThrow(new BusinessException("订单不存在"));
+                if (order.getStatus() == AuctionOrderStatus.NOT_PAID) {
+                    auctionOrderService.cancel(order);
+                }
+            } catch (Exception e) {
+                log.error("取消拍卖订单错误 " + o.getId(), e);
+            }
+        });
+    }
 }

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

@@ -6,6 +6,7 @@ import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PayQuery;
 import com.izouma.nineth.dto.UserBankCard;
+import com.izouma.nineth.enums.AuctionOrderStatus;
 import com.izouma.nineth.enums.MintOrderStatus;
 import com.izouma.nineth.enums.OrderStatus;
 import com.izouma.nineth.enums.PayMethod;
@@ -34,24 +35,26 @@ import java.util.stream.Stream;
 public class OrderPayService {
     private static String PAY_CHANNEL = Constants.PayChannel.SAND;
 
-    private final OrderService       orderService;
-    private final OrderRepo          orderRepo;
-    private final MintOrderRepo      mintOrderRepo;
-    private final GiftOrderRepo      giftOrderRepo;
-    private final SandPayService     sandPayService;
-    private final HMPayService       hmPayService;
-    private final GeneralProperties  generalProperties;
-    private final UserBalanceService userBalanceService;
-    private final RocketMQTemplate   rocketMQTemplate;
-    private final GiftOrderService   giftOrderService;
-    private final MintOrderService   mintOrderService;
-    private final UserRepo           userRepo;
-    private final SnowflakeIdWorker  snowflakeIdWorker;
-    private final RechargeOrderRepo  rechargeOrderRepo;
-    private final SysConfigService   sysConfigService;
-    private final PasswordEncoder    passwordEncoder;
-    private final PayEaseService     payEaseService;
-    private final UserBankCardRepo   userBankCardRepo;
+    private final OrderService        orderService;
+    private final OrderRepo           orderRepo;
+    private final MintOrderRepo       mintOrderRepo;
+    private final GiftOrderRepo       giftOrderRepo;
+    private final SandPayService      sandPayService;
+    private final HMPayService        hmPayService;
+    private final GeneralProperties   generalProperties;
+    private final UserBalanceService  userBalanceService;
+    private final RocketMQTemplate    rocketMQTemplate;
+    private final GiftOrderService    giftOrderService;
+    private final MintOrderService    mintOrderService;
+    private final UserRepo            userRepo;
+    private final SnowflakeIdWorker   snowflakeIdWorker;
+    private final RechargeOrderRepo   rechargeOrderRepo;
+    private final SysConfigService    sysConfigService;
+    private final PasswordEncoder     passwordEncoder;
+    private final PayEaseService      payEaseService;
+    private final UserBankCardRepo    userBankCardRepo;
+    private final AuctionOrderRepo    auctionOrderRepo;
+    private final AuctionOrderService auctionOrderService;
 
     public static void setPayChannel(String payChannel) {
         log.info("set pay channel {}", payChannel);
@@ -389,4 +392,65 @@ public class OrderPayService {
         }
         return Optional.ofNullable(query).orElse(PayQuery.builder().exist(false).build());
     }
+
+    @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
+    public String payAuctionOrder(Long orderId) {
+        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        switch (PAY_CHANNEL) {
+            case Constants.PayChannel.SAND:
+                return sandPayService.pay(orderId + "", "拍卖:" + order.getName(), order.getTotalPrice(),
+                        order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.AUCTION);
+            case Constants.PayChannel.HM:
+                return hmPayService.requestAlipay(orderId + "", order.getTotalPrice(),
+                        "拍卖:" + order.getName(), HMPayService.getTimeout(order.getCreatedAt(), 180),
+                        Constants.OrderNotifyType.AUCTION, generalProperties.getHost() + "/9th/home");
+        }
+        throw new BusinessException("绿洲宇宙冷却系统已启动,请稍后支付");
+    }
+
+    @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
+    public String payAuctionQuick(Long orderId) {
+        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        return sandPayService.payQuick(orderId + "", "拍卖:" + order.getName(),
+                order.getTotalPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.AUCTION,
+                generalProperties.getHost() + "/9th/home");
+    }
+
+    public void payAuctionOrderBalance(Long orderId, Long userId, String tradeCode) {
+        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        if (!Objects.equals(order.getUserId(), userId)) {
+            throw new BusinessException("订单不属于该用户");
+        }
+        if (!Objects.equals(userRepo.findTradeCode(userId), tradeCode)) {
+            throw new BusinessException("交易码错误");
+        }
+        BalanceRecord record = userBalanceService.balancePay(order.getUserId(), order.getTotalPrice(), orderId, "拍卖");
+        auctionOrderService.notify(orderId, PayMethod.BALANCE, record.getId().toString());
+    }
+
+    @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
+    public Map<String, Object> payAuctionOrderAgreement(Long orderId, String bindCardId) {
+        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        if (StringUtils.isEmpty(bindCardId)) {
+            bindCardId = userBankCardRepo.findByUserId(order.getUserId())
+                    .stream().map(UserBankCard::getBindCardId).findFirst().orElse(null);
+        }
+        if (StringUtils.isEmpty(bindCardId)) {
+            throw new BusinessException("请先绑定银行卡");
+        }
+        return payEaseService.pay("拍卖:" + order.getAuctionId(), orderId.toString(), order.getTotalPrice(),
+                order.getUserId().toString(), bindCardId, Constants.OrderNotifyType.AUCTION);
+    }
 }

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

@@ -118,7 +118,7 @@ public class OrderService {
                         SmsService smsService, ErrorOrderRepo errorOrderRepo, ShowCollectionRepo showCollectionRepo,
                         ShowroomService showroomService, CollectionPrivilegeRepo collectionPrivilegeRepo,
                         UserBankCardRepo userBankCardRepo, CacheService cacheService, UserPropertyRepo userPropertyRepo,
-                        UserBalanceService userBalanceService, ProxyManager<String> proxyManager) {
+                        UserBalanceService userBalanceService, ProxyManager<String> proxyManager,) {
         this.orderRepo = orderRepo;
         this.collectionRepo = collectionRepo;
         this.userAddressRepo = userAddressRepo;
@@ -716,7 +716,6 @@ public class OrderService {
                             smsService.sellOut(userRepo.findPhoneById(asset.getUserId()));
                         }
 
-                        userBalanceService.realtimeSettleOrder(order);
                     } else {
                         orderRepo.save(order);
                         //藏品其他信息/是否vip

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

@@ -9,6 +9,7 @@ import com.izouma.nineth.config.SandPayProperties;
 import com.izouma.nineth.dto.PayQuery;
 import com.izouma.nineth.enums.PayStatus;
 import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.AuctionOrderRepo;
 import com.izouma.nineth.repo.GiftOrderRepo;
 import com.izouma.nineth.repo.MintOrderRepo;
 import com.izouma.nineth.repo.OrderRepo;
@@ -18,7 +19,6 @@ import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.lang3.StringUtils;
-import org.springframework.data.redis.core.BoundSetOperations;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
@@ -35,7 +35,6 @@ import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
-import java.util.concurrent.TimeUnit;
 
 @Service
 @AllArgsConstructor
@@ -47,6 +46,7 @@ public class SandPayService {
     private final MintOrderRepo                 mintOrderRepo;
     private final SnowflakeIdWorker             snowflakeIdWorker;
     private final GeneralProperties             generalProperties;
+    private final AuctionOrderRepo              auctionOrderRepo;
     private final RedisTemplate<String, Object> redisTemplate;
 
     public static String paddingOrderId(String orderId) {

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

@@ -3,6 +3,7 @@ package com.izouma.nineth.service;
 import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
+import cn.hutool.core.collection.CollUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.fasterxml.jackson.core.sym.NameN;
 import com.huifu.adapay.core.exception.BaseAdaPayException;
@@ -13,6 +14,8 @@ import com.izouma.nineth.config.RedisKeys;
 import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.*;
+import com.izouma.nineth.dto.oasis.OasisLoginDTO;
+import com.izouma.nineth.enums.AssetStatus;
 import com.izouma.nineth.enums.AuthStatus;
 import com.izouma.nineth.enums.AuthorityName;
 import com.izouma.nineth.event.AccountCreatedEvent;
@@ -50,10 +53,12 @@ import org.springframework.data.domain.Sort;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
 
+import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
 import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
@@ -98,6 +103,8 @@ public class UserService {
     private UserBalanceRepo               userBalanceRepo;
     private ContentAuditService           contentAuditService;
     private OrderRepo                     orderRepo;
+    private AuctionPassRecordRepo         auctionPassRecordRepo;
+    private AssetRepo                     assetRepo;
 
     public User update(User user) {
         if (!SecurityUtils.hasRole(AuthorityName.ROLE_ADMIN)) {
@@ -911,4 +918,48 @@ public class UserService {
         user.setWalletEnabled(true);
         save(user);
     }
+
+    public Map<String, Object> oasisInfo(Long userId) {
+        Map<String, Object> map = new HashMap<>();
+        User user = userRepo.findById(userId).orElseThrow(new BusinessException("未找到用户信息"));
+        map.put("nickName", user.getNickname());
+        List<Asset> assets = assetRepo.findAllByOwnerIdAndStatusAndOasisIdNotNull(userId, AssetStatus.NORMAL);
+        List<OasisLoginDTO> oasisLoginDTOS = new ArrayList<>();
+        assets.forEach(asset -> {
+            OasisLoginDTO oasisLoginDTO = new OasisLoginDTO();
+            oasisLoginDTO.setOasisId(asset.getOasisId());
+            oasisLoginDTO.setAssetId(asset.getId());
+            oasisLoginDTO.setSource(asset.getStatus().getDescription());
+            Collection collection = collectionRepo.findFirstByOnShelfAndAssetId(true, asset.getId());
+            if (collection != null) {
+                oasisLoginDTO.setUrl(generalProperties.getHost() + "/9th/productDetail/" + collection
+                        .getId() + "?id=" + collection.getId());
+            } else {
+                oasisLoginDTO.setUrl("未公开展示");
+            }
+            oasisLoginDTOS.add(oasisLoginDTO);
+        });
+        map.put("oasisInfo", oasisLoginDTOS);
+        map.put("avatar", SecurityUtils.getAuthenticatedUser().getAvatar());
+        return map;
+    }
+
+    /**
+     * 流拍5次直接删号处罚
+     */
+    @Scheduled(cron = "0 0/10 * * * ?")
+    public void delUser() {
+        List<Long> userIds = auctionPassRecordRepo.checkUserId();
+        if (CollUtil.isNotEmpty(userIds)) {
+            log.info("流拍处罚:{}", userIds);
+            userRepo.softDeleteIn(userIds);
+            //清缓存
+            userIds.forEach(id -> {
+                cacheService.clearUserMy(id);
+                cacheService.clearUser(id);
+            });
+
+        }
+
+    }
 }

+ 77 - 0
src/main/java/com/izouma/nineth/web/AuctionActivityController.java

@@ -0,0 +1,77 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.AuctionActivity;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.dto.auction.AuctionInputDTO;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.AuctionActivityRepo;
+import com.izouma.nineth.repo.NewsLikeRepo;
+import com.izouma.nineth.service.AuctionActivityService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.SecurityUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/auctionActivity")
+@AllArgsConstructor
+public class AuctionActivityController extends BaseController {
+    private AuctionActivityService auctionActivityService;
+    private AuctionActivityRepo    auctionActivityRepo;
+    private NewsLikeRepo           newsLikeRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public AuctionActivity save(@RequestBody AuctionActivity record, @RequestParam(required = false) String code) {
+        if (record.getId() != null) {
+            AuctionActivity orig = auctionActivityRepo.findById(record.getId())
+                    .orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return auctionActivityService.save(orig, code);
+        }
+        return auctionActivityService.save(record, code);
+    }
+
+    @PostMapping("/create")
+    public AuctionActivity createFromAsset(@RequestBody AuctionInputDTO dto) {
+        return auctionActivityService.createFromAsset(dto);
+    }
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<AuctionActivity> all(@RequestBody PageQuery pageQuery) {
+        return auctionActivityService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public AuctionActivity get(@PathVariable Long id) {
+        AuctionActivity auctionActivity = auctionActivityRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+        if (SecurityUtils.getAuthenticatedUser() != null) {
+            if (newsLikeRepo.findByUserIdAndAuctionId(SecurityUtils.getAuthenticatedUser().getId(), id).size() > 0) {
+                auctionActivity.setLiked(true);
+                return auctionActivity;
+            }
+        }
+        auctionActivity.setLiked(false);
+        return auctionActivity;
+    }
+
+//    @PostMapping("/del/{id}")
+//    public void del(@PathVariable Long id) {
+//        auctionActivityRepo.softDelete(id);
+//    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<AuctionActivity> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

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

@@ -0,0 +1,124 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.AuctionOrder;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.AuctionPaymentType;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.AuctionOrderRepo;
+import com.izouma.nineth.service.AuctionOrderService;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/auctionOrder")
+@AllArgsConstructor
+public class AuctionOrderController extends BaseController {
+    private AuctionOrderService auctionOrderService;
+    private AuctionOrderRepo    auctionOrderRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public AuctionOrder save(@RequestBody AuctionOrder record) {
+        if (record.getId() != null) {
+            AuctionOrder orig = auctionOrderRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return auctionOrderRepo.save(orig);
+        }
+        return auctionOrderRepo.save(record);
+
+
+    }
+
+    @PostMapping("/create")
+    @ApiModelProperty(value = "创建拍卖订单(支付)")
+    public AuctionOrder create(Long userId, @RequestParam(required = false) Long auctionId,
+                               Long addressId, Long auctionRecordId, AuctionPaymentType type) {
+        return auctionOrderService.create(userId, auctionId, addressId, auctionRecordId, type);
+    }
+
+    @PostMapping("/createFixPrice")
+    @ApiModelProperty(value = "创建拍卖订单(一口价)")
+    public AuctionOrder createFixPrice(Long userId, @RequestParam(required = false) Long auctionId,
+                                       Long addressId, AuctionPaymentType type) {
+//        AuctionActivity auctionActivity = auctionActivityRepo.findById(auctionId)
+//                .orElseThrow(new BusinessException("暂无"));
+//        AuctionRecord auctionRecord = AuctionRecord.builder()
+//                .auctionId(auctionId)
+//                .type(AuctionRecordType.FIXEDPRICE)
+//                .bidderPrice(auctionActivity.getFixedPrice())
+//                .auctionPic(null)
+//                .userId(SecurityUtils.getAuthenticatedUser().getId())
+//                .avatar(SecurityUtils.getAuthenticatedUser().getAvatar())
+//                .name(auctionActivity.getName())
+//                .purchased(false)
+//                .build();
+//        AuctionRecord record = auctionRecordRepo.save(auctionRecord);
+        return auctionOrderService.create(userId, auctionId, addressId, null, type);
+    }
+
+    @PostMapping("/createDeposit")
+    @ApiModelProperty(value = "创建拍卖订单(保证金)")
+    public AuctionOrder createDeposit(Long userId, @RequestParam(required = false) Long auctionId,
+                                      Long addressId, AuctionPaymentType type) {
+
+//        AuctionActivity auctionActivity = auctionActivityRepo.findById(auctionId)
+//                .orElseThrow(new BusinessException("暂无"));
+//        AuctionRecord auctionRecord = AuctionRecord.builder()
+//                .auctionId(auctionId)
+//                .type(AuctionRecordType.DEPOSIT)
+//                .bidderPrice(auctionActivity.getDeposit())
+//                .auctionPic(null)
+//                .userId(SecurityUtils.getAuthenticatedUser().getId())
+//                .avatar(SecurityUtils.getAuthenticatedUser().getAvatar())
+//                .name(auctionActivity.getName())
+//                .purchased(false)
+//                .build();
+//        AuctionRecord record = auctionRecordRepo.save(auctionRecord);
+        return auctionOrderService.create(userId, auctionId, addressId, null, type);
+    }
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<AuctionOrder> all(@RequestBody PageQuery pageQuery) {
+        return auctionOrderService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public AuctionOrder get(@PathVariable Long id) {
+        return auctionOrderRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        auctionOrderRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<AuctionOrder> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+
+    @ApiOperation("订单完成")
+    @GetMapping("/finish/{id}")
+    public void finish(@PathVariable Long id) {
+        auctionOrderService.finish(id);
+    }
+
+    @ApiOperation("订单发货")
+    @GetMapping("/delivery")
+    public void delivery(@RequestParam Long id, @RequestParam String courierId) {
+        auctionOrderService.dispatch(id, courierId);
+    }
+}
+

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

@@ -0,0 +1,86 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.AuctionRecord;
+import com.izouma.nineth.dto.auction.AuctionRecordDTO;
+import com.izouma.nineth.service.AuctionRecordService;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.AuctionRecordRepo;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.SecurityUtils;
+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.math.BigDecimal;
+import java.util.List;
+
+@RestController
+@RequestMapping("/auctionRecord")
+@AllArgsConstructor
+public class AuctionRecordController extends BaseController {
+    private AuctionRecordService auctionRecordService;
+    private AuctionRecordRepo    auctionRecordRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public AuctionRecord save(@RequestBody AuctionRecord record) {
+        if (record.getId() != null) {
+            AuctionRecord orig = auctionRecordRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return auctionRecordRepo.save(orig);
+        }
+        return auctionRecordRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<AuctionRecord> all(@RequestBody PageQuery pageQuery) {
+        return auctionRecordService.all(pageQuery);
+    }
+
+    @PostMapping("/recordInfos")
+    public Page<AuctionRecordDTO> recordInfos(@RequestBody PageQuery pageQuery) {
+        return auctionRecordService.recordInfos(pageQuery);
+    }
+
+    @PostMapping("/userRecord")
+    public List<AuctionRecordDTO> userRecord(@RequestParam String type) {
+        return auctionRecordService.userRecord(SecurityUtils.getAuthenticatedUser().getId(), type);
+    }
+
+
+    @GetMapping("/get/{id}")
+    public AuctionRecord get(@PathVariable Long id) {
+        return auctionRecordRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        auctionRecordRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<AuctionRecord> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+
+    @GetMapping("/hasPayDeposit")
+    public AuctionRecord hasPayDeposit(Long auctionId) {
+        return auctionRecordRepo
+                .findTopByAuctionIdAndUserIdOrderByIdDesc(auctionId, SecurityUtils.getAuthenticatedUser()
+                        .getId());
+    }
+
+    @PostMapping("/create")
+    public AuctionRecord created(Long auctionId, BigDecimal amount) {
+        return auctionRecordService.create(SecurityUtils.getAuthenticatedUser().getId(), auctionId, amount);
+    }
+}
+

+ 18 - 0
src/main/java/com/izouma/nineth/web/AuthenticationController.java

@@ -35,6 +35,24 @@ public class AuthenticationController {
         return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
     }
 
+    @PostMapping("/oasisLogin")
+    public Map<String, Object> oasisLogin(String phone, String password, Integer expiration) {
+        User user = userService.loginByPhonePwd(phone, password);
+        Map<String, Object> result = userService.oasisInfo(user.getId());
+        result.put("token", jwtTokenUtil.generateToken(JwtUserFactory.create(user)));
+        result.put("userId", user.getId());
+        return result;
+    }
+
+    @PostMapping("/oasisLoginPhone")
+    public Map<String, Object> oasisLogin(String phone, String code) {
+        User user = userService.loginByPhone(phone, code);
+        Map<String, Object> result = userService.oasisInfo(user.getId());
+        result.put("token", jwtTokenUtil.generateToken(JwtUserFactory.create(user)));
+        result.put("userId", user.getId());
+        return result;
+    }
+
     @PostMapping("/loginAdmin")
     public String loginByUserPwdAdmin(String username, String password, Integer expiration) {
         User user = userService.loginByUsernamePwd(username, password);

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

@@ -214,5 +214,17 @@ public class CollectionController extends BaseController {
         collection.setSalable(salable);
         collectionRepo.save(collection);
     }
+
+    @PreAuthorize("hasAnyRole('ADMIN','COMPANY')")
+    @PostMapping("/onShelfOasis")
+    public List<Collection> onShelfOasis(@RequestBody List<Long> oasisIds) {
+        return collectionService.setOasisOnShelf(oasisIds);
+    }
+
+    @PreAuthorize("hasAnyRole('ADMIN','COMPANY')")
+    @PostMapping("/setScancodeOasis")
+    public List<Collection> setScancodeOasis(@RequestBody List<Long> oasisIds) {
+        return collectionService.setOasisScancode(oasisIds);
+    }
 }
 

+ 63 - 4
src/main/java/com/izouma/nineth/web/CompanyCollectionController.java

@@ -1,6 +1,8 @@
 package com.izouma.nineth.web;
 
 import com.izouma.nineth.domain.CompanyCollection;
+import com.izouma.nineth.dto.oasis.OasisDistrictCollectionDTO;
+import com.izouma.nineth.dto.oasis.OasisDistrictDTO;
 import com.izouma.nineth.enums.CollectionStatus;
 import com.izouma.nineth.service.CompanyCollectionService;
 import com.izouma.nineth.dto.PageQuery;
@@ -10,12 +12,14 @@ import com.izouma.nineth.utils.ObjUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 import com.izouma.nineth.utils.excel.ExcelUtils;
 import lombok.AllArgsConstructor;
+import org.springframework.beans.BeanUtils;
 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.ArrayList;
 import java.util.List;
 
 @RestController
@@ -42,6 +46,65 @@ public class CompanyCollectionController extends BaseController {
         return companyCollectionRepo.save(record);
     }
 
+    //oasis创建保存地块接口
+    @PreAuthorize("hasAnyRole('ADMIN','COMPANY')")
+    @PostMapping("/saveDistrict")
+    public CompanyCollection saveDistrict(@RequestBody OasisDistrictDTO oasisDistrictDTO) {
+        CompanyCollection record = new CompanyCollection();
+        BeanUtils.copyProperties(oasisDistrictDTO, record);
+        record.setStatus(CollectionStatus.PENDING);
+        record.setCategory("地块");
+        record.setUserId(7958191L);
+        record.setMinter("oasis地块测试");
+        if (record.getId() != null) {
+            CompanyCollection orig = companyCollectionRepo.findById(record.getId())
+                    .orElseThrow(new BusinessException("无记录"));
+            if (CollectionStatus.SUCCESS.equals(orig.getStatus())) {
+                throw new BusinessException("已通过申请,不可编辑");
+            }
+
+            ObjUtils.merge(orig, record);
+            return companyCollectionRepo.save(orig);
+        }
+        record.setUserId(SecurityUtils.getAuthenticatedUser().getId());
+        return companyCollectionRepo.save(record);
+    }
+
+    //oasis创建保存地块接口
+    @PreAuthorize("hasAnyRole('ADMIN','COMPANY')")
+    @PostMapping("/multiDistrict")
+    public List<CompanyCollection> multiDistrict(@RequestBody List<OasisDistrictDTO> oasisDistrictDTOs) {
+        List<CompanyCollection> companyCollections = new ArrayList<>();
+        oasisDistrictDTOs.forEach(oasisDistrictDTO -> {
+            CompanyCollection record = new CompanyCollection();
+            BeanUtils.copyProperties(oasisDistrictDTO, record);
+            record.setStatus(CollectionStatus.PENDING);
+            record.setCategory("地块");
+            record.setUserId(7958191L);
+            record.setMinter("oasis地块测试");
+            if (record.getId() != null) {
+                CompanyCollection orig = companyCollectionRepo.findById(record.getId())
+                        .orElseThrow(new BusinessException("无记录"));
+                if (CollectionStatus.SUCCESS.equals(orig.getStatus())) {
+                    throw new BusinessException("已通过申请,不可编辑");
+                }
+
+                ObjUtils.merge(orig, record);
+                companyCollections.add(orig);
+            }
+            record.setUserId(SecurityUtils.getAuthenticatedUser().getId());
+            companyCollections.add(record);
+        });
+        return companyCollectionRepo.saveAll(companyCollections);
+    }
+
+    //oasis创建地块接口
+    @PreAuthorize("hasAnyRole('ADMIN','COMPANY')")
+    @PostMapping("/oasisSearch")
+    public List<OasisDistrictCollectionDTO> oasisSearch(@RequestBody List<Long> oasisIds) {
+        return companyCollectionService.oasisSearch(oasisIds);
+    }
+
 
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/all")
@@ -76,9 +139,5 @@ public class CompanyCollectionController extends BaseController {
         companyCollectionService.audit(id, CollectionStatus.FAIL, reason);
     }
 
-//    @PostMapping("/createDistrict")
-//    public void deny(@RequestParam Long id, String reason) {
-//        companyCollectionService.audit(id, CollectionStatus.FAIL, reason);
-//    }
 }
 

+ 5 - 0
src/main/java/com/izouma/nineth/web/HmPayController.java

@@ -68,6 +68,11 @@ public class HmPayController extends BaseController {
                             new OrderNotifyEvent(id, PayMethod.SANDPAY, plat_trx_no,
                                     System.currentTimeMillis(), OrderNotifyEvent.TYPE_RECHARGE));
                     break;
+                case "auctionOrder":
+                    rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                            new OrderNotifyEvent(id, PayMethod.SANDPAY, plat_trx_no,
+                                    System.currentTimeMillis(), OrderNotifyEvent.TYPE_MINT_ORDER));
+                    break;
             }
         }
         return "SUCCESS";

+ 14 - 1
src/main/java/com/izouma/nineth/web/NewsLikeController.java

@@ -1,4 +1,5 @@
 package com.izouma.nineth.web;
+
 import com.izouma.nineth.domain.NewsLike;
 import com.izouma.nineth.service.NewsLikeService;
 import com.izouma.nineth.dto.PageQuery;
@@ -23,7 +24,7 @@ import java.util.List;
 @AllArgsConstructor
 public class NewsLikeController extends BaseController {
     private NewsLikeService newsLikeService;
-    private NewsLikeRepo newsLikeRepo;
+    private NewsLikeRepo    newsLikeRepo;
 
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
@@ -85,5 +86,17 @@ public class NewsLikeController extends BaseController {
     public void unlikeRoom(@PathVariable Long id) {
         newsLikeService.unlikeRoom(SecurityUtils.getAuthenticatedUser().getId(), id);
     }
+
+    @GetMapping("/{id}/likeAuction")
+    @ApiOperation("点赞拍卖")
+    public void likeAuction(@PathVariable Long id) {
+        newsLikeService.likeAuction(SecurityUtils.getAuthenticatedUser().getId(), id);
+    }
+
+    @GetMapping("/{id}/unlikeAuction")
+    @ApiOperation("取消点赞拍卖")
+    public void unlikeAuction(@PathVariable Long id) {
+        newsLikeService.unlikeAuction(SecurityUtils.getAuthenticatedUser().getId(), id);
+    }
 }
 

+ 6 - 4
src/main/java/com/izouma/nineth/web/OrderPayController.java

@@ -30,7 +30,6 @@ import java.util.regex.Pattern;
 @AllArgsConstructor
 public class OrderPayController {
     private final OrderService     orderService;
-    private final AssetService     assetService;
     private final WxMpService      wxMpService;
     private final GiftOrderService giftOrderService;
     private final OrderRepo        orderRepo;
@@ -96,7 +95,8 @@ public class OrderPayController {
     public String payOrderWeixinPC(@RequestParam Long id, @RequestParam String code, Model model) throws WxPayException, EncoderException, WxErrorException {
         WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
         WxMpUser user = wxMpService.oauth2getUserInfo(accessToken, null);
-        WxPayMpOrderResult payParams = (WxPayMpOrderResult) orderService.payOrderWeixin(id, WxPayConstants.TradeType.JSAPI, user.getOpenId());
+        WxPayMpOrderResult payParams = (WxPayMpOrderResult) orderService
+                .payOrderWeixin(id, WxPayConstants.TradeType.JSAPI, user.getOpenId());
         model.addAttribute("payParams", JSON.toJSONString(payParams));
         return "PayOrderPC";
     }
@@ -162,7 +162,8 @@ public class OrderPayController {
     public String payGiftOrderWeixinPC(@RequestParam Long id, @RequestParam String code, Model model) throws WxPayException, EncoderException, WxErrorException {
         WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
         WxMpUser user = wxMpService.oauth2getUserInfo(accessToken, null);
-        WxPayMpOrderResult payParams = (WxPayMpOrderResult) giftOrderService.payOrderWeixin(id, WxPayConstants.TradeType.JSAPI, user.getOpenId());
+        WxPayMpOrderResult payParams = (WxPayMpOrderResult) giftOrderService
+                .payOrderWeixin(id, WxPayConstants.TradeType.JSAPI, user.getOpenId());
         model.addAttribute("payParams", JSON.toJSONString(payParams));
         return "PayOrderPC";
     }
@@ -213,7 +214,8 @@ public class OrderPayController {
     public String payMintOrderWeixinPC(@RequestParam Long id, @RequestParam String code, Model model) throws WxPayException, EncoderException, WxErrorException {
         WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);
         WxMpUser user = wxMpService.oauth2getUserInfo(accessToken, null);
-        WxPayMpOrderResult payParams = (WxPayMpOrderResult) mintOrderService.payOrderWeixin(id, WxPayConstants.TradeType.JSAPI, user.getOpenId());
+        WxPayMpOrderResult payParams = (WxPayMpOrderResult) mintOrderService
+                .payOrderWeixin(id, WxPayConstants.TradeType.JSAPI, user.getOpenId());
         model.addAttribute("payParams", JSON.toJSONString(payParams));
         return "PayOrderPC";
     }

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

@@ -164,4 +164,35 @@ public class OrderPayControllerV2 {
     public String payRechargeQuick(@RequestParam Long userId, @RequestParam BigDecimal amount) {
         return orderPayService.rechargeQuick(userId, amount);
     }
+
+    @RequestMapping(value = "/auction/alipay", method = RequestMethod.GET)
+    @ResponseBody
+    public String payAuctionOrderAlipayH5(Long id) {
+        return orderPayService.payAuctionOrder(id);
+    }
+
+    @RequestMapping(value = "/auction/alipay_wx", method = RequestMethod.GET)
+    public String payAuctionOrderAlipayWx(Long id, Model model) {
+        String payUrl = orderPayService.payAuctionOrder(id);
+        model.addAttribute("payUrl", payUrl);
+        model.addAttribute("orderId", id);
+        return "AlipayHtml";
+    }
+
+    @RequestMapping(value = "/auction/sandQuick", method = RequestMethod.GET, produces = "text/html")
+    @ResponseBody
+    public String payAuctionQuick(@RequestParam Long id) {
+        return orderPayService.payAuctionQuick(id);
+    }
+
+    @RequestMapping(value = "auction/balance", method = RequestMethod.GET)
+    public void payAuctionOrderBalance(@RequestParam Long id, @RequestParam String tradeCode) {
+        orderPayService.payAuctionOrderBalance(id, SecurityUtils.getAuthenticatedUser().getId(), tradeCode);
+    }
+
+    @RequestMapping(value = "/auction/agreement")
+    @ResponseBody
+    public Map<String, Object> payAuctionAgreement(@RequestParam Long id, String bindCardId) {
+        return orderPayService.payAuctionOrderAgreement(id, bindCardId);
+    }
 }

+ 10 - 0
src/main/java/com/izouma/nineth/web/PayEaseController.java

@@ -5,6 +5,11 @@ import com.alibaba.fastjson15.JSONObject;
 import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.enums.PayMethod;
 import com.izouma.nineth.event.OrderNotifyEvent;
+import com.izouma.nineth.service.AuctionOrderService;
+import com.izouma.nineth.service.AuctionOrderService;
+import com.izouma.nineth.service.GiftOrderService;
+import com.izouma.nineth.service.MintOrderService;
+import com.izouma.nineth.service.UserBalanceService;
 import com.upay.sdk.CipherWrapper;
 import com.upay.sdk.onlinepay.executer.OnlinePayOrderExecuter;
 import lombok.AllArgsConstructor;
@@ -53,6 +58,11 @@ public class PayEaseController {
                             new OrderNotifyEvent(id, PayMethod.SANDPAY, serialNumber,
                                     System.currentTimeMillis(), OrderNotifyEvent.TYPE_RECHARGE));
                     break;
+                case "auctionOrder":
+                    rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                            new OrderNotifyEvent(id, PayMethod.SANDPAY, serialNumber,
+                                    System.currentTimeMillis(), OrderNotifyEvent.TYPE_RECHARGE));
+                    break;
             }
         }
         return "SUCCESS";

+ 5 - 0
src/main/java/com/izouma/nineth/web/SandPayController.java

@@ -75,6 +75,11 @@ public class SandPayController {
                                         new OrderNotifyEvent(id, PayMethod.SANDPAY, payOrderCode,
                                                 System.currentTimeMillis(), OrderNotifyEvent.TYPE_RECHARGE));
                                 break;
+                            case "auctionOrder":
+                                rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                                        new OrderNotifyEvent(id, PayMethod.SANDPAY, payOrderCode,
+                                                System.currentTimeMillis(), OrderNotifyEvent.TYPE_RECHARGE));
+                                break;
                         }
                     }
                     return "respCode=000000";

+ 5 - 5
src/main/java/com/izouma/nineth/web/SmsController.java

@@ -22,11 +22,11 @@ public class SmsController {
 
     @GetMapping("/sendVerify")
     public String sendVerify(@RequestParam String phone) {
-        throw new BusinessException("此接口已停用,请重启APP或刷新网页");
-//        if (!Pattern.matches(Constants.Regex.PHONE, phone)) {
-//            throw new BusinessException("请输入正确的手机号");
-//        }
-//        return smsService.sendVerify(phone);
+//        throw new BusinessException("此接口已停用,请重启APP或刷新网页");
+        if (!Pattern.matches(Constants.Regex.PHONE, phone)) {
+            throw new BusinessException("请输入正确的手机号");
+        }
+        return smsService.sendVerify(phone);
     }
 
     @GetMapping("/sendSecureVerify")

File diff ditekan karena terlalu besar
+ 0 - 0
src/main/resources/genjson/AuctionOrder.json


+ 69 - 0
src/main/vue/src/router.js

@@ -708,6 +708,75 @@ const router = new Router({
                         title: 'captcha'
                     }
                 },
+                {
+                    path: '/auctionOrderEdit',
+                    name: 'AuctionOrderEdit',
+                    component: () => import(/* webpackChunkName: "auctionOrderEdit" */ '@/views/AuctionOrderEdit.vue'),
+                    meta: {
+                        title: '拍卖订单编辑'
+                    }
+                },
+                {
+                    path: '/auctionOrderList',
+                    name: 'AuctionOrderList',
+                    component: () => import(/* webpackChunkName: "auctionOrderList" */ '@/views/AuctionOrderList.vue'),
+                    meta: {
+                        title: '拍卖订单'
+                    }
+                },
+                {
+                    path: '/auctionOrderUsedList',
+                    name: 'AuctionOrderUsedList',
+                    component: () => import(/* webpackChunkName: "auctionOrderList" */ '@/views/AuctionOrderUsedList.vue'),
+                    meta: {
+                        title: '拍卖订单'
+                    }
+                },
+                {
+                    path: '/auctionActivityEdit',
+                    name: 'AuctionActivityEdit',
+                    component: () =>
+                        import(/* webpackChunkName: "auctionActivityEdit" */ '@/views/AuctionActivityEdit.vue'),
+                    meta: {
+                        title: '拍卖编辑'
+                    }
+                },
+                {
+                    path: '/auctionActivityList',
+                    name: 'AuctionActivityList',
+                    component: () =>
+                        import(/* webpackChunkName: "auctionActivityList" */ '@/views/AuctionActivityList.vue'),
+                    meta: {
+                        title: '拍卖'
+                    }
+                },
+                {
+                    path: '/auctionUsedList',
+                    name: 'AuctionUsedList',
+                    component: () =>
+                        import(/* webpackChunkName: "auctionUsedList" */ '@/views/AuctionUsedList.vue'),
+                    meta: {
+                        title: '拍卖'
+                    }
+                },
+                {
+                    path: '/auctionRecordEdit',
+                    name: 'AuctionRecordEdit',
+                    component: () =>
+                        import(/* webpackChunkName: "auctionRecordEdit" */ '@/views/AuctionRecordEdit.vue'),
+                    meta: {
+                        title: '拍卖纪录编辑'
+                    }
+                },
+                {
+                    path: '/auctionRecordList',
+                    name: 'AuctionRecordList',
+                    component: () =>
+                        import(/* webpackChunkName: "auctionRecordList" */ '@/views/AuctionRecordList.vue'),
+                    meta: {
+                        title: '拍卖纪录'
+                    }
+                },
                 {
                     path: '/tagEdit',
                     name: 'TagEdit',

+ 410 - 0
src/main/vue/src/views/AuctionActivityEdit.vue

@@ -0,0 +1,410 @@
+<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="110px"
+                    label-position="right"
+                    size="small"
+                    style="max-width: 750px"
+                >
+                    <el-form-item prop="name" label="拍卖名称">
+                        <el-input v-model="formData.name" style="width: 500px" :disabled="!canEdit"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="pic" label="图片">
+                        <object-upload
+                            v-model="formData.pic[0]"
+                            compress
+                            width="3000"
+                            height="3000"
+                            :disabled="!canEdit"
+                        ></object-upload>
+                        <div class="tip">支持JPG、PNG、GIF、MP4,推荐长宽比1:1</div>
+                    </el-form-item>
+                    <el-form-item prop="model3d" label="3D模型">
+                        <model-upload
+                            :limit="1"
+                            v-model="formData.model3d"
+                            :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.model3d">
+                        <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.model3d">
+                        <el-input-number v-model="yOffset"></el-input-number>
+                    </el-form-item>
+                    <div class="inline-wrapper">
+                        <el-form-item prop="sellerId" label="起拍者">
+                            <minter-select
+                                v-model="formData.sellerId"
+                                @detail="onMinterDetail"
+                                :disabled="!canEdit"
+                            ></minter-select>
+                        </el-form-item>
+                        <el-form-item prop="category" label="分类">
+                            <el-select v-model="formData.category" :disabled="!canEdit">
+                                <el-option
+                                    v-for="item in cateogories"
+                                    :label="item"
+                                    :value="item"
+                                    :key="item"
+                                ></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </div>
+
+                    <el-form-item prop="detail" label="详情" style="width: calc(100vw - 450px)">
+                        <rich-text v-model="formData.detail"></rich-text>
+                    </el-form-item>
+                    <el-form-item prop="auctionType" label="拍卖类型">
+                        <el-select
+                            v-model="formData.auctionType"
+                            clearable
+                            filterable
+                            placeholder="请选择"
+                            :disabled="!canEdit"
+                        >
+                            <el-option
+                                v-for="item in auctionTypeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <div class="inline-wrapper">
+                        <el-form-item prop="startingPrice" label="起拍价">
+                            <el-input-number
+                                type="number"
+                                v-model="formData.startingPrice"
+                                :disabled="!canEdit"
+                            ></el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="increment" label="加价幅度">
+                            <el-input-number
+                                type="number"
+                                v-model="formData.increment"
+                                :disabled="!canEdit"
+                            ></el-input-number>
+                        </el-form-item>
+                    </div>
+                    <div class="inline-wrapper">
+                        <el-form-item prop="deposit" label="保证金">
+                            <el-input-number
+                                type="number"
+                                v-model="formData.deposit"
+                                :disabled="!canEdit"
+                            ></el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="fixedPrice" label="一口价">
+                            <el-input-number
+                                type="number"
+                                v-model="formData.fixedPrice"
+                                :disabled="!canEdit"
+                            ></el-input-number>
+                        </el-form-item>
+                    </div>
+                    <div class="inline-wrapper">
+                        <el-form-item prop="royalties" label="版税(%)">
+                            <el-input-number v-model="formData.royalties" :min="0" :max="99" :disabled="!canEdit">
+                            </el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="serviceCharge" label="手续费(%)">
+                            <el-input-number v-model="formData.serviceCharge" :min="0" :max="99" :disabled="!canEdit">
+                            </el-input-number>
+                        </el-form-item>
+                    </div>
+                    <el-form-item prop="startTime" label="开始时间">
+                        <el-date-picker
+                            v-model="formData.startTime"
+                            type="datetime"
+                            value-format="yyyy-MM-dd HH:mm:ss"
+                            placeholder="选择日期时间"
+                        >
+                        </el-date-picker>
+                    </el-form-item>
+                    <el-form-item prop="endTime" label="截止时间">
+                        <el-date-picker
+                            v-model="formData.endTime"
+                            type="datetime"
+                            value-format="yyyy-MM-dd HH:mm:ss"
+                            placeholder="选择日期时间"
+                        >
+                        </el-date-picker>
+                    </el-form-item>
+
+                    <!-- <el-form-item prop="purchasePrice" label="出价">
+                        <el-input-number type="number" v-model="formData.purchasePrice"></el-input-number>
+                    </el-form-item> -->
+                    <!-- <el-form-item prop="purchaserId" label="买家id">
+                        <el-input-number type="number" v-model="formData.purchaserId"></el-input-number>
+                    </el-form-item>
+                    <el-form-item prop="purchaser" label="买家">
+                        <el-input v-model="formData.purchaser"></el-input>
+                    </el-form-item> -->
+                    <!-- <el-form-item prop="status" label="状态">
+                        <el-select v-model="formData.status" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in statusOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item> -->
+                    <!-- <el-form-item prop="source" label="状态">
+                        <el-select v-model="formData.source" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in sourceOptions"
+                                :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 { format, parse, isBefore } from 'date-fns';
+export default {
+    name: 'AuctionActivityEdit',
+    components: { ModelUpload },
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('auctionActivity/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                    this.canEdit = res.source !== 'TRANSFER';
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {
+                pic: [],
+                source: 'OFFICIAL'
+            },
+            rules: {
+                name: [
+                    {
+                        required: true,
+                        message: '请输入名称',
+                        trigger: 'blur'
+                    }
+                ],
+                pic: [
+                    {
+                        validator: (rule, value, callback) => {
+                            if (value) {
+                                if (!(value instanceof Array)) {
+                                    callback(new Error('请上传内容'));
+                                    return;
+                                } else {
+                                    for (let f of value) {
+                                        if (!f.url) {
+                                            callback(new Error('请上传内容'));
+                                            return;
+                                        }
+                                    }
+                                }
+                                callback();
+                            } else {
+                                callback(new Error('请上传内容'));
+                            }
+                        },
+                        trigger: 'blur'
+                    }
+                ],
+                seller: [
+                    {
+                        required: true,
+                        message: '请输入铸造者',
+                        trigger: 'blur'
+                    }
+                ],
+                sellerId: [
+                    {
+                        required: true,
+                        message: '请输入铸造者ID',
+                        trigger: 'blur'
+                    }
+                ],
+                detail: [
+                    {
+                        required: true,
+                        message: '请输入详情',
+                        trigger: 'blur'
+                    }
+                ],
+                increment: [{ required: true, message: '请输入加价幅度' }],
+                royalties: [{ required: true, message: '请填写版税' }],
+                serviceCharge: [{ required: true, message: '请填手续费' }],
+                auctionType: [{ required: true, message: '请选择拍卖类型' }],
+                startTime: [
+                    {
+                        validator: (rule, value, callback) => {
+                            if (!value) {
+                                callback(new Error('请填写发布时间'));
+                                // } else if (isBefore(parse(value, 'yyyy-MM-dd HH:mm:ss', new Date()), new Date())) {
+                                //     callback(new Error('发布时间不能小于当前时间'));
+                            } else {
+                                callback();
+                            }
+                        },
+                        trigger: 'blur'
+                    }
+                ],
+                endTime: [
+                    {
+                        validator: (rule, value, callback) => {
+                            if (!value) {
+                                callback(new Error('请填写结束时间'));
+                            } else if (isBefore(parse(value, 'yyyy-MM-dd HH:mm:ss', new Date()), new Date())) {
+                                callback(new Error('结束时间不能小于当前时间'));
+                            } else if (this.formData.scheduleSale) {
+                                if (
+                                    isBefore(
+                                        parse(value, 'yyyy-MM-dd HH:mm:ss', new Date()),
+                                        parse(this.formData.startTime, 'yyyy-MM-dd HH:mm:ss', new Date())
+                                    )
+                                ) {
+                                    callback(new Error('结束时间不能小于发布时间'));
+                                }
+                            }
+                        },
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            auctionTypeOptions: [
+                { label: '虚拟藏品', value: 'NFT' },
+                { label: '实物', value: 'ENTITY' }
+            ],
+            statusOptions: [
+                { label: '未开始', value: 'NOTSTARTED' },
+                { label: '进行中', value: 'ONGOING' },
+                { label: '成交', value: 'PURCHASED' },
+                { label: '一口价成交', value: 'FIXED_PRICE_PURCHASED' },
+                { label: '流拍', value: 'PASS' },
+                { label: '完成', value: 'FINISH' }
+            ],
+            sourceOptions: [
+                { label: '官方拍卖', value: 'OFFICIAL' },
+                { label: '转让拍卖', value: 'TRANSFER' }
+            ],
+            cateogories: ['勋章', '收藏品', '数字艺术', '门票', '游戏', '音乐', '使用', '其他'],
+            customUrl: resolveUrl(this.$baseUrl, 'upload/3dModel'),
+            canEdit: true
+        };
+    },
+    // computed: {
+    //     canEdit() {
+    //         // return !!!this.$route.query.id;
+    //         return this.formData.source === 'TRANSFER';
+    //     }
+    // },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+            if (data.model3d) {
+                data.model3d.url = data.model3d.url + '?scale=' + this.scale + '&yOffset=' + this.yOffset;
+            }
+
+            this.saving = true;
+
+            this.$http
+                .post('/auctionActivity/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(`/auctionActivity/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 || '删除失败');
+                    }
+                });
+        },
+        onMinterDetail(e) {
+            console.log(e);
+            this.$set(this.formData, 'minter', e.nickname);
+            this.$set(this.formData, 'minterAvatar', e.avatar);
+
+            this.$set(this.formData, 'sellerId', e.id);
+            this.$set(this.formData, 'seller', e.nickname);
+            this.$set(this.formData, 'sellerAvatar', e.avatar);
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+.tip {
+    font-size: 12px;
+    color: @text3;
+    margin-top: 5px;
+}
+.inline-wrapper {
+    .el-form-item {
+        display: inline-block;
+    }
+}
+</style>

+ 259 - 0
src/main/vue/src/views/AuctionActivityList.vue

@@ -0,0 +1,259 @@
+<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>
+            <created-at-picker
+                v-model="endTime"
+                @input="getData"
+                name="拍卖结束"
+                class="filter-item"
+            ></created-at-picker>
+            <el-select v-model="status" clearable class="filter-item" placeholder="请选择状态" @change="getData">
+                <el-option
+                    v-for="item in statusOptions"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                ></el-option>
+            </el-select>
+        </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="90"> </el-table-column>
+            <el-table-column prop="sellerId" label="起拍人ID"> </el-table-column>
+            <el-table-column prop="seller" label="起拍人昵称"> </el-table-column>
+            <!-- <el-table-column prop="sellerAvatar" label="起拍人头像"> </el-table-column> -->
+            <el-table-column prop="name" label="拍卖名称"> </el-table-column>
+            <el-table-column prop="auctionType" label="拍卖类型" :formatter="auctionTypeFormatter"> </el-table-column>
+            <!-- <el-table-column prop="minter" label="铸造者"> </el-table-column> -->
+            <el-table-column prop="pic" label="作品内容" width="90" align="center">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.pic[0].thumb || row.pic[0].url"
+                        fit="cover"
+                        :preview-src-list="row.pic.map(i => i.thumb || i.url)"
+                    ></el-image>
+                </template>
+            </el-table-column>
+            <!-- <el-table-column prop="detail" label="详情"> </el-table-column> -->
+            <!-- <el-table-column prop="category" label="分类"> </el-table-column> -->
+            <el-table-column prop="startingPrice" label="起拍价"> </el-table-column>
+            <el-table-column prop="deposit" label="保证金"> </el-table-column>
+            <el-table-column prop="fixedPrice" label="一口价"> </el-table-column>
+            <el-table-column prop="startTime" label="开始时间"> </el-table-column>
+            <el-table-column prop="increment" label="加价幅度"> </el-table-column>
+            <el-table-column prop="endTime" label="截止时间"> </el-table-column>
+            <el-table-column prop="purchasePrice" label="成交价"> </el-table-column>
+            <el-table-column prop="purchaserId" label="买家id"> </el-table-column>
+            <el-table-column prop="purchaser" label="买家"> </el-table-column>
+            <el-table-column prop="status" label="状态" :formatter="statusFormatter"> </el-table-column>
+            <el-table-column prop="source" label="来源" :formatter="sourceFormatter"> </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: 'AuctionActivityList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/auctionActivity/all',
+            downloading: false,
+            auctionTypeOptions: [
+                { label: '虚拟藏品', value: 'NFT' },
+                { label: '实物', value: 'ENTITY' }
+            ],
+            statusOptions: [
+                { label: '未开始', value: 'NOTSTARTED' },
+                { label: '进行中', value: 'ONGOING' },
+                { label: '成交', value: 'PURCHASED' },
+                { label: '一口价成交', value: 'FIXED_PRICE_PURCHASED' },
+                { label: '流拍', value: 'PASS' },
+                { label: '完成', value: 'FINISH' }
+            ],
+            sourceOptions: [
+                { label: '官方拍卖', value: 'OFFICIAL' },
+                { label: '转让拍卖', value: 'TRANSFER' }
+            ],
+            endTime: '',
+            status: ''
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        auctionTypeFormatter(row, column, cellValue, index) {
+            let selectedOption = this.auctionTypeOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        statusFormatter(row, column, cellValue, index) {
+            let selectedOption = this.statusOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        sourceFormatter(row, column, cellValue, index) {
+            let selectedOption = this.sourceOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return {
+                search: this.search,
+                query: { del: false, endTime: this.endTime, status: this.status, source: 'OFFICIAL' }
+            };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/auctionActivityEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/auctionActivityEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/auctionActivity/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(`/auctionActivity/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>

+ 270 - 0
src/main/vue/src/views/AuctionOrderEdit.vue

@@ -0,0 +1,270 @@
+<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: 700px"
+                >
+                    <el-form-item prop="userId" label="用户信息">
+                        <el-input-number type="number" v-model="formData.userId" disabled></el-input-number>
+                        <el-input
+                            v-model="formData.nickname"
+                            disabled
+                            style="margin-left: 6px; width: 200px"
+                        ></el-input>
+                    </el-form-item>
+                    <el-form-item prop="auctionId" label="拍卖信息">
+                        <el-input-number type="number" v-model="formData.auctionId" disabled></el-input-number>
+                        <el-input v-model="formData.name" disabled style="margin-left: 6px; width: 200px"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="pic" label="">
+                        <object-upload :value="pic" disabled></object-upload>
+                    </el-form-item>
+                    <!-- <el-form-item prop="properties" label="特性">
+                        <el-input v-model="formData.properties"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="privileges" label="特权">
+                        <el-input v-model="formData.privileges"></el-input>
+                    </el-form-item> -->
+                    <div class="inline-wrapper">
+                        <el-form-item prop="royalties" label="版税(%)">
+                            <el-input-number v-model="formData.royalties" :min="0" :max="99" disabled>
+                            </el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="serviceCharge" label="手续费(%)">
+                            <el-input-number v-model="formData.serviceCharge" :min="0" :max="99" disabled>
+                            </el-input-number>
+                        </el-form-item>
+                    </div>
+                    <div class="inline-wrapper">
+                        <el-form-item prop="price" label="价格">
+                            <el-input-number type="number" v-model="formData.price" disabled></el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="totalPrice" label="总价">
+                            <el-input-number type="number" v-model="formData.totalPrice" disabled></el-input-number>
+                        </el-form-item>
+                    </div>
+                    <el-form-item prop="source" label="来源">
+                        <el-select v-model="formData.source" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in sourceOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item prop="paymentType" label="支付类型">
+                        <el-select v-model="formData.paymentType" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in paymentTypeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item prop="status" label="状态">
+                        <el-select v-model="formData.status" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in statusOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <div v-if="formData.status != 'CANCELLED' && formData.status != 'NOT_PAID'">
+                        <el-form-item prop="transactionId" label="交易订单号">
+                            <el-input v-model="formData.transactionId" disabled></el-input>
+                        </el-form-item>
+                        <el-form-item prop="payMethod" label="支付信息">
+                            <el-select
+                                v-model="formData.payMethod"
+                                clearable
+                                filterable
+                                placeholder="支付方式"
+                                disabled
+                            >
+                                <el-option
+                                    v-for="item in payMethodOptions"
+                                    :key="item.value"
+                                    :label="item.label"
+                                    :value="item.value"
+                                >
+                                </el-option>
+                            </el-select>
+                            <el-date-picker
+                                v-model="formData.payAt"
+                                type="datetime"
+                                value-format="yyyy-MM-dd HH:mm:ss"
+                                placeholder="支付时间"
+                                style="margin-left: 6px"
+                                disabled
+                            >
+                            </el-date-picker>
+                        </el-form-item>
+                    </div>
+                    <el-form-item prop="payTime" label="支付时间">
+                        <el-date-picker
+                            v-model="formData.payTime"
+                            type="datetime"
+                            value-format="yyyy-MM-dd HH:mm:ss"
+                            placeholder="选择日期时间"
+                        >
+                        </el-date-picker>
+                    </el-form-item>
+                    <el-form-item prop="cancelTime" label="取消时间" v-if="formData.status == 'CANCELLED'">
+                        <el-date-picker
+                            v-model="formData.cancelTime"
+                            type="datetime"
+                            value-format="yyyy-MM-dd HH:mm:ss"
+                            placeholder="选择日期时间"
+                            disabled
+                        >
+                        </el-date-picker>
+                    </el-form-item>
+                    <el-form-item label="收货信息">
+                        <el-input v-model="formData.contactName" style="width: 180px; margin-right: 6px"></el-input>
+                        <el-input v-model="formData.contactPhone" style="width: 240px"></el-input>
+                        <el-input
+                            type="textarea"
+                            size="2"
+                            v-model="formData.address"
+                            style="margin-top: 6px"
+                        ></el-input>
+                    </el-form-item>
+                    <el-form-item class="form-submit">
+                        <!-- <el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+                        <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                            删除
+                        </el-button> -->
+                        <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name: 'AuctionOrderEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('auctionOrder/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {},
+            sourceOptions: [
+                { label: '官方拍卖', value: 'OFFICIAL' },
+                { label: '转让拍卖', value: 'TRANSFER' }
+            ],
+            paymentTypeOptions: [
+                { label: '保证金', value: 'DEPOSIT' },
+                { label: '成交金', value: 'PURCHASE_PRICE' },
+                { label: '一口价', value: 'FIXED_PRICE' }
+            ],
+            statusOptions: [
+                { label: '未支付', value: 'NOT_PAID' },
+                { label: '已完成', value: 'FINISH' },
+                { label: '已取消', value: 'CANCELLED' },
+                { label: '退款中', value: 'REFUNDING' },
+                { label: '已退款', value: 'REFUNDED' },
+                { label: '待发货', value: 'DELIVERY' },
+                { label: '待收货', value: 'RECEIVE' }
+            ],
+            payMethodOptions: [
+                { label: '微信', value: 'WEIXIN' },
+                { label: '支付宝', value: 'ALIPAY' },
+                { label: '无GAS费', value: 'FREE' },
+                { label: '衫德支付', value: 'SANDPAY' },
+                { label: '河马支付', value: 'HMPAY' },
+                { label: '首信易', value: 'PAYEASE' },
+                { label: '余额支付', value: 'BALANCE' }
+            ]
+        };
+    },
+    computed: {
+        pic() {
+            return (this.formData.pic || [])[0] || {};
+        }
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/auctionOrder/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(`/auctionOrder/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>
+.inline-wrapper {
+    .el-form-item {
+        display: inline-block;
+    }
+}
+</style>

+ 289 - 0
src/main/vue/src/views/AuctionOrderList.vue

@@ -0,0 +1,289 @@
+<template>
+    <div class="list-view">
+        <page-title> </page-title>
+        <div class="filters-container">
+            <el-input
+                placeholder="搜索交易ID..."
+                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>
+            <el-select v-model="status" placeholder="筛选状态" clearable @change="getData" class="filter-item">
+                <el-option
+                    v-for="item in statusOptions"
+                    :key="item.value"
+                    :value="item.value"
+                    :label="item.label"
+                ></el-option>
+            </el-select>
+            <el-input
+                placeholder="搜索拍卖名称"
+                v-model="name"
+                clearable
+                class="filter-item"
+                @keyup.enter.native="getData"
+            >
+            </el-input>
+            <el-input
+                placeholder="搜索用户ID"
+                v-model="userId"
+                clearable
+                class="filter-item"
+                @keyup.enter.native="getData"
+            >
+            </el-input>
+            <created-at-picker v-model="createdAt" @input="getData" name="下单" class="filter-item"></created-at-picker>
+        </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="155"> </el-table-column>
+            <el-table-column prop="userId" label="用户ID"> </el-table-column>
+            <el-table-column prop="nickname" label="昵称"> </el-table-column>
+            <el-table-column prop="auctionId" label="拍卖ID"> </el-table-column>
+            <el-table-column prop="name" label="名称"> </el-table-column>
+            <el-table-column prop="pic" label="图片">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.pic[0].thumb || row.pic[0].url"
+                        fit="cover"
+                        :preview-src-list="row.pic.map(i => i.thumb || i.url)"
+                    ></el-image>
+                </template>
+            </el-table-column>
+            <el-table-column prop="source" label="来源" :formatter="sourceFormatter"> </el-table-column>
+            <el-table-column prop="paymentType" label="支付类型" :formatter="paymentTypeFormatter"> </el-table-column>
+            <!-- <el-table-column prop="price" label="价格"> </el-table-column> -->
+            <!-- <el-table-column prop="gasPrice" label="gas费"> </el-table-column> -->
+            <el-table-column prop="totalPrice" label="总价"> </el-table-column>
+            <el-table-column prop="status" label="状态" :formatter="statusFormatter"> </el-table-column>
+            <el-table-column prop="payMethod" label="支付方式" :formatter="payMethodFormatter"> </el-table-column>
+            <el-table-column prop="transactionId" label="交易ID"> </el-table-column>
+            <el-table-column prop="createdAt" label="下单时间"> </el-table-column>
+            <el-table-column prop="payTime" label="支付时间"> </el-table-column>
+            <el-table-column prop="cancelTime" label="取消时间"> </el-table-column>
+            <el-table-column label="收件人信息" min-width="120" show-overflow-tooltip>
+                <template slot-scope="{ row }">
+                    <span>{{ row.contactName }}</span>
+                    <span style="margin-left: 10px">{{ row.contactPhone }}</span
+                    ><br />
+                    <span>{{ row.address }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" width="120">
+                <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: 'AuctionOrderList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/auctionOrder/all',
+            downloading: false,
+            sourceOptions: [
+                { label: '官方拍卖', value: 'OFFICIAL' },
+                { label: '转让拍卖', value: 'TRANSFER' }
+            ],
+            paymentTypeOptions: [
+                { label: '保证金', value: 'DEPOSIT' },
+                { label: '成交金', value: 'PURCHASE_PRICE' },
+                { label: '一口价', value: 'FIXED_PRICE' }
+            ],
+            statusOptions: [
+                { label: '未支付', value: 'NOT_PAID' },
+                { label: '已完成', value: 'FINISH' },
+                { label: '已取消', value: 'CANCELLED' },
+                { label: '退款中', value: 'REFUNDING' },
+                { label: '已退款', value: 'REFUNDED' },
+                { label: '待发货', value: 'DELIVERY' },
+                { label: '待收货', value: 'RECEIVE' }
+            ],
+            payMethodOptions: [
+                { label: '微信', value: 'WEIXIN' },
+                { label: '支付宝', value: 'ALIPAY' },
+                { label: '无GAS费', value: 'FREE' },
+                { label: '衫德支付', value: 'SANDPAY' },
+                { label: '河马支付', value: 'HMPAY' },
+                { label: '首信易', value: 'PAYEASE' },
+                { label: '余额支付', value: 'BALANCE' }
+            ],
+            name: '',
+            userId: '',
+            status: '',
+            createdAt: ''
+        };
+    },
+    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 '';
+        },
+        sourceFormatter(row, column, cellValue, index) {
+            let selectedOption = this.sourceOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        paymentTypeFormatter(row, column, cellValue, index) {
+            let selectedOption = this.paymentTypeOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        statusFormatter(row, column, cellValue, index) {
+            let selectedOption = this.statusOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        payMethodFormatter(row, column, cellValue, index) {
+            let selectedOption = this.payMethodOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return {
+                search: this.search,
+                query: {
+                    del: false,
+                    source: 'OFFICIAL',
+                    name: this.name,
+                    userId: this.userId,
+                    status: this.status,
+                    createdAt: this.createdAt
+                }
+            };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/auctionOrderEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/auctionOrderEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/auctionOrder/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(`/auctionOrder/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>

+ 289 - 0
src/main/vue/src/views/AuctionOrderUsedList.vue

@@ -0,0 +1,289 @@
+<template>
+    <div class="list-view">
+        <page-title> </page-title>
+        <div class="filters-container">
+            <el-input
+                placeholder="搜索交易ID..."
+                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>
+            <el-select v-model="status" placeholder="筛选状态" clearable @change="getData" class="filter-item">
+                <el-option
+                    v-for="item in statusOptions"
+                    :key="item.value"
+                    :value="item.value"
+                    :label="item.label"
+                ></el-option>
+            </el-select>
+            <el-input
+                placeholder="搜索拍卖名称"
+                v-model="name"
+                clearable
+                class="filter-item"
+                @keyup.enter.native="getData"
+            >
+            </el-input>
+            <el-input
+                placeholder="搜索用户ID"
+                v-model="userId"
+                clearable
+                class="filter-item"
+                @keyup.enter.native="getData"
+            >
+            </el-input>
+            <created-at-picker v-model="createdAt" @input="getData" name="下单" class="filter-item"></created-at-picker>
+        </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="155"> </el-table-column>
+            <el-table-column prop="userId" label="用户ID"> </el-table-column>
+            <el-table-column prop="nickname" label="昵称"> </el-table-column>
+            <el-table-column prop="auctionId" label="拍卖ID"> </el-table-column>
+            <el-table-column prop="name" label="名称"> </el-table-column>
+            <el-table-column prop="pic" label="图片">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.pic[0].thumb || row.pic[0].url"
+                        fit="cover"
+                        :preview-src-list="row.pic.map(i => i.thumb || i.url)"
+                    ></el-image>
+                </template>
+            </el-table-column>
+            <el-table-column prop="source" label="来源" :formatter="sourceFormatter"> </el-table-column>
+            <el-table-column prop="paymentType" label="支付类型" :formatter="paymentTypeFormatter"> </el-table-column>
+            <!-- <el-table-column prop="price" label="价格"> </el-table-column> -->
+            <!-- <el-table-column prop="gasPrice" label="gas费"> </el-table-column> -->
+            <el-table-column prop="totalPrice" label="总价"> </el-table-column>
+            <el-table-column prop="status" label="状态" :formatter="statusFormatter"> </el-table-column>
+            <el-table-column prop="payMethod" label="支付方式" :formatter="payMethodFormatter"> </el-table-column>
+            <el-table-column prop="transactionId" label="交易ID"> </el-table-column>
+            <el-table-column prop="createdAt" label="下单时间"> </el-table-column>
+            <el-table-column prop="payTime" label="支付时间"> </el-table-column>
+            <el-table-column prop="cancelTime" label="取消时间"> </el-table-column>
+            <el-table-column label="收件人信息" min-width="120" show-overflow-tooltip>
+                <template slot-scope="{ row }">
+                    <span>{{ row.contactName }}</span>
+                    <span style="margin-left: 10px">{{ row.contactPhone }}</span
+                    ><br />
+                    <span>{{ row.address }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" width="120">
+                <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: 'AuctionOrderList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/auctionOrder/all',
+            downloading: false,
+            sourceOptions: [
+                { label: '官方拍卖', value: 'OFFICIAL' },
+                { label: '转让拍卖', value: 'TRANSFER' }
+            ],
+            paymentTypeOptions: [
+                { label: '保证金', value: 'DEPOSIT' },
+                { label: '成交金', value: 'PURCHASE_PRICE' },
+                { label: '一口价', value: 'FIXED_PRICE' }
+            ],
+            statusOptions: [
+                { label: '未支付', value: 'NOT_PAID' },
+                { label: '已完成', value: 'FINISH' },
+                { label: '已取消', value: 'CANCELLED' },
+                { label: '退款中', value: 'REFUNDING' },
+                { label: '已退款', value: 'REFUNDED' },
+                { label: '待发货', value: 'DELIVERY' },
+                { label: '待收货', value: 'RECEIVE' }
+            ],
+            payMethodOptions: [
+                { label: '微信', value: 'WEIXIN' },
+                { label: '支付宝', value: 'ALIPAY' },
+                { label: '无GAS费', value: 'FREE' },
+                { label: '衫德支付', value: 'SANDPAY' },
+                { label: '河马支付', value: 'HMPAY' },
+                { label: '首信易', value: 'PAYEASE' },
+                { label: '余额支付', value: 'BALANCE' }
+            ],
+            name: '',
+            userId: '',
+            status: '',
+            createdAt: ''
+        };
+    },
+    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 '';
+        },
+        sourceFormatter(row, column, cellValue, index) {
+            let selectedOption = this.sourceOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        paymentTypeFormatter(row, column, cellValue, index) {
+            let selectedOption = this.paymentTypeOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        statusFormatter(row, column, cellValue, index) {
+            let selectedOption = this.statusOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        payMethodFormatter(row, column, cellValue, index) {
+            let selectedOption = this.payMethodOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return {
+                search: this.search,
+                query: {
+                    del: false,
+                    source: 'TRANSFER',
+                    name: this.name,
+                    userId: this.userId,
+                    status: this.status,
+                    createdAt: this.createdAt
+                }
+            };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/auctionOrderEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/auctionOrderEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/auctionOrder/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(`/auctionOrder/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>

+ 132 - 0
src/main/vue/src/views/AuctionRecordEdit.vue

@@ -0,0 +1,132 @@
+<template>
+    <div class="edit-view">
+        <page-title>
+            <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+            <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                删除
+            </el-button>
+            <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+        </page-title>
+        <div class="edit-view__content-wrapper">
+            <div class="edit-view__content-section">
+                <el-form :model="formData" :rules="rules" ref="form" label-width="108px" label-position="right"
+                         size="small"
+                         style="max-width: 500px;">
+                        <el-form-item prop="userId" label="用户ID">
+                                    <el-input-number type="number" v-model="formData.userId"></el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="avatar" label="用户头像">
+                                    <el-input v-model="formData.avatar"></el-input>
+                        </el-form-item>
+                        <el-form-item prop="user" label="用户昵称">
+                                    <el-input v-model="formData.user"></el-input>
+                        </el-form-item>
+                        <el-form-item prop="auctionId" label="拍卖活动ID">
+                                    <el-input-number type="number" v-model="formData.auctionId"></el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="name" label="拍卖活动">
+                                    <el-input-number type="number" v-model="formData.name"></el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="auctionPic" label="拍卖活动图片">
+                                    <el-input v-model="formData.auctionPic"></el-input>
+                        </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 prop="purchased" label="是否竞得">
+                                    <el-input v-model="formData.purchased"></el-input>
+                        </el-form-item>
+                        <el-form-item prop="bidderPrice" label="出价">
+                                    <el-input-number type="number" v-model="formData.bidderPrice"></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: 'AuctionRecordEdit',
+        created() {
+            if (this.$route.query.id) {
+                this.$http
+                    .get('auctionRecord/get/' + this.$route.query.id)
+                    .then(res => {
+                        this.formData = res;
+                    })
+                    .catch(e => {
+                        console.log(e);
+                        this.$message.error(e.error);
+                    });
+            }
+        },
+        data() {
+            return {
+                saving: false,
+                formData: {
+                },
+                rules: {
+                },
+                typeOptions: [{"label":"保证金","value":"DEPOSIT"},{"label":"竞拍","value":"BIDDER"},{"label":"一口价","value":"FIXEDPRICE"}],
+            }
+        },
+        methods: {
+            onSave() {
+                this.$refs.form.validate((valid) => {
+                    if (valid) {
+                        this.submit();
+                    } else {
+                        return false;
+                    }
+                });
+            },
+            submit() {
+                let data = {...this.formData};
+
+                this.saving = true;
+                this.$http
+                    .post('/auctionRecord/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(`/auctionRecord/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>

+ 196 - 0
src/main/vue/src/views/AuctionRecordList.vue

@@ -0,0 +1,196 @@
+<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="avatar" label="用户头像"
+>
+                    </el-table-column>
+                    <el-table-column prop="user" label="用户昵称"
+>
+                    </el-table-column>
+                    <el-table-column prop="auctionId" label="拍卖活动ID"
+>
+                    </el-table-column>
+                    <el-table-column prop="name" label="拍卖活动"
+>
+                    </el-table-column>
+                    <el-table-column prop="auctionPic" label="拍卖活动图片"
+>
+                    </el-table-column>
+                    <el-table-column prop="type" label="类型"
+                            :formatter="typeFormatter"
+                        >
+                    </el-table-column>
+                    <el-table-column prop="purchased" label="是否竞得"
+>
+                    </el-table-column>
+                    <el-table-column prop="bidderPrice" 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: 'AuctionRecordList',
+        mixins: [pageableTable],
+        data() {
+            return {
+                multipleMode: false,
+                search: "",
+                url: "/auctionRecord/all",
+                downloading: false,
+                        typeOptions:[{"label":"保证金","value":"DEPOSIT"},{"label":"竞拍","value":"BIDDER"},{"label":"一口价","value":"FIXEDPRICE"}],
+            }
+        },
+        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: "/auctionRecordEdit",
+                    query: {
+                        ...this.$route.query
+                    }
+                });
+            },
+            editRow(row) {
+                this.$router.push({
+                    path: "/auctionRecordEdit",
+                    query: {
+                    id: row.id
+                    }
+                });
+            },
+            download() {
+                this.downloading = true;
+                this.$axios
+                    .get("/auctionRecord/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(`/auctionRecord/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>

+ 260 - 0
src/main/vue/src/views/AuctionUsedList.vue

@@ -0,0 +1,260 @@
+<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>
+            <created-at-picker
+                v-model="endTime"
+                @input="getData"
+                name="拍卖结束"
+                class="filter-item"
+            ></created-at-picker>
+            <el-select v-model="status" clearable class="filter-item" placeholder="请选择状态" @change="getData">
+                <el-option
+                    v-for="item in statusOptions"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                ></el-option>
+            </el-select>
+        </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="90"> </el-table-column>
+            <el-table-column prop="sellerId" label="起拍人ID"> </el-table-column>
+            <el-table-column prop="seller" label="起拍人昵称"> </el-table-column>
+            <!-- <el-table-column prop="sellerAvatar" label="起拍人头像"> </el-table-column> -->
+            <el-table-column prop="name" label="拍卖名称"> </el-table-column>
+            <el-table-column prop="auctionType" label="拍卖类型" :formatter="auctionTypeFormatter"> </el-table-column>
+            <el-table-column prop="minter" label="铸造者"> </el-table-column>
+            <el-table-column prop="assetId" label="藏品ID"> </el-table-column>
+            <el-table-column prop="pic" label="作品内容" width="90" align="center">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.pic[0].thumb || row.pic[0].url"
+                        fit="cover"
+                        :preview-src-list="row.pic.map(i => i.thumb || i.url)"
+                    ></el-image>
+                </template>
+            </el-table-column>
+            <!-- <el-table-column prop="detail" label="详情"> </el-table-column> -->
+            <!-- <el-table-column prop="category" label="分类"> </el-table-column> -->
+            <el-table-column prop="startingPrice" label="起拍价"> </el-table-column>
+            <el-table-column prop="deposit" label="保证金"> </el-table-column>
+            <el-table-column prop="fixedPrice" label="一口价"> </el-table-column>
+            <el-table-column prop="startTime" label="开始时间"> </el-table-column>
+            <el-table-column prop="increment" label="加价幅度"> </el-table-column>
+            <el-table-column prop="endTime" label="截止时间"> </el-table-column>
+            <el-table-column prop="purchasePrice" label="成交价"> </el-table-column>
+            <el-table-column prop="purchaserId" label="买家id"> </el-table-column>
+            <el-table-column prop="purchaser" label="买家"> </el-table-column>
+            <el-table-column prop="status" label="状态" :formatter="statusFormatter"> </el-table-column>
+            <el-table-column prop="source" label="来源" :formatter="sourceFormatter"> </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: 'AuctionActivityList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/auctionActivity/all',
+            downloading: false,
+            auctionTypeOptions: [
+                { label: '虚拟藏品', value: 'NFT' },
+                { label: '实物', value: 'ENTITY' }
+            ],
+            statusOptions: [
+                { label: '未开始', value: 'NOTSTARTED' },
+                { label: '进行中', value: 'ONGOING' },
+                { label: '成交', value: 'PURCHASED' },
+                { label: '一口价成交', value: 'FIXED_PRICE_PURCHASED' },
+                { label: '流拍', value: 'PASS' },
+                { label: '完成', value: 'FINISH' }
+            ],
+            sourceOptions: [
+                { label: '官方拍卖', value: 'OFFICIAL' },
+                { label: '转让拍卖', value: 'TRANSFER' }
+            ],
+            endTime: '',
+            status: ''
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        auctionTypeFormatter(row, column, cellValue, index) {
+            let selectedOption = this.auctionTypeOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        statusFormatter(row, column, cellValue, index) {
+            let selectedOption = this.statusOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        sourceFormatter(row, column, cellValue, index) {
+            let selectedOption = this.sourceOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return {
+                search: this.search,
+                query: { del: false, endTime: this.endTime, status: this.status, source: 'TRANSFER' }
+            };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/auctionActivityEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/auctionActivityEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/auctionActivity/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(`/auctionActivity/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

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

@@ -156,6 +156,7 @@ export default {
                 { label: '首页', value: 'HOME' },
                 { label: '发现页', value: 'DISCOVER' },
                 { label: '铸造者', value: 'MINTER' },
+                { label: '拍卖', value: 'AUCTION' },
                 { label: '二手市场', value: 'MARKET' },
                 { label: 'PC官方活动', value: 'PC_ACT' },
                 { label: 'PC首页大图', value: 'PC_TITLE' }
@@ -164,6 +165,7 @@ export default {
                 { label: '藏品/盲盒', value: 'collection' },
                 { label: '铸造者', value: 'user' },
                 { label: '活动', value: 'activity' },
+                { label: '拍卖', value: 'auctionActivity' },
                 { label: '多个藏品', value: 'collections' },
                 { label: '指定链接', value: 'hyperlink' }
             ],

+ 1 - 0
src/main/vue/src/views/BannerList.vue

@@ -111,6 +111,7 @@ export default {
                 { label: '首页', value: 'HOME' },
                 { label: '发现页', value: 'DISCOVER' },
                 { label: '铸造者', value: 'MINTER' },
+                { label: '拍卖', value: 'AUCTION' },
                 { label: '二手市场', value: 'MARKET' },
                 { label: 'PC官方活动', value: 'PC_ACT' },
                 { label: 'PC首页大图', value: 'PC_TITLE' }

+ 1 - 1
src/main/vue/src/views/MintOrderList.vue

@@ -23,7 +23,7 @@
         <div class="filters-container">
             <created-at-picker v-model="createdAt" @input="getData" name="支付" class="filter-item"></created-at-picker>
             <el-input
-                placeholder="搜索..."
+                placeholder="搜索手机号..."
                 v-model="search"
                 clearable
                 class="filter-item search"

+ 18 - 0
src/test/java/com/izouma/nineth/repo/AuctionRecordRepoTest.java

@@ -0,0 +1,18 @@
+package com.izouma.nineth.repo;
+
+
+import com.izouma.nineth.ApplicationTests;
+import com.izouma.nineth.enums.AuctionType;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class AuctionRecordRepoTest extends ApplicationTests {
+    @Autowired
+    private AuctionRecordRepo auctionRecordRepo;
+
+    @Test
+    public void test() {
+        auctionRecordRepo.findByUserId(9972L, AuctionType.NFT.toString());
+    }
+
+}

+ 24 - 73
src/test/java/com/izouma/nineth/repo/UserPropertyRepoTest.java

@@ -8,6 +8,7 @@ import com.izouma.nineth.domain.UserProperty;
 import com.izouma.nineth.dto.AirDropExcelDTO;
 import com.izouma.nineth.enums.AssetStatus;
 import com.izouma.nineth.utils.excel.UploadDataListener;
+import org.apache.commons.lang3.ObjectUtils;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -169,18 +170,22 @@ public class UserPropertyRepoTest extends ApplicationTests {
 
     @Test
     public void setById() {
-        userPropertyRepo.deleteAll();
+//        userPropertyRepo.deleteAll();
         File file = new File("/Users/sunnianwen/Desktop/all1.xlsx");
+        File file2 = new File("/Users/sunnianwen/Desktop/123.xlsx");
+
         UploadDataListener<AirDropExcelDTO> listener = new UploadDataListener<>();
         List<AirDropExcelDTO> dtos = EasyExcel.read(file, AirDropExcelDTO.class, listener)
                 .sheet()
                 .doReadSync();
+        List<AirDropExcelDTO> dtos2 = EasyExcel.read(file2, AirDropExcelDTO.class, listener)
+                .sheet()
+                .doReadSync();
 //        Set<String> userIds = dtos.stream()
 //                .map(AirDropExcelDTO::getPhone)
 //                .collect(Collectors.toSet());
 //        System.out.println(userIds);
         dtos.forEach(id -> {
-
             UserProperty userProperty = userPropertyRepo.findById(Long.parseLong(id.getPhone()))
                     .orElse(new UserProperty(Long.parseLong(id.getPhone()), 0));
             userProperty.setMaxCount(Integer.parseInt(id.getName()));
@@ -189,6 +194,18 @@ public class UserPropertyRepoTest extends ApplicationTests {
 //            }
             userPropertyRepo.save(userProperty);
         });
+        dtos2.forEach(id -> {
+
+            UserProperty userProperty = userPropertyRepo.findById(Long.parseLong(id.getPhone()))
+                    .orElse(null);
+            if (ObjectUtils.isEmpty(userProperty)) {
+                userProperty = new UserProperty(Long.parseLong(id.getPhone()), Integer.parseInt(id.getName()));
+            } else {
+                userProperty.setMaxCount(userProperty.getMaxCount() + 1);
+            }
+
+            userPropertyRepo.save(userProperty);
+        });
     }
 
     @Test
@@ -226,80 +243,14 @@ public class UserPropertyRepoTest extends ApplicationTests {
     }
 
     @Test
-    public void test() {
+    public void test(){
         Iterable<UserProperty> all = userPropertyRepo.findAll();
-//        System.out.println(all);
-        AtomicInteger sum = new AtomicInteger();
         all.forEach(p -> {
-            sum.getAndIncrement();
+            if (p.getMaxCount() > 1) {
+                p.setMaxCount(p.getMaxCount() - 1);
+                userPropertyRepo.save(p);
+            }
         });
-        System.out.println(sum.get());
-    }
-
-    @Test
-    public void heji() {
-        Map<String, Integer> map = new HashMap<>();
-        UploadDataListener<AirDropExcelDTO> listener = new UploadDataListener<>();
-
-//        File fileLei = new File("/Users/sunnianwen/Desktop/lei.xlsx");
-//        List<AirDropExcelDTO> leis = EasyExcel.read(fileLei, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        leis.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-//
-//        File fileTuan = new File("/Users/sunnianwen/Desktop/tuan.xlsx");
-//        List<AirDropExcelDTO> tuans = EasyExcel.read(fileTuan, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        tuans.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-//
-//        File fileDi = new File("/Users/sunnianwen/Desktop/di.xlsx");
-//        List<AirDropExcelDTO> dis = EasyExcel.read(fileDi, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        dis.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-//
-//        File fileTop1 = new File("/Users/sunnianwen/Desktop/top20_1.xlsx");
-//        List<AirDropExcelDTO> top1s = EasyExcel.read(fileTop1, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        top1s.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-//
-//        File fileTop2 = new File("/Users/sunnianwen/Desktop/top20_2.xlsx");
-//        List<AirDropExcelDTO> top2s = EasyExcel.read(fileTop2, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        top2s.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-//
-//        File vipLou = new File("/Users/sunnianwen/Desktop/vip_man_lou.xlsx");
-//        List<AirDropExcelDTO> lou1s = EasyExcel.read(vipLou, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        lou1s.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-//
-//        File baiLou = new File("/Users/sunnianwen/Desktop/bai_lou.xlsx");
-//        List<AirDropExcelDTO> lou2s = EasyExcel.read(baiLou, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        lou2s.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-//
-//        File vip = new File("/Users/sunnianwen/Desktop/vip.xlsx");
-//        List<AirDropExcelDTO> vips = EasyExcel.read(vip, AirDropExcelDTO.class, listener)
-//                .sheet()
-//                .doReadSync();
-//        vips.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-
-        File all = new File("/Users/sunnianwen/Desktop/all1.xlsx");
-        List<AirDropExcelDTO> alls = EasyExcel.read(all, AirDropExcelDTO.class, listener)
-                .sheet()
-                .doReadSync();
-        alls.forEach(dto -> map.merge(dto.getPhone(), Integer.parseInt(dto.getName()), Integer::sum));
-
-        map.forEach((key, value) -> System.out.println(key + "," + value));
-        AtomicInteger sum = new AtomicInteger();
-        map.forEach((key, value) -> sum.addAndGet(value));
-        System.out.println(sum.get());
     }
 
-
 }

+ 10 - 4
src/test/java/com/izouma/nineth/repo/UserRepoTest.java

@@ -9,6 +9,7 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.test.context.junit4.SpringRunner;
 
 import java.time.LocalDate;
@@ -18,7 +19,10 @@ import java.util.*;
 @SpringBootTest
 public class UserRepoTest {
     @Autowired
-    private UserRepo userRepo;
+    private UserRepo        userRepo;
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+
 
     @Test
     public void testUser() {
@@ -66,8 +70,10 @@ public class UserRepoTest {
 
     @Test
     public void test1() {
-        List<Long> userIds = userRepo.findInvitorByCollectionId(3460186L, 2);
-        System.out.println(userIds.size());
-        userRepo.updateAllByInvitor(userIds);
+//        List<Long> userIds = userRepo.findInvitorByCollectionId(3460186L, 2);
+//        System.out.println(userIds.size());
+//        userRepo.updateAllByInvitor(userIds);
+        User user = userRepo.findById(9972L).orElse(null);
+        System.out.println(passwordEncoder.matches("123456", user.getTradeCode()));
     }
 }

+ 44 - 0
src/test/java/com/izouma/nineth/service/AuctionActivityServiceTest.java

@@ -0,0 +1,44 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.ApplicationTests;
+import com.izouma.nineth.domain.AuctionActivity;
+import com.izouma.nineth.dto.auction.AuctionInputDTO;
+import com.izouma.nineth.repo.AuctionActivityRepo;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+
+public class AuctionActivityServiceTest extends ApplicationTests {
+
+    @Autowired
+    private AuctionActivityService auctionActivityService;
+    @Autowired
+    private AuctionActivityRepo    auctionActivityRepo;
+
+    @Test
+    public void test() {
+        auctionActivityService.init();
+    }
+
+    @Test
+    public void test1() {
+        AuctionActivity activity = auctionActivityRepo.findById(2652491L).orElse(null);
+
+        auctionActivityService.offShelfTask(activity);
+    }
+
+    @Test
+    public void test2() {
+        AuctionInputDTO dto = new AuctionInputDTO();
+        dto.setAssetId(206885L);
+        dto.setDeposit(new BigDecimal("0.1"));
+        dto.setStartTime(LocalDateTime.now());
+        dto.setFixedPrice(new BigDecimal("0.01"));
+        dto.setIncrement(new BigDecimal("0.01"));
+        dto.setTradeCode("123456");
+        auctionActivityService.createFromAsset(dto);
+
+    }
+}

+ 16 - 0
src/test/java/com/izouma/nineth/service/AuctionOrderServiceTest.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.service;
+
+
+import com.izouma.nineth.ApplicationTests;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class AuctionOrderServiceTest extends ApplicationTests {
+    @Autowired
+    private AuctionOrderService auctionOrderService;
+
+    @Test
+    public void test() {
+        auctionOrderService.passOverTimeAuction();
+    }
+}

+ 17 - 0
src/test/java/com/izouma/nineth/service/AuctionRecordServiceTest.java

@@ -0,0 +1,17 @@
+package com.izouma.nineth.service;
+
+
+import com.izouma.nineth.ApplicationTests;
+import com.izouma.nineth.dto.PageQuery;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class AuctionRecordServiceTest extends ApplicationTests {
+    @Autowired
+    private AuctionRecordService auctionRecordService;
+
+    @Test
+    public void test() {
+    }
+
+}

+ 1 - 1
src/test/java/com/izouma/nineth/service/OrderServiceTest.java

@@ -261,7 +261,7 @@ public class OrderServiceTest extends ApplicationTests {
 
     @Test
     public void test1() {
-        orderService.queryCreateOrder("945378720611303424");
+        orderService.queryCreateOrder("1234");
     }
 
     @Test

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini