Преглед изворни кода

Merge branch 'dev' of http://git.izouma.com/xiongzhu/raex_back into dev

wangqifan пре 3 година
родитељ
комит
08fa313ab7
65 измењених фајлова са 1558 додато и 559 уклоњено
  1. 3 1
      src/main/java/com/izouma/nineth/config/CacheConfig.java
  2. 28 0
      src/main/java/com/izouma/nineth/converter/IntArrayConverter.java
  3. 3 0
      src/main/java/com/izouma/nineth/domain/Collection.java
  4. 6 0
      src/main/java/com/izouma/nineth/domain/DestroyRecord.java
  5. 6 0
      src/main/java/com/izouma/nineth/domain/MetaDestroyActivity.java
  6. 13 1
      src/main/java/com/izouma/nineth/domain/MetaTask.java
  7. 20 4
      src/main/java/com/izouma/nineth/domain/MetaTaskToUser.java
  8. 11 0
      src/main/java/com/izouma/nineth/domain/MetaUserTaskProgress.java
  9. 26 0
      src/main/java/com/izouma/nineth/domain/NumberSeq.java
  10. 42 0
      src/main/java/com/izouma/nineth/domain/PublicScreenChat.java
  11. 2 2
      src/main/java/com/izouma/nineth/domain/User.java
  12. 23 0
      src/main/java/com/izouma/nineth/dto/MetaWebsocketMessage.java
  13. 38 0
      src/main/java/com/izouma/nineth/dto/TransactionTopTenDTO.java
  14. 4 4
      src/main/java/com/izouma/nineth/enums/MetaTaskType.java
  15. 18 0
      src/main/java/com/izouma/nineth/enums/OperationSource.java
  16. 2 2
      src/main/java/com/izouma/nineth/repo/MetaDestroyActivityRepo.java
  17. 3 0
      src/main/java/com/izouma/nineth/repo/MetaTaskRepo.java
  18. 0 4
      src/main/java/com/izouma/nineth/repo/MetaTaskToUserRepo.java
  19. 2 0
      src/main/java/com/izouma/nineth/repo/MetaUserTaskProgressRepo.java
  20. 9 0
      src/main/java/com/izouma/nineth/repo/NumberSeqRepo.java
  21. 8 0
      src/main/java/com/izouma/nineth/repo/OrderRepo.java
  22. 9 0
      src/main/java/com/izouma/nineth/repo/PublicScreenChatRepo.java
  23. 11 0
      src/main/java/com/izouma/nineth/security/WebSecurityConfig.java
  24. 40 11
      src/main/java/com/izouma/nineth/service/AssetService.java
  25. 63 63
      src/main/java/com/izouma/nineth/service/MetaTaskToUserService.java
  26. 76 21
      src/main/java/com/izouma/nineth/service/MetaUserTaskProgressService.java
  27. 20 0
      src/main/java/com/izouma/nineth/service/PublicScreenChatService.java
  28. 4 0
      src/main/java/com/izouma/nineth/service/UserAssetSummaryService.java
  29. 1 1
      src/main/java/com/izouma/nineth/service/UserHoldCountCache.java
  30. 2 0
      src/main/java/com/izouma/nineth/utils/excel/ExcelUtils.java
  31. 42 0
      src/main/java/com/izouma/nineth/utils/excel/MetaTaskStatusConverter.java
  32. 43 0
      src/main/java/com/izouma/nineth/utils/excel/MetaTaskTypeConverter.java
  33. 45 2
      src/main/java/com/izouma/nineth/web/AssetController.java
  34. 6 6
      src/main/java/com/izouma/nineth/web/MetaBonusSceneController.java
  35. 14 5
      src/main/java/com/izouma/nineth/web/MetaDestroyActivityController.java
  36. 19 2
      src/main/java/com/izouma/nineth/web/MetaTaskController.java
  37. 5 0
      src/main/java/com/izouma/nineth/web/MetaTaskToUserController.java
  38. 20 0
      src/main/java/com/izouma/nineth/web/MetaUserController.java
  39. 1 1
      src/main/java/com/izouma/nineth/web/MetaUserTaskProgressController.java
  40. 35 0
      src/main/java/com/izouma/nineth/web/PublicScreenChatController.java
  41. 139 0
      src/main/java/com/izouma/nineth/websocket/PublicScreenChatWebsocket.java
  42. 4 23
      src/main/java/com/izouma/nineth/websocket/WebSocket.java
  43. 51 0
      src/main/java/com/izouma/nineth/websocket/WebsocketCommon.java
  44. 1 0
      src/main/resources/genjson/PublicScreenChat.json
  45. BIN
      src/main/resources/static/img/bg2.jpeg
  46. 1 0
      src/main/vue/package.json
  47. 8 1
      src/main/vue/src/main.js
  48. 9 1
      src/main/vue/src/router.js
  49. 3 1
      src/main/vue/src/store.js
  50. 1 1
      src/main/vue/src/views/Admin.vue
  51. 1 7
      src/main/vue/src/views/BlindBoxEdit.vue
  52. 24 27
      src/main/vue/src/views/CollectionEdit.vue
  53. 1 7
      src/main/vue/src/views/CompanyBlindBoxEdit.vue
  54. 7 8
      src/main/vue/src/views/CompanyCollectionEdit.vue
  55. 5 1
      src/main/vue/src/views/CompanyList.vue
  56. 3 0
      src/main/vue/src/views/MetaDestroyActivityEdit.vue
  57. 185 162
      src/main/vue/src/views/MetaDestroyActivityList.vue
  58. 1 1
      src/main/vue/src/views/MetaSpatialInfoList.vue
  59. 191 179
      src/main/vue/src/views/MetaTaskEdit.vue
  60. 15 0
      src/main/vue/src/views/MetaTaskList.vue
  61. 2 0
      src/main/vue/src/views/MetaTaskToUserList.vue
  62. 116 0
      src/main/vue/src/views/PublicScreenChatList.vue
  63. 4 4
      src/main/vue/src/views/UserBalance.vue
  64. 25 0
      src/main/vue/yarn.lock
  65. 38 6
      src/test/java/com/izouma/nineth/CommonTest.java

+ 3 - 1
src/main/java/com/izouma/nineth/config/CacheConfig.java

@@ -142,7 +142,9 @@ public class CacheConfig {
         cacheNamesConfigurationMap.put("blindBoxRare", RedisCacheConfiguration.defaultCacheConfig()
                 .entryTtl(Duration.ofSeconds(2))
                 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())));
-
+        cacheNamesConfigurationMap.put("transactionTopTen", RedisCacheConfiguration.defaultCacheConfig()
+                .entryTtl(Duration.ofDays(1))
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())));
         RedisCacheManager redisCacheManager = RedisCacheManager.builder()
                 .cacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory()))
                 .withInitialCacheConfigurations(cacheNamesConfigurationMap)

+ 28 - 0
src/main/java/com/izouma/nineth/converter/IntArrayConverter.java

@@ -0,0 +1,28 @@
+package com.izouma.nineth.converter;
+
+import org.apache.commons.lang3.StringUtils;
+
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Converter
+public class IntArrayConverter implements AttributeConverter<List<Integer>, String> {
+    @Override
+    public String convertToDatabaseColumn(List<Integer> integers) {
+        if (integers != null && !integers.isEmpty())
+            return StringUtils.join(integers, ",");
+        return null;
+    }
+
+    @Override
+    public List<Integer> convertToEntityAttribute(String s) {
+        if (StringUtils.isNotEmpty(s)) {
+            return Arrays.stream(s.split(",")).map(Integer::parseInt).collect(Collectors.toList());
+        }
+        return new ArrayList<>();
+    }
+}

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

@@ -287,6 +287,7 @@ public class Collection extends CollectionBaseEntity {
     @Column(columnDefinition = "bigint default 1 not null")
     private Long companyId = 1L;
 
+
     @ApiModelProperty("bit 0: 蚂蚁链, bit 1: 华储链")
     @Column(columnDefinition = "int default 3 not null")
     private int chainFlag;
@@ -301,4 +302,6 @@ public class Collection extends CollectionBaseEntity {
     private String newsPic;
 
     private LocalDateTime newsCreatedTime;
+
+    private boolean messNumber;
 }

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

@@ -1,6 +1,8 @@
 package com.izouma.nineth.domain;
 
+import com.izouma.nineth.enums.OperationSource;
 import com.izouma.nineth.enums.RecordType;
+import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -32,4 +34,8 @@ public class DestroyRecord extends BaseEntity {
 
     @Column(columnDefinition = "bigint default 1 not null")
     private Long companyId;
+
+    @ApiModelProperty("操作来源")
+    @Enumerated(EnumType.STRING)
+    private OperationSource source;
 }

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

@@ -18,9 +18,15 @@ public class MetaDestroyActivity extends BaseEntity{
     @ApiModelProperty("藏品id")
     private Long collectionId;
 
+    @ApiModelProperty("图片")
+    private String pic;
+
     @ApiModelProperty("销毁数量配置")
     private int num;
 
+    @ApiModelProperty("用途")
+    private int application;
+
     @ApiModelProperty("是否发布")
     private boolean publish;
 }

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

@@ -1,6 +1,7 @@
 package com.izouma.nineth.domain;
 
 
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.izouma.nineth.annotations.Searchable;
 import com.izouma.nineth.enums.MetaTaskType;
 import io.swagger.annotations.ApiModel;
@@ -23,25 +24,36 @@ public class MetaTask extends BaseEntity{
 
     @ApiModelProperty("任务名称")
     @Searchable
+    @ExcelProperty("任务名称")
     private String name;
 
     @ApiModelProperty("任务详情")
     @Column(columnDefinition = "TEXT")
+    @ExcelProperty("任务详情")
     private String detail;
 
     @ApiModelProperty("任务类型")
     @Enumerated(EnumType.STRING)
+    @ExcelProperty("任务类型")
     private MetaTaskType type;
 
     @ApiModelProperty("参数配置")
+    @ExcelProperty("参数")
     private String value;
 
     @ApiModelProperty("是否发布")
+    @ExcelProperty("是否发布")
     private boolean publish;
 
-    @ApiModelProperty("角标")
+    @ApiModelProperty("是否展示角标")
+    @ExcelProperty("是否展示角标")
     private boolean mark;
 
     @ApiModelProperty("是否结束")
+    @ExcelProperty("是否结束")
     private boolean finish;
+
+    @ApiModelProperty("奖励图片")
+    @ExcelProperty("图片地址")
+    private String awardPic;
 }

+ 20 - 4
src/main/java/com/izouma/nineth/domain/MetaTaskToUser.java

@@ -1,5 +1,7 @@
 package com.izouma.nineth.domain;
 
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.izouma.nineth.annotations.Searchable;
 import com.izouma.nineth.enums.MetaTaskStatus;
 import com.izouma.nineth.enums.MetaTaskType;
@@ -9,10 +11,7 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.Transient;
+import javax.persistence.*;
 import java.time.LocalDateTime;
 
 @Data
@@ -24,31 +23,48 @@ public class MetaTaskToUser extends BaseEntity{
 
     @ApiModelProperty("用户id")
     @Searchable
+    @ExcelProperty("用户id")
     private Long userId;
 
     @ApiModelProperty("领取时间")
+    @ExcelProperty("领取时间")
     private LocalDateTime getTime;
 
     @ApiModelProperty("完成时间")
+    @ExcelProperty("完成时间")
     private LocalDateTime finishTime;
 
     @ApiModelProperty("任务id")
+    @ExcelProperty("任务id")
     @Searchable
     private Long taskId;
 
     @ApiModelProperty("任务名称")
+    @ExcelProperty("任务名称")
     @Searchable
     private String taskName;
 
     @ApiModelProperty("任务类型")
     @Enumerated(EnumType.STRING)
+    @ExcelProperty("任务类型")
     private MetaTaskType taskType;
 
+    @ApiModelProperty("任务详情")
+    @ExcelProperty("任务详情")
+    @Column(columnDefinition = "TEXT")
+    private String detail;
+
+    @ApiModelProperty("奖励图片")
+    @ExcelProperty("奖励图片地址")
+    private String awardPic;
+
     @ApiModelProperty("当前状态")
+    @ExcelProperty("当前状态")
     @Enumerated(EnumType.STRING)
     private MetaTaskStatus status;
 
     @Transient
     @ApiModelProperty("进度")
+    @ExcelIgnore
     private String process;
 }

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

@@ -1,5 +1,6 @@
 package com.izouma.nineth.domain;
 
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.izouma.nineth.enums.MetaTaskType;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -19,21 +20,31 @@ import javax.persistence.Enumerated;
 public class MetaUserTaskProgress extends BaseEntity{
 
     @ApiModelProperty("元宇宙玩家领取任务记录id")
+    @ExcelProperty("玩家领取任务记录id")
     private Long metaTaskToUserId;
 
     @ApiModelProperty("任务id")
+    @ExcelProperty("任务id")
     private Long taskId;
 
     @ApiModelProperty("任务类型")
+    @ExcelProperty("任务类型")
     @Enumerated(EnumType.STRING)
     private MetaTaskType taskType;
 
     @ApiModelProperty("任务名称")
+    @ExcelProperty("任务名称")
     private String taskName;
 
     @ApiModelProperty("用户id")
+    @ExcelProperty("用户id")
     private Long userId;
 
     @ApiModelProperty("备注")
+    @ExcelProperty("备注")
     private String remark;
+
+    @ApiModelProperty("备注参数是否唯一,例如收集彩蛋的任务每个备注(彩蛋id)不允许重复完成")
+    @ExcelProperty("备注参数是否唯一")
+    private boolean remarkOnly;
 }

+ 26 - 0
src/main/java/com/izouma/nineth/domain/NumberSeq.java

@@ -0,0 +1,26 @@
+package com.izouma.nineth.domain;
+
+
+import com.izouma.nineth.converter.IntArrayConverter;
+import lombok.Data;
+
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.List;
+
+@Data
+@Entity
+public class NumberSeq {
+
+    @Id
+    private Long id;
+
+    private int total;
+
+    @Convert(converter = IntArrayConverter.class)
+    @Column(columnDefinition = "text(65535)")
+    private List<Integer> numbers;
+
+}

+ 42 - 0
src/main/java/com/izouma/nineth/domain/PublicScreenChat.java

@@ -0,0 +1,42 @@
+package com.izouma.nineth.domain;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.izouma.nineth.annotations.Searchable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.Transient;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@ApiModel("元宇宙用户聊天信息")
+public class PublicScreenChat extends BaseEntity {
+
+    @ApiModelProperty("发送方姓名")
+    @ExcelProperty("发送方姓名")
+    @Searchable
+    private String fromNickName;
+
+    @ApiModelProperty("发送方id")
+    @ExcelProperty("发送方id")
+    @Searchable
+    private String fromUserId;
+
+    @ApiModelProperty("消息内容")
+    @ExcelProperty("消息内容")
+    private String messageInfo;
+
+    @ApiModelProperty("消息发送时间")
+    @ExcelProperty("消息发送时间")
+    private LocalDateTime time;
+
+    @Transient
+    private boolean myself;
+}

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

@@ -57,10 +57,10 @@ public class User extends UserBaseEntity implements Serializable {
     //    @Pattern(regexp = Constants.Regex.USERNAME)
     @Size(min = 1, max = 50)
     @Column(nullable = false, unique = true)
-    @Searchable
+    @Searchable(mode = SearchMode.EXACT)
     private String username;
 
-    @Searchable
+    @Searchable(mode = SearchMode.EXACT)
     private String nickname;
 
     private String avatar;

+ 23 - 0
src/main/java/com/izouma/nineth/dto/MetaWebsocketMessage.java

@@ -0,0 +1,23 @@
+package com.izouma.nineth.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@ApiModel("元宇宙websocket通用返回消息")
+public class MetaWebsocketMessage {
+
+    @ApiModelProperty("消息类型 1 -> " +
+            "上线 2 -> 下线 " +
+            "3 -> 重复登陆")
+    private Integer type;
+
+    @ApiModelProperty("提示语")
+    private String tip;
+
+}

+ 38 - 0
src/main/java/com/izouma/nineth/dto/TransactionTopTenDTO.java

@@ -0,0 +1,38 @@
+package com.izouma.nineth.dto;
+
+import com.izouma.nineth.converter.FileObjectListConverter;
+import com.izouma.nineth.domain.FileObject;
+import com.izouma.nineth.enums.CollectionSource;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TransactionTopTenDTO {
+
+    private Long id;
+
+    private String name;
+
+    private String prefixName;
+
+    @ApiModelProperty("来源")
+    @Enumerated(EnumType.STRING)
+    private CollectionSource source;
+
+    @ApiModelProperty("图片")
+    @Convert(converter = FileObjectListConverter.class)
+    @Column(columnDefinition = "TEXT")
+    private List<FileObject> pic;
+
+    private int num;
+}

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

@@ -3,15 +3,15 @@ package com.izouma.nineth.enums;
 
 public enum MetaTaskType {
 
-    SIGN_IN_SINGLE_DAY("单日签到 -> 指定单日签到日期"),
+    SIGN_IN_SINGLE_DAY("单日签到"),
 
-    SIGN_IN_CONTINUOUS("连续多日签到 -> 指定开始日期"),
+    SIGN_IN_CONTINUOUS("连续多日签到"),
 
-    COLLECT_COLLECTION("收集藏品 -> 指定藏品id"),
+    COLLECT_COLLECTION("收集藏品"),
 
     ON_LINE_TIME_DAILY("每日在线时长"),
 
-    ACCUMULATE("累计 -> 指定累计数量");
+    ACCUMULATE("累计");
 
     private final String description;
 

+ 18 - 0
src/main/java/com/izouma/nineth/enums/OperationSource.java

@@ -0,0 +1,18 @@
+package com.izouma.nineth.enums;
+
+public enum OperationSource {
+
+    META("元宇宙"),
+
+    RAEX("绿洲");
+
+    private final String description;
+
+    OperationSource(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

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

@@ -16,7 +16,7 @@ public interface MetaDestroyActivityRepo extends JpaRepository<MetaDestroyActivi
     @Transactional
     void softDelete(Long id);
 
-    List<MetaDestroyActivity> findAllByDelAndPublish(boolean del, boolean publish);
+    List<MetaDestroyActivity> findAllByApplicationAndDelAndPublish(int appliation, boolean del, boolean publish);
 
-    MetaDestroyActivity findByCollectionIdAndDel(Long collectionId, boolean del);
+    MetaDestroyActivity findByCollectionIdAndApplicationAndDel(Long collectionId, int application, boolean del);
 }

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

@@ -18,4 +18,7 @@ public interface MetaTaskRepo extends JpaRepository<MetaTask, Long>, JpaSpecific
     MetaTask findByIdAndDel(Long taskId, boolean del);
 
     List<MetaTask> findAllByPublishAndDelAndFinish(boolean publish, boolean del, boolean finish);
+
+    @Query(value = "select * from meta_task where finish = false and del = false and publish = true and id not in (select task_id from meta_task_to_user where user_id = ?1)", nativeQuery = true)
+    List<MetaTask> canGet(Long userId);
 }

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

@@ -9,16 +9,12 @@ 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.List;
 
 public interface MetaTaskToUserRepo extends JpaRepository<MetaTaskToUser, Long>, JpaSpecificationExecutor<MetaTaskToUser> {
 
     MetaTaskToUser findByUserIdAndTaskIdAndDel(Long userId, Long taskId, boolean del);
 
-    @Query(value = "select * from meta_task_to_user where user_id = ?1 and task_id = ?2 and del = false and get_time > ?3", nativeQuery = true)
-    MetaTaskToUser findByCondition(Long userId, Long taskId, LocalDateTime startTime);
-
     MetaTaskToUser findByIdAndDel(Long id, boolean del);
 
     List<MetaTaskToUser> findAllByUserIdAndStatusAndDel(Long userId, MetaTaskStatus status, boolean del);

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

@@ -23,4 +23,6 @@ public interface MetaUserTaskProgressRepo extends JpaRepository<MetaUserTaskProg
 
     List<MetaUserTaskProgress> findAllByMetaTaskToUserIdAndDel(Long metaTaskToUserId, boolean del);
 
+    MetaUserTaskProgress findByMetaTaskToUserIdAndDelAndRemark(Long metaTaskToUserId, boolean del, String remark);
+
 }

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

@@ -0,0 +1,9 @@
+package com.izouma.nineth.repo;
+
+
+import com.izouma.nineth.domain.NumberSeq;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface NumberSeqRepo extends JpaRepository<NumberSeq, Long> {
+
+}

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

@@ -16,6 +16,7 @@ import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 public interface OrderRepo extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order> {
@@ -110,4 +111,11 @@ public interface OrderRepo extends JpaRepository<Order, Long>, JpaSpecificationE
     int countAllByUserIdAndCollectionIdAndStatusIn(Long userId, Long collectionId, Collection<OrderStatus> status);
 
     Order findFirstByCollectionIdOrderByCreatedAtDesc(Long collectionId);
+
+    @Query(value = "SELECT a.id id, a.name name ,a.prefixName prefixName, a.source source, SUM(a.num) num FROM " +
+            "(SELECT a.id id, o.name name,a.prefix_name prefixName, o.source source,COUNT(*) num FROM order_info o LEFT JOIN asset a ON o.asset_id = a.id WHERE o.`status` = 'FINISH' AND o.source = 'TRANSFER' AND o.pay_time > ?1 GROUP BY a.prefix_name " +
+            "UNION ALL" +
+            " SELECT a.id id, o.name name,a.prefix_name prefixName, o.source source,COUNT(*) num FROM order_info o LEFT JOIN collection_info a ON o.collection_id = a.id WHERE o.`status` = 'FINISH' and o.source = 'OFFICIAL' AND o.pay_time > ?1 GROUP BY a.prefix_name) a " +
+            "GROUP BY a.prefixName ORDER BY (SUM(a.num)) DESC LIMIT 10", nativeQuery = true)
+    List<Map<String, String>> transactionTopTen(LocalDateTime payTime);
 }

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

@@ -0,0 +1,9 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.PublicScreenChat;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface PublicScreenChatRepo extends JpaRepository<PublicScreenChat, Long>, JpaSpecificationExecutor<PublicScreenChat> {
+
+}

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

@@ -154,6 +154,17 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/metaTask/**").permitAll()
                 .antMatchers("/asset/destroy").permitAll()
                 .antMatchers("/user/topTen").permitAll()
+                .antMatchers("/metaUserTaskProgress/**").permitAll()
+                .antMatchers("/metaUserGold/**").permitAll()
+                .antMatchers("/metaTask/findAll").permitAll()
+                .antMatchers("/metaTaskToUser/**").permitAll()
+                .antMatchers("/metaTaskActivity/queryPublishActivity").permitAll()
+                .antMatchers("/metaAdvertRecord/**").permitAll()
+                .antMatchers("/metaDestroyActivity/*/metaQuery").permitAll()
+                .antMatchers("/metaResourceVersion/**").permitAll()
+                .antMatchers("/asset/topTen").permitAll()
+                .antMatchers("/metaUser/internalTest").permitAll()
+
                 // all other requests need to be authenticated
                 .anyRequest().authenticated().and()
                 // make sure we use stateless session; session won't be used to

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

@@ -1,6 +1,7 @@
 package com.izouma.nineth.service;
 
 import com.google.common.collect.Lists;
+import com.google.common.hash.Hashing;
 import com.izouma.nineth.TokenHistory;
 import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.config.GeneralProperties;
@@ -35,6 +36,7 @@ import org.springframework.stereotype.Service;
 
 import javax.persistence.criteria.Predicate;
 import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
@@ -71,6 +73,7 @@ public class AssetService {
     private AssetLockRepo           assetLockRepo;
     private UserBalanceService      userBalanceService;
     private PhotoAssetRepo          photoAssetRepo;
+    private NumberSeqRepo           numberSeqRepo;
 
     public Page<Asset> all(PageQuery pageQuery) {
 
@@ -150,6 +153,9 @@ public class AssetService {
 
     public Asset createAsset(Collection collection, User user, Long orderId, BigDecimal price, String type,
                              Integer number, boolean safeFlag) {
+        if (collection.isMessNumber() && number != null) {
+            number = getMessedNumber(collection.getId(), number, collection.getTotal());
+        }
         Asset asset = Asset.create(collection, user);
         asset.setTokenId(TokenUtils.genTokenId());
         asset.setNumber(number);
@@ -248,12 +254,34 @@ public class AssetService {
         return asset;
     }
 
+    public synchronized int getMessedNumber(Long collectionId, int number, int total) {
+        NumberSeq numberSeq = numberSeqRepo.findById(collectionId).orElse(null);
+        if (numberSeq == null || numberSeq.getTotal() != total) {
+            numberSeq = new NumberSeq();
+            numberSeq.setId(collectionId);
+            numberSeq.setTotal(total);
+            Map<Integer, Integer> map = new HashMap<>();
+            for (int i = 0; i < total; i++) {
+                map.put(i, Hashing.md5().hashString(collectionId + ":" + i, StandardCharsets.UTF_8).asInt());
+            }
+            numberSeq.setNumbers(map.entrySet().stream()
+                    .sorted(Map.Entry.comparingByValue())
+                    .map(Map.Entry::getKey)
+                    .collect(Collectors.toList()));
+            numberSeqRepo.save(numberSeq);
+        }
+        return numberSeq.getNumbers().get(number);
+    }
+
     public Asset createAsset(BlindBoxItem winItem, User user, Long orderId, BigDecimal price, String type,
                              Integer number, Integer holdDays, boolean safeFlag) {
         Collection blindBox = collectionRepo.findDetailById(winItem.getBlindBoxId())
                 .orElseThrow(new BusinessException("盲盒不存在"));
         Collection collection = collectionRepo.findDetailById(winItem.getCollectionId())
                 .orElseThrow(new BusinessException("藏品不存在"));
+        if (collection.isMessNumber() && number != null) {
+            number = getMessedNumber(collection.getId(), number, collection.getTotal());
+        }
         winItem.setCollection(collection);
         Asset asset = Asset.create(winItem, user, holdDays);
         asset.setTokenId(TokenUtils.genTokenId());
@@ -884,16 +912,16 @@ public class AssetService {
             if (tags.isEmpty()) return new PageImpl<>(Collections.emptyList());
             return assetRepo.findAll((Specification<Asset>) (root, query, criteriaBuilder) ->
                     query.distinct(true).where(
-                            // where userId=some id
-                            criteriaBuilder.equal(root.get("userId"), userId),
-                            // and (lockTo is null or (lockTo is not null and lockTo < now))
-                            criteriaBuilder.or(criteriaBuilder.isNull(root.get("lockTo")),
-                                    criteriaBuilder.and(criteriaBuilder.isNotNull(root.get("lockTo")),
-                                            criteriaBuilder.lessThan(root.get("lockTo"), LocalDateTime.now()))),
-                            // and status = 'NORMAL'
-                            criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
-                            // and has some tagId
-                            root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
+                                    // where userId=some id
+                                    criteriaBuilder.equal(root.get("userId"), userId),
+                                    // and (lockTo is null or (lockTo is not null and lockTo < now))
+                                    criteriaBuilder.or(criteriaBuilder.isNull(root.get("lockTo")),
+                                            criteriaBuilder.and(criteriaBuilder.isNotNull(root.get("lockTo")),
+                                                    criteriaBuilder.lessThan(root.get("lockTo"), LocalDateTime.now()))),
+                                    // and status = 'NORMAL'
+                                    criteriaBuilder.equal(root.get("status"), AssetStatus.NORMAL),
+                                    // and has some tagId
+                                    root.join("tags").get("id").in(tags.stream().map(Tag::getId).toArray()))
                             .getRestriction(), pageable);
         } else {
             return assetRepo.findByUserIdAndStatusAndNameLike(userId, AssetStatus.NORMAL,
@@ -901,7 +929,7 @@ public class AssetService {
         }
     }
 
-    public void destroy(Long id, Long userId, String tradeCode) {
+    public void destroy(Long id, Long userId, String tradeCode, OperationSource source) {
         Asset asset = assetRepo.findById(id).orElseThrow(new BusinessException("无记录"));
         if (!asset.getUserId().equals(userId)) {
             throw new BusinessException("此藏品不属于你");
@@ -954,6 +982,7 @@ public class AssetService {
                 .record(1)
                 .type(RecordType.OBTAIN)
                 .companyId(asset.getCompanyId())
+                .source(source)
                 .build());
 
         //加积分

+ 63 - 63
src/main/java/com/izouma/nineth/service/MetaTaskToUserService.java

@@ -42,23 +42,23 @@ public class MetaTaskToUserService {
 
     public MetaRestResult<MetaTaskToUser> receiveTask(MetaTaskToUser metaTaskToUser) {
         if (Objects.nonNull(metaTaskToUser.getId())) {
-            return MetaRestResult.returnError("参数不合法:id必须为空");
+            return MetaRestResult.returnError("Illegal parameter : id must be null");
         }
         if (Objects.isNull(metaTaskToUser.getTaskId())) {
-            return MetaRestResult.returnError("参数不合法:任务id不可为空");
+            return MetaRestResult.returnError("Illegal parameter : taskId can not be null");
         }
         if (Objects.isNull(metaTaskToUser.getUserId())) {
-            return MetaRestResult.returnError("参数不合法:用户id不可为空");
+            return MetaRestResult.returnError("Illegal parameter : userId can not be null");
         }
         MetaTask metaTask = metaTaskRepo.findByIdAndDel(metaTaskToUser.getTaskId(), false);
         if (Objects.isNull(metaTask)) {
             return MetaRestResult.returnError(String.format("根据任务id[%S]查询不到任务详情", metaTaskToUser.getTaskId()));
         }
         if (!metaTask.isPublish()) {
-            return MetaRestResult.returnError("当前任务还未发布,不可领取");
+            return MetaRestResult.returnError("status error : task has not been published");
         }
         if (metaTask.isFinish()) {
-            return MetaRestResult.returnError("当前任务已经结束,不可领取");
+            return MetaRestResult.returnError("status error : task has been finished");
         }
         if (StringUtils.isBlank(metaTask.getName())) {
             return MetaRestResult.returnError(String.format("任务[%S]缺少任务名称", metaTaskToUser.getTaskId()));
@@ -66,15 +66,22 @@ public class MetaTaskToUserService {
         if (Objects.isNull(metaTask.getType())) {
             return MetaRestResult.returnError(String.format("任务[%S]缺少任务类型", metaTaskToUser.getTaskId()));
         }
+        if (Objects.isNull(metaTask.getDetail())) {
+            return MetaRestResult.returnError(String.format("任务[%S]缺少任务详情", metaTaskToUser.getTaskId()));
+        }
         // 判断当前玩家是否领取过任务
         MetaTaskToUser dbMetaTaskToUser = metaTaskToUserRepo.findByUserIdAndTaskIdAndDel(metaTaskToUser.getUserId(), metaTaskToUser.getTaskId(), false);
         if (Objects.nonNull(dbMetaTaskToUser)) {
             return MetaRestResult.returnError(String.format("当前用户已在[%S]领取过该任务[%S]", dbMetaTaskToUser.getGetTime(), metaTask.getName()));
         }
+        if (Objects.nonNull(metaTask.getAwardPic())) {
+            metaTaskToUser.setAwardPic(metaTask.getAwardPic());
+        }
         metaTaskToUser.setTaskName(metaTask.getName());
         metaTaskToUser.setTaskType(metaTask.getType());
+        metaTaskToUser.setDetail(metaTask.getDetail());
         metaTaskToUser.setGetTime(LocalDateTime.now());
-        metaTaskToUser.setStatus(MetaTaskStatus.GET);
+        metaTaskToUser.setStatus(MetaTaskStatus.PROGRESS);
         return MetaRestResult.returnSuccess(metaTaskToUserRepo.save(metaTaskToUser));
     }
 
@@ -85,8 +92,18 @@ public class MetaTaskToUserService {
         return MetaRestResult.returnSuccess(metaTaskToUsers);
     }
 
+    public MetaRestResult<MetaTaskToUser> findProgress(Long id) {
+        MetaTaskToUser metaTaskToUsers = metaTaskToUserRepo.findByIdAndDel(id, false);
+        if (Objects.isNull(metaTaskToUsers)) {
+            return MetaRestResult.returnError(String.format("根据id[%S]查询不到用户领取记录", id));
+        }
+        setProgress(metaTaskToUsers);
+        return MetaRestResult.returnSuccess(metaTaskToUsers);
+    }
+
+
     /**
-     * 计算任务进度
+     * 设置任务进度
      *
      * @param metaTaskToUser 参数
      */
@@ -95,98 +112,81 @@ public class MetaTaskToUserService {
         if (Objects.isNull(metaTask)) {
             log.error("任务信息为空");
         }
-        MetaTaskStatus status = metaTaskToUser.getStatus();
         String value = metaTask.getValue();
         switch (metaTaskToUser.getTaskType()) {
             case SIGN_IN_SINGLE_DAY:
-                if (MetaTaskStatus.GET.equals(status)) {
+                List<MetaUserTaskProgress> signInSingleDay = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDel(metaTaskToUser.getId(), false);
+                if (CollectionUtils.isEmpty(signInSingleDay)) {
                     metaTaskToUser.setProcess(PREFIX.concat("1"));
                     break;
                 }
-                setCommonProgress(metaTaskToUser);
+                metaTaskToUser.setProcess(String.valueOf(signInSingleDay.size()).concat(" / 1"));
                 break;
             case SIGN_IN_CONTINUOUS:
-                if (MetaTaskStatus.GET.equals(status)) {
-                    metaTaskToUser.setProcess(PREFIX.concat("7"));
-                    break;
-                }
-                if (MetaTaskStatus.PROGRESS.equals(status)) {
-                    int num = countSignIn(value, metaTaskToUser);
-                    metaTaskToUser.setProcess(String.valueOf(num).concat(" / 7"));
-                    break;
-                }
-                setCommonProgress(metaTaskToUser);
+                int num = countSignIn(value, metaTaskToUser.getId());
+                metaTaskToUser.setProcess(String.valueOf(num).concat(" / 7"));
                 break;
             case COLLECT_COLLECTION:
                 if (StringUtils.isBlank(value)) {
-                    log.error("请先配置藏品id");
+                    String errMsg = "任务进度显示异常,缺少藏品id配置";
+                    log.error(errMsg);
+                    metaTaskToUser.setProcess(errMsg);
                     break;
                 }
-                String[] split = value.split(",");
-                if (MetaTaskStatus.GET.equals(status)) {
+                String[] split = value.split(" ");
+                List<MetaUserTaskProgress> collectCollection = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDel(metaTaskToUser.getId(), false);
+                if (CollectionUtils.isEmpty(collectCollection)) {
                     metaTaskToUser.setProcess(PREFIX.concat(String.valueOf(split.length)));
                     break;
                 }
-                if (MetaTaskStatus.PROGRESS.equals(status)) {
-                    List<MetaUserTaskProgress> metaUserTaskProgresses = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDel(metaTaskToUser.getId(), false);
-                    if (CollectionUtils.isEmpty(metaUserTaskProgresses)) {
-                        metaTaskToUser.setProcess(PREFIX.concat(String.valueOf(split.length)));
-                        break;
-                    }
-                    metaTaskToUser.setProcess(String.valueOf(metaUserTaskProgresses.size()).concat(" / ").concat(String.valueOf(split.length)));
-                }
-                setCommonProgress(metaTaskToUser);
+                metaTaskToUser.setProcess(String.valueOf(collectCollection.size()).concat(" / ").concat(String.valueOf(split.length)));
                 break;
             case ON_LINE_TIME_DAILY:
-                if (MetaTaskStatus.GET.equals(status)) {
-                    metaTaskToUser.setProcess("0");
+                if (StringUtils.isBlank(value)) {
+                    String errMsg = "任务进度显示异常,缺少在线时长配置";
+                    log.error(errMsg);
+                    metaTaskToUser.setProcess(errMsg);
                     break;
                 }
-                if (MetaTaskStatus.PROGRESS.equals(status)) {
-                    MetaUserTaskProgress metaUserTaskProgress = metaUserTaskProgressRepo.findByMetaTaskToUserIdAndDelAndCreatedAtAfter(metaTaskToUser.getId(), false, LocalDateTime.now().withHour(0).withMinute(0).withSecond(0));
-                    if (Objects.isNull(metaUserTaskProgress)) {
-                        metaTaskToUser.setProcess("0");
-                        break;
-                    }
-                    metaTaskToUser.setProcess(metaUserTaskProgress.getRemark());
+                MetaUserTaskProgress onLineTimeDaily = metaUserTaskProgressRepo.findByMetaTaskToUserIdAndDelAndCreatedAtAfter(metaTaskToUser.getId(), false, LocalDateTime.now().withHour(0).withMinute(0).withSecond(0));
+                if (Objects.isNull(onLineTimeDaily)) {
+                    metaTaskToUser.setProcess("0");
+                    break;
                 }
-                setCommonProgress(metaTaskToUser);
+                metaTaskToUser.setProcess(onLineTimeDaily.getRemark().concat(" / ").concat(value));
                 break;
             case ACCUMULATE:
-                if (MetaTaskStatus.GET.equals(status)) {
-                    metaTaskToUser.setProcess(PREFIX.concat(value));
+                if (StringUtils.isBlank(value)) {
+                    String errMsg = "任务进度显示异常,缺少累计任务的数量配置";
+                    log.error(errMsg);
+                    metaTaskToUser.setProcess(errMsg);
                     break;
                 }
-                if (MetaTaskStatus.PROGRESS.equals(status)) {
-                    List<MetaUserTaskProgress> metaUserTaskProgresses = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDel(metaTaskToUser.getId(), false);
-                    if (CollectionUtils.isEmpty(metaUserTaskProgresses)) {
-                        metaTaskToUser.setProcess(PREFIX.concat(value));
-                        break;
-                    }
-                    metaTaskToUser.setProcess(String.valueOf(metaUserTaskProgresses.size()).concat(" / ").concat(value));
+                List<MetaUserTaskProgress> accumulate = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDel(metaTaskToUser.getId(), false);
+                if (CollectionUtils.isEmpty(accumulate)) {
+                    metaTaskToUser.setProcess(PREFIX.concat(value));
+                    break;
                 }
-                setCommonProgress(metaTaskToUser);
+                metaTaskToUser.setProcess(String.valueOf(accumulate.size()).concat(" / ").concat(value));
                 break;
             default:
                 break;
         }
     }
 
-    private void setCommonProgress(MetaTaskToUser metaTaskToUser) {
-        if (MetaTaskStatus.COMPLETION.equals(metaTaskToUser.getStatus())) {
-            metaTaskToUser.setProcess(MetaTaskStatus.COMPLETION.getDescription());
-            return;
-        }
-        if (MetaTaskStatus.FINISH.equals(metaTaskToUser.getStatus())) {
-            metaTaskToUser.setProcess(MetaTaskStatus.FINISH.getDescription());
-        }
-    }
 
-    private int countSignIn(String value, MetaTaskToUser metaTaskToUser) {
+    /**
+     * 计算累计签到天数
+     *
+     * @param value            开始日期配置
+     * @param metaTaskToUserId 用户领取任务记录id
+     * @return 累计签到天数
+     */
+    private int countSignIn(String value, Long metaTaskToUserId) {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         LocalDateTime startTime = LocalDate.parse(value, formatter).atStartOfDay();
         LocalDateTime endTime = LocalDateTime.now();
-        List<MetaUserTaskProgress> metaUserTaskProgresses = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDelAndCreatedAtBetweenOrderByCreatedAtDesc(metaTaskToUser.getId(), false, startTime, endTime);
+        List<MetaUserTaskProgress> metaUserTaskProgresses = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDelAndCreatedAtBetweenOrderByCreatedAtDesc(metaTaskToUserId, false, startTime, endTime);
         int num = 0;
         if (CollectionUtils.isEmpty(metaUserTaskProgresses)) {
             return num;

+ 76 - 21
src/main/java/com/izouma/nineth/service/MetaUserTaskProgressService.java

@@ -7,6 +7,7 @@ import com.izouma.nineth.dto.MetaRestResult;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.enums.MetaTaskStatus;
 import com.izouma.nineth.enums.MetaTaskType;
+import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.MetaTaskRepo;
 import com.izouma.nineth.repo.MetaTaskToUserRepo;
 import com.izouma.nineth.repo.MetaUserTaskProgressRepo;
@@ -17,6 +18,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 
+import javax.transaction.Transactional;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
@@ -40,7 +42,8 @@ public class MetaUserTaskProgressService {
         return metaUserTaskProgressRepo.findAll(JpaUtils.toSpecification(pageQuery, MetaUserTaskProgress.class), JpaUtils.toPageRequest(pageQuery));
     }
 
-    public MetaRestResult<MetaUserTaskProgress> metaSave(MetaUserTaskProgress metaUserTaskProgress) {
+    @Transactional
+    public MetaRestResult<Boolean> metaSave(MetaUserTaskProgress metaUserTaskProgress) {
         // 参数校验
         if (Objects.nonNull(metaUserTaskProgress.getId())) {
             return MetaRestResult.returnError("Illegal parameter : id must be null");
@@ -51,6 +54,16 @@ public class MetaUserTaskProgressService {
         if (Objects.isNull(metaUserTaskProgress.getUserId())) {
             return MetaRestResult.returnError("Illegal parameter : userId can not be null");
         }
+        // 指定备注唯一,校验是否重复
+        if (metaUserTaskProgress.isRemarkOnly()) {
+            if (StringUtils.isBlank(metaUserTaskProgress.getRemark())) {
+                return MetaRestResult.returnError("Illegal parameter : remark can not be null");
+            }
+            MetaUserTaskProgress dbMetaUserTaskProgress = metaUserTaskProgressRepo.findByMetaTaskToUserIdAndDelAndRemark(metaUserTaskProgress.getMetaTaskToUserId(), false, metaUserTaskProgress.getRemark());
+            if (Objects.nonNull(dbMetaUserTaskProgress)) {
+                return MetaRestResult.returnError("重复触发");
+            }
+        }
         MetaTaskToUser metaTaskToUser = metaTaskToUserRepo.findByIdAndDel(metaUserTaskProgress.getMetaTaskToUserId(), false);
         if (Objects.isNull(metaTaskToUser)) {
             return MetaRestResult.returnError("无用户领取任务记录");
@@ -58,13 +71,32 @@ public class MetaUserTaskProgressService {
         if (!metaTaskToUser.getStatus().equals(MetaTaskStatus.PROGRESS)) {
             return MetaRestResult.returnError(String.format("状态错误:当前任务状态为[%S]", metaTaskToUser.getStatus().getDescription()));
         }
+        // 收集藏品类型任务 remark为藏品id 不可为空
         if (metaTaskToUser.getTaskType().equals(MetaTaskType.COLLECT_COLLECTION)) {
             if (StringUtils.isBlank(metaUserTaskProgress.getRemark())) {
-                return MetaRestResult.returnError(String.format("类型[%S的任务状态 remark不可为空", metaTaskToUser.getTaskType().getDescription()));
+                return MetaRestResult.returnError(String.format("类型[%S]的任务状态 remark不可为空", metaTaskToUser.getTaskType().getDescription()));
             }
         }
         MetaTask metaTask = metaTaskRepo.findByIdAndDel(metaTaskToUser.getTaskId(), false);
         String value = metaTask.getValue();
+        // 收集藏品任务
+        if (metaTaskToUser.getTaskType().equals(MetaTaskType.COLLECT_COLLECTION)) {
+            if (StringUtils.isBlank(value)) {
+                return MetaRestResult.returnError("该任务未配置藏品id");
+            }
+            String[] split = value.split(" ");
+            List<String> all = Arrays.asList(split);
+            if (!all.contains(metaUserTaskProgress.getRemark())) {
+                return MetaRestResult.returnError("不在任务规定藏品内");
+            }
+        }
+        // 累计任务
+        if (metaTaskToUser.getTaskType().equals(MetaTaskType.ACCUMULATE)) {
+            if (StringUtils.isBlank(value)) {
+                return MetaRestResult.returnError("该任务未配置达标次数");
+            }
+        }
+        // 单日签到任务
         if (metaTaskToUser.getTaskType().equals(MetaTaskType.SIGN_IN_SINGLE_DAY)) {
             if (StringUtils.isBlank(value)) {
                 return MetaRestResult.returnError("单日签到任务未配置签到日期");
@@ -79,52 +111,75 @@ public class MetaUserTaskProgressService {
                 return MetaRestResult.returnError("该签到任务已经签到过,不允许重复签到");
             }
         }
+        // 在线时长任务 如记录存在则更新remark(每日在线时长) 如不存在则每日首次触发该任务
         if (metaTaskToUser.getTaskType().equals(MetaTaskType.ON_LINE_TIME_DAILY)) {
-            MetaUserTaskProgress metaUserTaskProgresses = metaUserTaskProgressRepo.findByMetaTaskToUserIdAndDelAndCreatedAtAfter(metaTaskToUser.getId(), false, LocalDateTime.now().withHour(0).withMinute(0).withSecond(0));
-            if (Objects.isNull(metaUserTaskProgresses)) {
-                return MetaRestResult.returnError("当日在线时长任务进度已经存在");
+            MetaUserTaskProgress dbMetaUserTaskProgress = metaUserTaskProgressRepo.findByMetaTaskToUserIdAndDelAndCreatedAtAfter(metaTaskToUser.getId(), false, LocalDateTime.now().withHour(0).withMinute(0).withSecond(0));
+            if (Objects.isNull(dbMetaUserTaskProgress)) {
+                save(metaUserTaskProgress, metaTaskToUser);
+                // 该类型任务不存在完成
+                return MetaRestResult.returnSuccess(false);
             }
+            int remark = Integer.parseInt(metaUserTaskProgress.getRemark()) + Integer.parseInt(dbMetaUserTaskProgress.getRemark());
+            dbMetaUserTaskProgress.setRemark(String.valueOf(remark));
+            metaUserTaskProgressRepo.save(dbMetaUserTaskProgress);
+            return MetaRestResult.returnSuccess(false);
         }
+        // 连续签到任务 每天只允许签到一次。remark可为每天签到时间也可以为空,具体判定以记录创建时间为准
         if (metaTaskToUser.getTaskType().equals(MetaTaskType.SIGN_IN_CONTINUOUS)) {
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+            LocalDateTime startTime = LocalDate.parse(value, formatter).atStartOfDay();
+            if (startTime.isAfter(LocalDateTime.now())) {
+                return MetaRestResult.returnError("未到开始日期");
+            }
             MetaUserTaskProgress dbMetaUserTaskProgress = metaUserTaskProgressRepo.findByMetaTaskToUserIdAndDelAndCreatedAtAfter(metaTaskToUser.getId(), false, LocalDateTime.now().withHour(0).withMinute(0).withSecond(0));
             if (Objects.nonNull(dbMetaUserTaskProgress)) {
                 return MetaRestResult.returnError("当日已经存在签到记录,不可重复签到");
             }
+            save(metaUserTaskProgress, metaTaskToUser);
+            // 该类型任务不存在完成
+            return MetaRestResult.returnSuccess(false);
         }
-        metaUserTaskProgress.setTaskId(metaTaskToUser.getTaskId());
-        metaUserTaskProgress.setTaskType(metaTaskToUser.getTaskType());
-        metaUserTaskProgress.setTaskName(metaTaskToUser.getTaskName());
-        MetaUserTaskProgress save = metaUserTaskProgressRepo.save(metaUserTaskProgress);
+        // 保存进度
+        save(metaUserTaskProgress, metaTaskToUser);
         List<MetaUserTaskProgress> metaUserTaskProgresses = metaUserTaskProgressRepo.findAllByMetaTaskToUserIdAndDel(metaTaskToUser.getId(), false);
         // 是否完成任务
         switch (metaTaskToUser.getTaskType()) {
             case COLLECT_COLLECTION:
-                if (StringUtils.isBlank(value)) {
-                    return MetaRestResult.returnError("该任务未配置藏品id");
-                }
-                String[] split = value.split(",");
+                String[] split = value.split(" ");
                 List<String> all = Arrays.asList(split);
                 List<String> completed = metaUserTaskProgresses.stream().map(MetaUserTaskProgress::getRemark).collect(Collectors.toList());
                 if (new HashSet<>(completed).containsAll(all)) {
                     completeTask(metaTaskToUser);
+                    return MetaRestResult.returnSuccess(true);
                 }
-                break;
+                return MetaRestResult.returnSuccess(false);
             case SIGN_IN_SINGLE_DAY:
                 // 单日登陆任务签到成功后直接完成
                 completeTask(metaTaskToUser);
-                break;
+                return MetaRestResult.returnSuccess(true);
             case ACCUMULATE:
-                if (StringUtils.isBlank(value)) {
-                    return MetaRestResult.returnError("该任务未配置达标次数");
-                }
                 if (metaUserTaskProgresses.size() >= Integer.parseInt(value)) {
                     completeTask(metaTaskToUser);
+                    return MetaRestResult.returnSuccess(true);
                 }
-                break;
+                return MetaRestResult.returnSuccess(false);
             default:
-                break;
+                throw new BusinessException(String.format("不存在的任务类型[%S]", metaTaskToUser.getTaskType()));
         }
-        return MetaRestResult.returnSuccess(save);
+    }
+
+
+    /**
+     * 保存任务进度
+     *
+     * @param metaUserTaskProgress 参数
+     * @param metaTaskToUser       参数
+     */
+    private void save(MetaUserTaskProgress metaUserTaskProgress, MetaTaskToUser metaTaskToUser) {
+        metaUserTaskProgress.setTaskId(metaTaskToUser.getTaskId());
+        metaUserTaskProgress.setTaskType(metaTaskToUser.getTaskType());
+        metaUserTaskProgress.setTaskName(metaTaskToUser.getTaskName());
+        metaUserTaskProgressRepo.save(metaUserTaskProgress);
     }
 
     /**

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

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

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

@@ -1,5 +1,6 @@
 package com.izouma.nineth.service;
 
+import com.izouma.nineth.annotations.RedisLock;
 import com.izouma.nineth.domain.Asset;
 import com.izouma.nineth.domain.UserAssetSummary;
 import com.izouma.nineth.dto.PageQuery;
@@ -14,6 +15,7 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 
+import javax.transaction.Transactional;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -30,6 +32,8 @@ public class UserAssetSummaryService {
         return userAssetSummaryRepo.findAll(JpaUtils.toSpecification(pageQuery, UserAssetSummary.class), JpaUtils.toPageRequest(pageQuery));
     }
 
+    @Transactional
+    @RedisLock("#userId")
     public void calculateNum(Long userId, Long companyId) {
         log.info("开始重新计算用户:{},companyId:{}的资产数量", userId, companyId);
         List<UserAssetSummary> userAssetSummaries = new ArrayList<>();

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

@@ -84,7 +84,7 @@ public class UserHoldCountCache {
             // 该系列没有寄售的话取originalPrice
             if (Objects.isNull(minPrice)) {
                 Collection collection = collectionRepo.findById(asset.getCollectionId()).orElse(null);
-                if (Objects.isNull(collection)) {
+                if (Objects.isNull(collection) || Objects.isNull(collection.getOriginalPrice())) {
                     minPrice = BigDecimal.ZERO;
                 } else {
                     minPrice = collection.getOriginalPrice();

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

@@ -22,6 +22,8 @@ public class ExcelUtils<T> {
                 .registerConverter(new AuthStatusConverter())
                 .registerConverter(new OrderStatusConverter())
                 .registerConverter(new PayMethodConverter())
+                .registerConverter(new MetaTaskTypeConverter())
+                .registerConverter(new MetaTaskStatusConverter())
                 .doWrite(data);
     }
 }

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

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

+ 43 - 0
src/main/java/com/izouma/nineth/utils/excel/MetaTaskTypeConverter.java

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

+ 45 - 2
src/main/java/com/izouma/nineth/web/AssetController.java

@@ -1,13 +1,19 @@
 package com.izouma.nineth.web;
 
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONArray;
 import com.fasterxml.jackson.annotation.JsonView;
 import com.izouma.nineth.TokenHistory;
 import com.izouma.nineth.domain.Asset;
+import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.GiftOrder;
 import com.izouma.nineth.dto.*;
+import com.izouma.nineth.enums.CollectionSource;
 import com.izouma.nineth.enums.CollectionType;
+import com.izouma.nineth.enums.OperationSource;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.AssetRepo;
+import com.izouma.nineth.repo.CollectionRepo;
 import com.izouma.nineth.repo.OrderRepo;
 import com.izouma.nineth.service.AssetService;
 import com.izouma.nineth.service.CacheService;
@@ -21,6 +27,7 @@ import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.cache.annotation.Cacheable;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
@@ -42,6 +49,7 @@ public class AssetController extends BaseController {
     private OrderRepo               orderRepo;
     private CacheService            cacheService;
     private UserAssetSummaryService userAssetSummaryService;
+    private CollectionRepo collectionRepo;
 
     //@PreAuthorize("hasRole('ADMIN')")
 //    @PostMapping("/save")
@@ -194,8 +202,8 @@ public class AssetController extends BaseController {
 
     @ApiOperation("销毁")
     @PostMapping("/destroy")
-    public void destroy(@RequestParam Long id, @RequestParam String tradeCode) {
-        assetService.destroy(id, SecurityUtils.getAuthenticatedUser().getId(), tradeCode);
+    public void destroy(@RequestParam Long id, @RequestParam String tradeCode, @RequestParam OperationSource source) {
+        assetService.destroy(id, SecurityUtils.getAuthenticatedUser().getId(), tradeCode, source);
     }
 
     @ApiOperation("开盲盒")
@@ -240,6 +248,41 @@ public class AssetController extends BaseController {
     public void recal(@RequestParam Long userId) {
         userAssetSummaryService.calculateNum(userId, 1L);
     }
+
+    @GetMapping("/topTen")
+    @Cacheable(value = "transactionTopTen")
+    public List<TransactionTopTenDTO> transactionTopTen() {
+        LocalDateTime localDateTime = LocalDateTime.now().plusDays(-7);
+        List<Map<String, String>> map = orderRepo.transactionTopTen(localDateTime);
+        JSONArray jsonArray = new JSONArray();
+        if (CollectionUtil.isEmpty(map)) {
+            return null;
+        }
+        jsonArray.addAll(map);
+        List<TransactionTopTenDTO> transactionTopTenDTOS = jsonArray.toJavaList(TransactionTopTenDTO.class);
+        if (CollectionUtil.isEmpty(transactionTopTenDTOS)) {
+            return null;
+        }
+        transactionTopTenDTOS.forEach(transactionTopTenDTO -> {
+            if (Objects.nonNull(transactionTopTenDTO.getId())) {
+                if (transactionTopTenDTO.getSource().equals(CollectionSource.OFFICIAL)) {
+                    Collection collection = collectionRepo.findById(transactionTopTenDTO.getId()).orElse(null);
+                    if (Objects.nonNull(collection)) {
+                        transactionTopTenDTO.setPic(collection.getPic());
+                    }
+                }
+                if (transactionTopTenDTO.getSource().equals(CollectionSource.TRANSFER)) {
+                    Asset asset = assetRepo.findById(transactionTopTenDTO.getId()).orElse(null);
+                    if (Objects.nonNull(asset)) {
+                        transactionTopTenDTO.setPic(asset.getPic());
+                    }
+                }
+            }
+            transactionTopTenDTO.setId(null);
+            transactionTopTenDTO.setSource(null);
+        });
+        return transactionTopTenDTOS;
+    }
 }
 
 

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

@@ -33,22 +33,22 @@ public class MetaBonusSceneController {
     }
 
     @PostMapping("/save")
-    public MetaRestResult<Boolean> save(@RequestBody MetaBonusScene metaBonusScene) {
+    public MetaRestResult<Integer> save(@RequestBody MetaBonusScene metaBonusScene) {
         if (Objects.nonNull(metaBonusScene.getId())) {
-            return MetaRestResult.returnSuccess("参数错误:id必须为空", false);
+            return MetaRestResult.returnError("Illegal parameter : id must be null");
         }
         if (Objects.isNull(metaBonusScene.getUserId())) {
-            return MetaRestResult.returnError("参数错误:缺少用户id", false);
+            return MetaRestResult.returnError("Illegal parameter : userId can not be null");
         }
         if (!(0 < metaBonusScene.getButton())) {
-            return MetaRestResult.returnError("参数错误:缺少按钮信息", false);
+            return MetaRestResult.returnError("Illegal parameter : button can not be null");
         }
         MetaBonusScene dbMetaBonusScene = metaBonusSceneRepo.findByUserIdAndButton(metaBonusScene.getUserId(), metaBonusScene.getButton());
         if (Objects.nonNull(dbMetaBonusScene)) {
-            return MetaRestResult.returnError("该玩家已经触发过同一个彩蛋,无法多次触发", false);
+            return MetaRestResult.returnError("the same bonus scene cannot be triggered multiple times");
         }
         metaBonusSceneRepo.save(metaBonusScene);
-        return MetaRestResult.returnSuccess("触发成功", true);
+        return MetaRestResult.returnSuccess(Integer.parseInt(String.valueOf(metaBonusSceneRepo.countNum())));
     }
 
     @GetMapping("/count")

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

@@ -1,9 +1,11 @@
 package com.izouma.nineth.web;
 
+import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.MetaDestroyActivity;
 import com.izouma.nineth.dto.MetaRestResult;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.CollectionRepo;
 import com.izouma.nineth.repo.MetaDestroyActivityRepo;
 import com.izouma.nineth.service.MetaDestroyActivityService;
 import com.izouma.nineth.utils.ObjUtils;
@@ -25,16 +27,23 @@ public class MetaDestroyActivityController extends BaseController {
     private MetaDestroyActivityService metaDestroyActivityService;
     private MetaDestroyActivityRepo metaDestroyActivityRepo;
 
+    private CollectionRepo collectionRepo;
+
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public MetaDestroyActivity save(@RequestBody MetaDestroyActivity record) {
-        MetaDestroyActivity metaDestroyActivity = metaDestroyActivityRepo.findByCollectionIdAndDel(record.getCollectionId(), false);
+        Collection collection = collectionRepo.findById(record.getCollectionId()).orElseThrow(new BusinessException("不存在该藏品"));
+        MetaDestroyActivity metaDestroyActivity = metaDestroyActivityRepo.findByCollectionIdAndApplicationAndDel(record.getCollectionId(), record.getApplication(), false);
         if (Objects.nonNull(metaDestroyActivity) && !Objects.equals(metaDestroyActivity.getId(), record.getId())) {
-            throw new BusinessException("已经存在该collectionId的相关配置!");
+            throw new BusinessException("该用途已经存在此藏品的相关配置!");
+        }
+        if (CollectionUtils.isNotEmpty(collection.getPic())) {
+            record.setPic(collection.getPic().get(0).getUrl());
         }
         if (record.getId() != null) {
             MetaDestroyActivity orig = metaDestroyActivityRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
             ObjUtils.merge(orig, record);
+            orig.setPic(collection.getPic().get(0).getUrl());
             return metaDestroyActivityRepo.save(orig);
         }
         return metaDestroyActivityRepo.save(record);
@@ -64,9 +73,9 @@ public class MetaDestroyActivityController extends BaseController {
         ExcelUtils.export(response, data);
     }
 
-    @GetMapping("/metaQuery")
-    public MetaRestResult<List<MetaDestroyActivity>> metaQuery() {
-        List<MetaDestroyActivity> metaDestroyActivities = metaDestroyActivityRepo.findAllByDelAndPublish(false, true);
+    @GetMapping("/{application}/metaQuery")
+    public MetaRestResult<List<MetaDestroyActivity>> metaQuery(@PathVariable int application) {
+        List<MetaDestroyActivity> metaDestroyActivities = metaDestroyActivityRepo.findAllByApplicationAndDelAndPublish(application, false, true);
         if (CollectionUtils.isEmpty(metaDestroyActivities)) {
             return MetaRestResult.returnError("查询不到销毁活动的配置");
         }

+ 19 - 2
src/main/java/com/izouma/nineth/web/MetaTaskController.java

@@ -3,7 +3,9 @@ package com.izouma.nineth.web;
 import com.izouma.nineth.domain.MetaTask;
 import com.izouma.nineth.dto.MetaRestResult;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.MetaTaskType;
 import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.CollectionRepo;
 import com.izouma.nineth.repo.MetaTaskRepo;
 import com.izouma.nineth.repo.MetaTaskToUserRepo;
 import com.izouma.nineth.service.MetaTaskService;
@@ -15,6 +17,7 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -27,9 +30,17 @@ public class MetaTaskController extends BaseController {
 
     private MetaTaskToUserRepo metaTaskToUserRepo;
 
+    private CollectionRepo collectionRepo;
+
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public MetaTask save(@RequestBody MetaTask record) {
+        if (record.getType().equals(MetaTaskType.COLLECT_COLLECTION)) {
+            String[] split = record.getValue().split(" ");
+            Arrays.stream(split).forEach(collectionId -> {
+                collectionRepo.findByIdAndDelFalse(Long.parseLong(collectionId)).orElseThrow(new BusinessException(String.format("不存在collectionId[%S]的藏品", collectionId)));
+            });
+        }
         if (record.getId() != null) {
             MetaTask orig = metaTaskRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
             ObjUtils.merge(orig, record);
@@ -68,6 +79,12 @@ public class MetaTaskController extends BaseController {
         return MetaRestResult.returnSuccess(metaTasks);
     }
 
+    @GetMapping("/{userId}/canGet")
+    public MetaRestResult<List<MetaTask>> canGet(@PathVariable Long userId) {
+        List<MetaTask> metaTasks = metaTaskRepo.canGet(userId);
+        return MetaRestResult.returnSuccess(metaTasks);
+    }
+
     @PostMapping("/{id}/finish")
     private void finish(@PathVariable Long id) {
         MetaTask metaTask = metaTaskRepo.findByIdAndDel(id, false);
@@ -77,8 +94,8 @@ public class MetaTaskController extends BaseController {
         metaTask.setFinish(true);
         metaTaskRepo.save(metaTask);
 
-        // TODO 是否要将用户领取记录改为结束
-        // metaTaskToUserRepo.updateStatus(id);
+        // 将用户领取记录改为结束
+        metaTaskToUserRepo.updateStatus(id);
     }
 
 }

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

@@ -74,4 +74,9 @@ public class MetaTaskToUserController {
     public MetaRestResult<List<MetaTaskToUser>> findByStatus(@PathVariable Long userId, @PathVariable MetaTaskStatus status) {
         return metaTaskToUserService.findByStatus(userId, status);
     }
+
+    @GetMapping("/{id}/findProgress")
+    public MetaRestResult<MetaTaskToUser> findProgress(@PathVariable Long id) {
+        return metaTaskToUserService.findProgress(id);
+    }
 }

+ 20 - 0
src/main/java/com/izouma/nineth/web/MetaUserController.java

@@ -1,9 +1,12 @@
 package com.izouma.nineth.web;
 
 import com.izouma.nineth.domain.MetaUser;
+import com.izouma.nineth.domain.SysConfig;
+import com.izouma.nineth.dto.MetaRestResult;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.MetaUserRepo;
+import com.izouma.nineth.repo.SysConfigRepo;
 import com.izouma.nineth.service.MetaUserService;
 import com.izouma.nineth.utils.ObjUtils;
 import com.izouma.nineth.utils.excel.ExcelUtils;
@@ -23,6 +26,10 @@ public class MetaUserController extends BaseController {
     private MetaUserService metaUserService;
     private MetaUserRepo metaUserRepo;
 
+    private SysConfigRepo sysConfigRepo;
+
+    private final String INTERNAL_TEST = "internal_test";
+
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public MetaUser save(@RequestBody MetaUser record) {
@@ -64,5 +71,18 @@ public class MetaUserController extends BaseController {
         List<MetaUser> data = all(pageQuery).getContent();
         ExcelUtils.export(response, data);
     }
+
+    @GetMapping("/internalTest")
+    public MetaRestResult<Boolean> internalTest() {
+        SysConfig sysConfig = sysConfigRepo.findByName(INTERNAL_TEST).orElse(null);
+        if (Objects.isNull(sysConfig)) {
+            return MetaRestResult.returnError("缺少内测开关配置");
+        }
+        String value = sysConfig.getValue();
+        if ("1".equals(value)) {
+            return MetaRestResult.returnSuccess(Boolean.TRUE);
+        }
+        return MetaRestResult.returnSuccess(Boolean.FALSE);
+    }
 }
 

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

@@ -60,7 +60,7 @@ public class MetaUserTaskProgressController extends BaseController {
     }
 
     @PostMapping("/metaSave")
-    public MetaRestResult<MetaUserTaskProgress> metaSave(@RequestBody MetaUserTaskProgress metaUserTaskProgress) {
+    public MetaRestResult<Boolean> metaSave(@RequestBody MetaUserTaskProgress metaUserTaskProgress) {
         return metaUserTaskProgressService.metaSave(metaUserTaskProgress);
     }
 

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

@@ -0,0 +1,35 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.PublicScreenChat;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.service.PublicScreenChatService;
+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("/publicScreenChat")
+@AllArgsConstructor
+public class PublicScreenChatController extends BaseController {
+    private PublicScreenChatService publicScreenChatService;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<PublicScreenChat> all(@RequestBody PageQuery pageQuery) {
+        return publicScreenChatService.all(pageQuery);
+    }
+
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<PublicScreenChat> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 139 - 0
src/main/java/com/izouma/nineth/websocket/PublicScreenChatWebsocket.java

@@ -0,0 +1,139 @@
+package com.izouma.nineth.websocket;
+
+import com.alibaba.fastjson.JSON;
+import com.izouma.nineth.domain.PublicScreenChat;
+import com.izouma.nineth.dto.MetaWebsocketMessage;
+import com.izouma.nineth.repo.PublicScreenChatRepo;
+import com.izouma.nineth.utils.ApplicationContextUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.time.LocalDateTime;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Service
+@ServerEndpoint(value = "/websocket/public/screen/{nickName}/{userId}")
+@Slf4j
+public class PublicScreenChatWebsocket extends WebsocketCommon {
+
+    /**
+     * 当前在线的客户端map
+     */
+    private static final Map<String, Session> clients = new ConcurrentHashMap();
+
+    private PublicScreenChatRepo publicScreenChatRepo;
+
+    private final String PREFIX = "meta-chat:";
+
+    private void init() {
+        if (Objects.isNull(publicScreenChatRepo)) {
+            publicScreenChatRepo = (PublicScreenChatRepo) ApplicationContextUtil.getBean("publicScreenChatRepo");
+        }
+    }
+
+    @OnOpen
+    public void onOpen(@PathParam("nickName") String nickName, @PathParam("userId") String userId, Session session) {
+        init();
+        // 判断当前玩家是否在其他地方登陆
+        if (clients.containsKey(PREFIX.concat(userId))) {
+            String msg = String.format("玩家[%S]已在别处登陆,sessionId为[%S],正在为您关闭本连接", userId, session.getId());
+            log.info(msg);
+            // 关闭连接
+            MetaWebsocketMessage message = new MetaWebsocketMessage();
+            message.setType(3);
+            message.setTip(msg);
+            sendMessageTo(clients, JSON.toJSONString(message), PREFIX.concat(userId));
+            try {
+                log.info("关闭上次登陆的session连接");
+                clients.get(PREFIX.concat(userId)).close();
+            } catch (Exception e) {
+                log.error("session close throw exception:", e);
+            }
+        }
+        log.info("现在来连接的sessionId:" + session.getId() + "玩家id:" + userId + "玩家昵称" + nickName);
+        clients.put(PREFIX.concat(userId), session);
+        PublicScreenChat publicScreenChat = new PublicScreenChat();
+        publicScreenChat.setFromNickName(nickName);
+        publicScreenChat.setFromUserId(userId);
+        publicScreenChat.setMessageInfo(String.format("玩家[%S][%S]进入大厅", userId, nickName));
+        publicScreenChat.setTime(LocalDateTime.now());
+        PublicScreenChat save = publicScreenChatRepo.save(publicScreenChat);
+        sendMessageToOther(clients, JSON.toJSONString(save), PREFIX.concat(userId));
+    }
+
+    @OnError
+    public void onError(Session session, Throwable error) {
+        // 异常处理
+        log.error(String.format("sessionId[%S]的服务端发生了错误:[%S]", session.getId(), error.getMessage()));
+    }
+
+    @OnClose
+    public void onClose(@PathParam("nickName") String nickName, @PathParam("userId") String userId, Session session) {
+        init();
+        log.info(String.format("sessionId:[%S] userId:[%S] is closed", session.getId(), userId));
+        PublicScreenChat publicScreenChat = new PublicScreenChat();
+        publicScreenChat.setFromNickName(nickName);
+        publicScreenChat.setFromUserId(userId);
+        publicScreenChat.setMessageInfo(String.format("玩家[%S][%S]离开大厅", userId, nickName));
+        publicScreenChat.setTime(LocalDateTime.now());
+        PublicScreenChat save = publicScreenChatRepo.save(publicScreenChat);
+        sendMessageToOther(clients, JSON.toJSONString(save), PREFIX.concat(userId));
+        clients.remove(PREFIX.concat(userId));
+    }
+
+    @OnMessage
+    public void onMessage(@PathParam("nickName") String nickName, @PathParam("userId") String userId, String message) {
+        init();
+        if (StringUtils.isBlank(message)) {
+            log.error("Illegal parameter : message can not be null");
+            return;
+        }
+        PublicScreenChat publicScreenChat = JSON.parseObject(message, PublicScreenChat.class);
+        if (Objects.isNull(publicScreenChat)) {
+            log.error("metaChatResultMessage can not be null");
+            return;
+        }
+        if (StringUtils.isBlank(publicScreenChat.getMessageInfo())) {
+            log.error("Illegal parameter : messageInfo can not be null");
+            return;
+        }
+        if (StringUtils.isBlank(publicScreenChat.getFromNickName())) {
+            publicScreenChat.setFromNickName(nickName);
+        }
+        if (StringUtils.isBlank(publicScreenChat.getFromUserId())) {
+            publicScreenChat.setFromUserId(userId);
+        }
+        publicScreenChat.setTime(LocalDateTime.now());
+        // 消息入库
+        PublicScreenChat save = publicScreenChatRepo.save(publicScreenChat);
+        sendMessageToAll(clients, JSON.toJSONString(save), PREFIX.concat(userId));
+    }
+
+    /**
+     * 给所有用户发消息
+     *
+     * @param clients 在线客户端
+     * @param message 消息
+     */
+    public void sendMessageToAll(Map<String, Session> clients, String message, String userId) {
+        PublicScreenChat publicScreenChat = JSON.parseObject(message, PublicScreenChat.class);
+        Set<String> userIds = clients.keySet();
+        userIds.forEach(id -> {
+            try {
+                publicScreenChat.setMyself(id.equals(userId));
+                log.info(String.format("服务器给所有在线用户发送消息,当前在线人员为[%S]。消息:[%S]", id, JSON.toJSONString(publicScreenChat)));
+                clients.get(id).getBasicRemote().sendText(JSON.toJSONString(publicScreenChat));
+            } catch (Exception e) {
+                log.error(String.format("send message [%S] to [%S] throw exception [%S]:", JSON.toJSONString(publicScreenChat), id, e));
+            }
+        });
+    }
+}
+

+ 4 - 23
src/main/java/com/izouma/nineth/websocket/WebSocket.java

@@ -29,7 +29,7 @@ import java.util.stream.Collectors;
 @Service
 @ServerEndpoint(value = "/websocket/mmo/{nickName}/{userId}")
 @Slf4j
-public class WebSocket {
+public class WebSocket extends WebsocketCommon{
 
     /**
      * 当前在线的客户端map
@@ -61,7 +61,7 @@ public class WebSocket {
             MMOSingleMessage mmoSingleMessage = new MMOSingleMessage();
             mmoSingleMessage.setMessageType(6);
             mmoSingleMessage.setMessage("您已在其他地方登录,已为您关闭上次登陆信息");
-            sendMessageTo(JSON.toJSONString(mmoSingleMessage), REDIS_PREFIX.concat(userId));
+            sendMessageTo(clients, JSON.toJSONString(mmoSingleMessage), REDIS_PREFIX.concat(userId));
             try {
                 log.info("关闭上次登陆的session连接");
                 clients.get(REDIS_PREFIX.concat(userId)).close();
@@ -84,7 +84,7 @@ public class WebSocket {
         mmoMessage.setMessageType(5);
         mmoMessage.setMessage(save);
         log.info(String.format("通知玩家[%S],本次登陆信息[%S]", userId, JSON.toJSONString(mmoMessage)));
-        sendMessageTo(JSON.toJSONString(mmoMessage), REDIS_PREFIX.concat(userId));
+        sendMessageTo(clients, JSON.toJSONString(mmoMessage), REDIS_PREFIX.concat(userId));
     }
 
     @OnError
@@ -280,26 +280,7 @@ public class WebSocket {
         MMOMessage mmoMessage = new MMOMessage();
         mmoMessage.setMessageType(messageType);
         mmoMessage.setMap(metaMMOLoginInfos);
-        sendMessageTo(JSON.toJSONString(mmoMessage), userId);
-    }
-
-    /**
-     * 给指定用户分发消息
-     *
-     * @param message  消息体
-     * @param toUserId 用户id
-     */
-    public void sendMessageTo(String message, String toUserId) {
-        if (!clients.containsKey(toUserId)) {
-            log.error("session信息不存在");
-            return;
-        }
-        log.info(String.format("给指定的在线用户发送消息,当前在线人员为[%S]。消息:[%S]", toUserId, message));
-        try {
-            clients.get(toUserId).getBasicRemote().sendText(message);
-        } catch (Exception e) {
-            log.error(String.format("send message [%S] to [%S] throw exception [%S]:", message, toUserId, e));
-        }
+        sendMessageTo(clients, JSON.toJSONString(mmoMessage), userId);
     }
 
     /**

+ 51 - 0
src/main/java/com/izouma/nineth/websocket/WebsocketCommon.java

@@ -0,0 +1,51 @@
+package com.izouma.nineth.websocket;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.websocket.Session;
+import java.util.Map;
+import java.util.Set;
+
+@Slf4j
+public class WebsocketCommon {
+
+    /**
+     * 给指定用户分发消息
+     *
+     * @param message  消息体
+     * @param toUserId 用户id
+     */
+    public void sendMessageTo(Map<String, Session> clients, String message, String toUserId) {
+        if (!clients.containsKey(toUserId)) {
+            log.error("session信息不存在");
+            return;
+        }
+        log.info(String.format("给指定的在线用户发送消息,当前在线人员为[%S]。消息:[%S]", toUserId, message));
+        try {
+            clients.get(toUserId).getBasicRemote().sendText(message);
+        } catch (Exception e) {
+            log.error(String.format("send message [%S] to [%S] throw exception [%S]:", message, toUserId, e));
+        }
+    }
+
+    /**
+     * 给所有用户发消息
+     *
+     * @param clients 在线客户端
+     * @param message 消息
+     */
+    public void sendMessageToOther(Map<String, Session> clients, String message, String userId) {
+        Set<String> userIds = clients.keySet();
+        userIds.forEach(id -> {
+            try {
+                if (!id.equals(userId)) {
+                    log.info(String.format("服务器给所有在线用户发送消息,当前在线人员为[%S]。消息:[%S]", id, message));
+                    clients.get(id).getBasicRemote().sendText(message);
+                }
+            } catch (Exception e) {
+                log.error(String.format("send message [%S] to [%S] throw exception [%S]:", message, id, e));
+            }
+        });
+    }
+}

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

@@ -0,0 +1 @@
+{"tableName":"PublicScreenChat","className":"PublicScreenChat","remark":"公屏对话","genTable":true,"genClass":true,"genList":true,"genForm":false,"genRouter":true,"javaPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/java/com/izouma/nineth","viewPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src/views","routerPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/vue/src","resourcesPath":"/Users/xiaohuoban/IdeaProjects/raex_back/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"fromNickName","modelName":"fromNickName","remark":"用户昵称","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"fromUserId","modelName":"fromUserId","remark":"用户id","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"messageInfo","modelName":"messageInfo","remark":"消息","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"time","modelName":"time","remark":"发送消息的时间","showInList":true,"showInForm":true,"formType":"datetime"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.PublicScreenChat"}

BIN
src/main/resources/static/img/bg2.jpeg


+ 1 - 0
src/main/vue/package.json

@@ -25,6 +25,7 @@
     "jdenticon": "^3.1.0",
     "normalize.css": "^8.0.1",
     "qs": "^6.9.0",
+    "query-string": "^7.1.1",
     "randomstring": "^1.2.1",
     "resolve-url": "^0.2.1",
     "swiper": "^6.8.1",

+ 8 - 1
src/main/vue/src/main.js

@@ -23,6 +23,8 @@ import CollectionCoupon from '@/components/CollectionCoupon';
 import CreatedAtPicker from '@/components/CreatedAtPicker';
 import { mapGetters } from 'vuex';
 import Vant from 'vant';
+import queryString from 'query-string';
+
 import 'vant/lib/index.css';
 
 import 'normalize.css/normalize.css';
@@ -81,8 +83,13 @@ Vue.mixin({
 });
 Vue.prototype.$theme = theme;
 console.log(theme);
-new Vue({
+const app = new Vue({
     router,
     store,
     render: h => h(App)
 }).$mount('#app');
+
+let query = queryString.parse(location.search);
+if (query.companyId) {
+    sessionStorage.setItem('companyId', query.companyId);
+}

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

@@ -1228,7 +1228,15 @@ const router = new Router({
                     meta: {
                         title: '相册',
                     },
-                }
+                },
+                {
+                    path: '/publicScreenChatList',
+                    name: 'PublicScreenChatList',
+                    component: () => import(/* webpackChunkName: "publicScreenChatList" */ '@/views/PublicScreenChatList.vue'),
+                    meta: {
+                       title: '公屏对话',
+                    },
+               }
                 /**INSERT_LOCATION**/
             ]
         },

+ 3 - 1
src/main/vue/src/store.js

@@ -23,7 +23,9 @@ export default new Vuex.Store({
     actions: {},
     getters: {
         companyId(state) {
-            if (state.userInfo) {
+            if (sessionStorage.getItem('companyId')) {
+                return sessionStorage.getItem('companyId');
+            } else if (state.userInfo) {
                 return state.userInfo.companyId || 1;
             }
             return 1;

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

@@ -176,7 +176,7 @@ export default {
             findActiveMenu([], this.rawMenus);
         },
         getMenus() {
-            if (this.userInfo.authorities.find(i => i.name === 'ROLE_SAAS')) {
+            if (this.isCompany) {
                 let menus = [
                     {
                         id: '3410316',

+ 1 - 7
src/main/vue/src/views/BlindBoxEdit.vue

@@ -186,13 +186,7 @@
                             ></el-date-picker>
                         </div>
                     </el-form-item>
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>

+ 24 - 27
src/main/vue/src/views/CollectionEdit.vue

@@ -188,6 +188,10 @@
                             <el-option label="蚂蚁链" :value="1" :key="1"></el-option>
                         </el-select>
                     </el-form-item>
+                    <el-form-item prop="messNumber" label="编号乱序">
+                        <el-radio v-model="formData.messNumber" :label="true">是</el-radio>
+                        <el-radio v-model="formData.messNumber" :label="false">否</el-radio>
+                    </el-form-item>
                     <!-- <el-form-item prop="likes" label="点赞">
                         <el-input-number v-model="formData.likes"></el-input-number>
                     </el-form-item> -->
@@ -220,13 +224,7 @@
                         </el-form-item>
                     </div>
 
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>
@@ -338,19 +336,19 @@
                     </el-form-item>
                     <el-form-item prop="newsId" label="新闻">
                         <el-select v-model="formData.newsId" placeholder="请选择">
-                        <el-option
-                            v-for="item in newsOptions"
-                            :key="item.id"
-                            :label="item.title"
-                            :value="item.id"
-                            class="minter-item"
-                        >
-<!--                            <el-image :src="item.pic" fit="cover" class="avatar"></el-image>-->
-                            <div class="content">
-                                <div class="name">{{ item.title }}</div>
-                                <div class="id">#{{ item.subTitle }}</div>
-                            </div>
-                        </el-option>
+                            <el-option
+                                v-for="item in newsOptions"
+                                :key="item.id"
+                                :label="item.title"
+                                :value="item.id"
+                                class="minter-item"
+                            >
+                                <!--                            <el-image :src="item.pic" fit="cover" class="avatar"></el-image>-->
+                                <div class="content">
+                                    <div class="name">{{ item.title }}</div>
+                                    <div class="id">#{{ item.subTitle }}</div>
+                                </div>
+                            </el-option>
                         </el-select>
                     </el-form-item>
                     <el-form-item class="form-submit">
@@ -435,12 +433,10 @@ export default {
                 }
                 return resolve();
             }),
-            this.$http
-                .post('/news/all', { size: 10000, query: { del: false } }, { body: 'json' })
-                .then(res => {
-                    this.newsOptions = res.content;
-                    return Promise.resolve();
-                }),
+            this.$http.post('/news/all', { size: 10000, query: { del: false } }, { body: 'json' }).then(res => {
+                this.newsOptions = res.content;
+                return Promise.resolve();
+            }),
             this.$http
                 .post('/privilegeOption/all', { size: 10000, query: { del: false } }, { body: 'json' })
                 .then(res => {
@@ -484,7 +480,8 @@ export default {
                 assignment: 0,
                 couponPayment: false,
                 chainFlag: 3,
-                vip: false
+                vip: false,
+                messNumber: false
             },
             rules: {
                 name: [

+ 1 - 7
src/main/vue/src/views/CompanyBlindBoxEdit.vue

@@ -186,13 +186,7 @@
                             ></el-date-picker>
                         </div>
                     </el-form-item>
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>

+ 7 - 8
src/main/vue/src/views/CompanyCollectionEdit.vue

@@ -188,6 +188,10 @@
                             <el-option label="蚂蚁链" :value="1" :key="1"></el-option>
                         </el-select>
                     </el-form-item>
+                    <el-form-item prop="messNumber" label="编号乱序">
+                        <el-radio v-model="formData.messNumber" :label="true">是</el-radio>
+                        <el-radio v-model="formData.messNumber" :label="false">否</el-radio>
+                    </el-form-item>
                     <!-- <el-form-item prop="likes" label="点赞">
                         <el-input-number v-model="formData.likes"></el-input-number>
                     </el-form-item> -->
@@ -220,13 +224,7 @@
                         </el-form-item>
                     </div>
 
-                    <el-form-item
-                        prop="salable"
-                        label="可售"
-                        v-if="
-                            formData.onShelf === true || (formData.scanCode === true && formData.scheduleSale === false)
-                        "
-                    >
+                    <el-form-item prop="salable" label="可售">
                         <el-radio v-model="formData.salable" :label="true">是</el-radio>
                         <el-radio v-model="formData.salable" :label="false">仅展示</el-radio>
                     </el-form-item>
@@ -459,7 +457,8 @@ export default {
                 scanCode: false,
                 noSoldOut: true,
                 assignment: 0,
-                couponPayment: false
+                couponPayment: false,
+                messNumber: false
             },
             rules: {
                 name: [

+ 5 - 1
src/main/vue/src/views/CompanyList.vue

@@ -62,11 +62,12 @@
                     <el-tag :type="row.disabled ? '' : 'info'">{{ row.disabled }}</el-tag>
                 </template>
             </el-table-column>
-            <el-table-column label="操作" align="center" fixed="right" width="220">
+            <el-table-column label="操作" align="center" fixed="right" width="300">
                 <template slot-scope="{ row }">
                     <el-button @click="adminMgmt(row)" size="mini">管理员</el-button>
                     <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
                     <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                    <el-button @click="admin(row)" size="mini" type="">后台</el-button>
                 </template>
             </el-table-column>
         </el-table>
@@ -195,6 +196,9 @@ export default {
                     id: row.id
                 }
             });
+        },
+        admin(row) {
+            window.open(`../?companyId=${row.id}&from=super`, '_blank');
         }
     }
 };

+ 3 - 0
src/main/vue/src/views/MetaDestroyActivityEdit.vue

@@ -22,6 +22,9 @@
 					<el-form-item prop="num" label="数量配置">
 						<el-input-number type="number" v-model="formData.num"> </el-input-number>
 					</el-form-item>
+					<el-form-item prop="application" label="用途">
+						<el-input-number type="application" v-model="formData.application"> </el-input-number>
+					</el-form-item>
 					<el-form-item prop="publish" label="是否发布">
 						<el-switch v-model="formData.publish"> </el-switch>
 					</el-form-item>

+ 185 - 162
src/main/vue/src/views/MetaDestroyActivityList.vue

@@ -1,172 +1,195 @@
 <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="collectionId" label="藏品id"
->
-                    </el-table-column>
-                    <el-table-column prop="num" label="数量配置"
->
-                    </el-table-column>
-                    <el-table-column prop="publish" label="是否发布"
->
-                            <template slot-scope="{row}">
-                                <el-tag :type="row.publish?'':'info'">{{row.publish}}</el-tag>
-                            </template>
-                    </el-table-column>
-            <el-table-column
-                    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>
+	<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="collectionId" label="藏品id"> </el-table-column>
+            <el-table-column prop="pic" label="图片">
+				<template slot-scope="{ row }">
+					<el-image
+						style="width: 30px; height: 30px"
+						:src="row.pic"
+						fit="cover"
+						:preview-src-list="[row.pic]"
+					>
+					</el-image>
+				</template>
+			</el-table-column>
+			<el-table-column prop="num" label="数量配置"> </el-table-column>
+            <el-table-column prop="application" label="用途"> </el-table-column>
+			<el-table-column prop="publish" label="是否发布">
+				<template slot-scope="{ row }">
+					<el-tag :type="row.publish ? '' : 'info'"> {{ row.publish }} </el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column 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 @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>
+			<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";
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
 
-    export default {
-        name: 'MetaDestroyActivityList',
-        mixins: [pageableTable],
-        data() {
-            return {
-                multipleMode: false,
-                search: "",
-                url: "/metaDestroyActivity/all",
-                downloading: false,
-            }
-        },
-        computed: {
-            selection() {
-                return this.$refs.table.selection.map(i => i.id);
-            }
-        },
-        methods: {
-            beforeGetData() {
-                return { search: this.search, query: { del: false } };
-            },
-            toggleMultipleMode(multipleMode) {
-                this.multipleMode = multipleMode;
-                if (!multipleMode) {
-                    this.$refs.table.clearSelection();
-                }
-            },
-            addRow() {
-                this.$router.push({
-                    path: "/metaDestroyActivityEdit",
-                    query: {
-                        ...this.$route.query
-                    }
-                });
-            },
-            editRow(row) {
-                this.$router.push({
-                    path: "/metaDestroyActivityEdit",
-                    query: {
-                    id: row.id
-                    }
-                });
-            },
-            download() {
-                this.downloading = true;
-                this.$axios
-                    .get("/metaDestroyActivity/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(`/metaDestroyActivity/del/${row.id}`)
-                }).then(() => {
-                    this.$message.success('删除成功');
-                    this.getData();
-                }).catch(e => {
-                    if (e !== 'cancel') {
-                        this.$message.error(e.error);
-                    }
-                })
-            },
-        }
-    }
+export default {
+	name: 'MetaDestroyActivityList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/metaDestroyActivity/all',
+			downloading: false
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		beforeGetData() {
+			return { search: this.search, query: { del: false } };
+		},
+		toggleMultipleMode(multipleMode) {
+			this.multipleMode = multipleMode;
+			if (!multipleMode) {
+				this.$refs.table.clearSelection();
+			}
+		},
+		addRow() {
+			this.$router.push({
+				path: '/metaDestroyActivityEdit',
+				query: {
+					...this.$route.query
+				}
+			});
+		},
+		editRow(row) {
+			this.$router.push({
+				path: '/metaDestroyActivityEdit',
+				query: {
+					id: row.id
+				}
+			});
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/metaDestroyActivity/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(`/metaDestroyActivity/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>

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

@@ -129,7 +129,7 @@ export default {
 			return '';
 		},
 		beforeGetData() {
-			return { search: this.search, query: { del: false } };
+			return { search: this.search, query: { del: false }, sort:'id,asc' };
 		},
 		toggleMultipleMode(multipleMode) {
 			this.multipleMode = multipleMode;

+ 191 - 179
src/main/vue/src/views/MetaTaskEdit.vue

@@ -1,191 +1,203 @@
 <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: 500px"
-				>
-					<el-form-item prop="name" label="任务名称">
-						<el-input v-model="formData.name"> </el-input>
-					</el-form-item>
-					<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="type" label="任务类型">
-						<el-select v-model="formData.type" clearable filterable placeholder="请选择" @change="change">
-							<el-option
-								v-for="item in typeOptions"
-								:key="item.value"
-								:label="item.label"
-								:value="item.value"
-							>
-							</el-option>
-						</el-select>
-					</el-form-item>
-					<template v-if="formData.type">
-						<el-form-item
-							v-if="formData.type === 'SIGN_IN_SINGLE_DAY'"
-							prop="value"
-							label="签到日期"
-						>
-							<el-date-picker
-								v-model="formData.value"
-								type="date"
-								value-format="yyyy-MM-dd"
-								placeholder="指定签到日期"
-							>
-							</el-date-picker>
-						</el-form-item>
-						<el-form-item
-							v-if="formData.type === 'SIGN_IN_CONTINUOUS'"
-							prop="value"
-							label="开始日期"
-						>
-							<el-date-picker
-								v-model="formData.value"
-								type="date"
-								value-format="yyyy-MM-dd"
-								placeholder="指定开始日期"
-							>
-							</el-date-picker>
-						</el-form-item>
-						<el-form-item prop="value" label="藏品id" v-if="formData.type === 'COLLECT_COLLECTION'">
-							<el-input v-model="formData.value" placeholder="指定藏品id,多个用'','隔开"> </el-input>
-						</el-form-item>
-						<el-form-item prop="value" label="次数" v-if="formData.type === 'ACCUMULATE'">
-							<el-input-number v-model="formData.value" :min="0"> </el-input-number>
-						</el-form-item>
-					</template>
-					<el-form-item prop="publish" label="是否发布">
-						<el-switch v-model="formData.publish"> </el-switch>
-					</el-form-item>
-					<el-form-item prop="mark" label="是否展示角标">
-						<el-switch v-model="formData.mark"> </el-switch>
-					</el-form-item>
-					<el-form-item class="form-submit">
-						<el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
-						<el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
-							删除
-						</el-button>
-						<el-button @click="$router.go(-1)" :disabled="saving"> 取消 </el-button>
-					</el-form-item>
-				</el-form>
-			</div>
-		</div>
-	</div>
+    <div class="edit-view">
+        <page-title>
+            <el-button @click="$router.go(-1)" :disabled="saving"> 取消 </el-button>
+            <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id"> 删除 </el-button>
+            <el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+        </page-title>
+        <div class="edit-view__content-wrapper">
+            <div class="edit-view__content-section">
+                <el-form
+                    :model="formData"
+                    :rules="rules"
+                    ref="form"
+                    label-width="130px"
+                    label-position="right"
+                    size="small"
+                    style="max-width: 500px"
+                >
+                    <el-form-item prop="name" label="任务名称">
+                        <el-input v-model="formData.name"> </el-input>
+                    </el-form-item>
+                    <el-form-item prop="detail" label="详情" style="width: calc(100vw - 450px)">
+                        <el-input
+                            v-model="formData.detail"
+                            type="textarea"
+                            :autosize="{ minRows: 3, maxRows: 20 }"
+                            placeholder="请输入任务详情"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item prop="awardPic" label="奖励图片">
+                        <single-upload v-model="formData.awardPic"> </single-upload>
+                    </el-form-item>
+                    <el-form-item prop="type" label="任务类型">
+                        <el-select v-model="formData.type" clearable filterable placeholder="请选择" @change="change">
+                            <el-option
+                                v-for="item in typeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <template v-if="formData.type">
+                        <el-form-item v-if="formData.type === 'SIGN_IN_SINGLE_DAY'" prop="value" label="签到日期">
+                            <el-date-picker
+                                v-model="formData.value"
+                                type="date"
+                                value-format="yyyy-MM-dd"
+                                placeholder="指定签到日期"
+                            >
+                            </el-date-picker>
+                        </el-form-item>
+                        <el-form-item v-if="formData.type === 'SIGN_IN_CONTINUOUS'" prop="value" label="开始日期">
+                            <el-date-picker
+                                v-model="formData.value"
+                                type="date"
+                                value-format="yyyy-MM-dd"
+                                placeholder="指定开始日期"
+                            >
+                            </el-date-picker>
+                        </el-form-item>
+                        <el-form-item prop="value" label="藏品id" v-if="formData.type === 'COLLECT_COLLECTION'">
+                            <el-input v-model="formData.value" placeholder="请输入定藏品id"> </el-input>
+                            <div class="tip">多个藏品id请用空格隔开 例如 111 222 333</div>
+                        </el-form-item>
+                        <el-form-item prop="value" label="次数" v-if="formData.type === 'ACCUMULATE'">
+                            <el-input-number v-model="formData.value" :min="0"> </el-input-number>
+                        </el-form-item>
+                        <el-form-item prop="value" label="在线时长(min)" v-if="formData.type === 'ON_LINE_TIME_DAILY'">
+                            <el-input-number v-model="formData.value" :min="0"> </el-input-number>
+                        </el-form-item>
+                    </template>
+                    <el-form-item prop="publish" label="是否发布">
+                        <el-switch v-model="formData.publish"> </el-switch>
+                    </el-form-item>
+                    <el-form-item prop="mark" label="是否展示角标">
+                        <el-switch v-model="formData.mark"> </el-switch>
+                    </el-form-item>
+                    <el-form-item class="form-submit">
+                        <el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+                        <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                            删除
+                        </el-button>
+                        <el-button @click="$router.go(-1)" :disabled="saving"> 取消 </el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+    </div>
 </template>
 <script>
 export default {
-	name: 'MetaTaskEdit',
-	created() {
-		if (this.$route.query.id) {
-			this.$http
-				.get('metaTask/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: {
-				name: [
-					{
-						required: true,
-						message: '请输入任务名称',
-						trigger: 'blur'
-					}
-				],
-				description: [
-					{
-						required: true,
-						message: '请输入任务详情',
-						trigger: 'blur'
-					}
-				],
-				type: [
-					{
-						required: true,
-						message: '请输入任务类型',
-						trigger: 'blur'
-					}
-				]
-			},
-			typeOptions: [
-				{ label: '单日签到', value: 'SIGN_IN_SINGLE_DAY' },
-				{ label: '连续多日签到', value: 'SIGN_IN_CONTINUOUS' },
-				{ label: '收集藏品', value: 'COLLECT_COLLECTION' },
-				{ label: '每日在线时长', value: 'ON_LINE_TIME_DAILY' },
-				{ label: '累计', value: 'ACCUMULATE' }
-			]
-		};
-	},
-	methods: {
+    name: 'MetaTaskEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('metaTask/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: {
+                name: [
+                    {
+                        required: true,
+                        message: '请输入任务名称',
+                        trigger: 'blur'
+                    }
+                ],
+                description: [
+                    {
+                        required: true,
+                        message: '请输入任务详情',
+                        trigger: 'blur'
+                    }
+                ],
+                type: [
+                    {
+                        required: true,
+                        message: '请输入任务类型',
+                        trigger: 'blur'
+                    }
+                ],
+                value: [
+                    {
+                        required: true,
+                        message: '请指定参数配置',
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            typeOptions: [
+                { label: '单日签到', value: 'SIGN_IN_SINGLE_DAY' },
+                { label: '连续多日签到', value: 'SIGN_IN_CONTINUOUS' },
+                { label: '收集藏品', value: 'COLLECT_COLLECTION' },
+                { label: '每日在线时长', value: 'ON_LINE_TIME_DAILY' },
+                { label: '累计', value: 'ACCUMULATE' }
+            ]
+        };
+    },
+    methods: {
         change() {
             if (this.formData.value) {
-                this.formData.value = undefined
+                this.formData.value = undefined;
             }
         },
-		onSave() {
-			this.$refs.form.validate(valid => {
-				if (valid) {
-					this.submit();
-				} else {
-					return false;
-				}
-			});
-		},
-		submit() {
-			let data = { ...this.formData };
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
 
-			this.saving = true;
-			this.$http
-				.post('/metaTask/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(`/metaTask/del/${this.formData.id}`);
-				})
-				.then(() => {
-					this.$message.success('删除成功');
-					this.$router.go(-1);
-				})
-				.catch(e => {
-					if (e !== 'cancel') {
-						console.log(e);
-						this.$message.error((e || {}).error || '删除失败');
-					}
-				});
-		}
-	}
+            this.saving = true;
+            this.$http
+                .post('/metaTask/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(`/metaTask/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>

+ 15 - 0
src/main/vue/src/views/MetaTaskList.vue

@@ -49,6 +49,17 @@
 			<el-table-column prop="type" label="任务类型" :formatter="typeFormatter"> </el-table-column>
 			<el-table-column prop="value" label="配置值"> </el-table-column>
 			<el-table-column prop="detail" label="详情"> </el-table-column>
+			<el-table-column prop="awardPic" label="奖励图片">
+				<template slot-scope="{ row }">
+					<el-image
+						style="width: 30px; height: 30px"
+						:src="row.awardPic"
+						fit="cover"
+						:preview-src-list="[row.awardPic]"
+					>
+					</el-image>
+				</template>
+			</el-table-column>
 			<el-table-column prop="publish" label="是否发布">
 				<template slot-scope="{ row }">
 					<el-tag :type="row.publish ? '' : 'info'"> {{ row.publish }} </el-tag>
@@ -181,6 +192,10 @@ export default {
 			this.$message('操作2');
 		},
 		deleteRow(row) {
+			if(!row.finish) {
+				this.$alert('请先结束任务!', '警告', { type: 'error' })
+				return;
+			}
 			this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
 				.then(() => {
 					return this.$http.post(`/metaTask/del/${row.id}`);

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

@@ -39,6 +39,8 @@
             <el-table-column prop="getTime" label="领取时间"> </el-table-column>
 			<el-table-column prop="finishTime" label="完成时间"> </el-table-column>
 			<el-table-column prop="taskId" label="任务id"> </el-table-column>
+			<el-table-column prop="taskName" label="任务名称"> </el-table-column>
+			<el-table-column prop="detail" label="详情"> </el-table-column>
 			<el-table-column prop="taskType" label="任务类型" :formatter="taskTypeFormatter"> </el-table-column>
 			<el-table-column prop="status" label="当前状态" :formatter="taskStatusFormatter"> </el-table-column>
 		</el-table>

+ 116 - 0
src/main/vue/src/views/PublicScreenChatList.vue

@@ -0,0 +1,116 @@
+<template>
+	<div class="list-view">
+		<page-title>
+			<el-button
+				@click="download"
+				icon="el-icon-upload2"
+				:loading="downloading"
+				:disabled="fetchingData"
+				class="filter-item"
+			>
+				导出
+			</el-button>
+		</page-title>
+		<div class="filters-container">
+			<el-input
+				placeholder="搜索..."
+				v-model="search"
+				clearable
+				class="filter-item search"
+				@keyup.enter.native="getData"
+			>
+				<el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
+			</el-input>
+		</div>
+		<el-table
+			:data="tableData"
+			row-key="id"
+			ref="table"
+			header-row-class-name="table-header-row"
+			header-cell-class-name="table-header-cell"
+			row-class-name="table-row"
+			cell-class-name="table-cell"
+			:height="tableHeight"
+			v-loading="fetchingData"
+		>
+			<el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+			<el-table-column prop="fromNickName" label="用户昵称"> </el-table-column>
+			<el-table-column prop="fromUserId" label="用户id"> </el-table-column>
+			<el-table-column prop="messageInfo" label="消息内容"> </el-table-column>
+			<el-table-column prop="time" label="消息发送时间"> </el-table-column>
+		</el-table>
+		<div class="pagination-wrapper">
+			<el-pagination
+				background
+				@size-change="onSizeChange"
+				@current-change="onCurrentChange"
+				:current-page="page"
+				:page-sizes="[10, 20, 30, 40, 50]"
+				:page-size="pageSize"
+				layout="total, sizes, prev, pager, next, jumper"
+				:total="totalElements"
+			>
+			</el-pagination>
+		</div>
+	</div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+	name: 'PublicScreenChatList',
+	mixins: [pageableTable],
+	data() {
+		return {
+			multipleMode: false,
+			search: '',
+			url: '/publicScreenChat/all',
+			downloading: false
+		};
+	},
+	computed: {
+		selection() {
+			return this.$refs.table.selection.map(i => i.id);
+		}
+	},
+	methods: {
+		beforeGetData() {
+			return { search: this.search, query: { del: false } };
+		},
+		toggleMultipleMode(multipleMode) {
+			this.multipleMode = multipleMode;
+			if (!multipleMode) {
+				this.$refs.table.clearSelection();
+			}
+		},
+		download() {
+			this.downloading = true;
+			this.$axios
+				.get('/publicScreenChat/excel', {
+					responseType: 'blob',
+					params: { size: 10000 }
+				})
+				.then(res => {
+					console.log(res);
+					this.downloading = false;
+					const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+					const link = document.createElement('a');
+					link.href = downloadUrl;
+					link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+					document.body.appendChild(link);
+					link.click();
+					link.remove();
+				})
+				.catch(e => {
+					console.log(e);
+					this.downloading = false;
+					this.$message.error(e.error);
+				});
+		}
+	}
+};
+</script>
+<style lang="less" scoped>
+
+</style>

+ 4 - 4
src/main/vue/src/views/UserBalance.vue

@@ -12,7 +12,7 @@
                 <el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
             </el-input>
             <template v-if="$store.state.userInfo && $store.state.userInfo.username === 'xiong'">
-                <el-popover class="filter-item" placement="bottom" width="350" trigger="click" v-model="showSettlePop">
+                <!-- <el-popover class="filter-item" placement="bottom" width="350" trigger="click" v-model="showSettlePop">
                     <el-date-picker
                         type="daterange"
                         value-format="yyyy-MM-dd"
@@ -63,7 +63,7 @@
                 </el-button>
                 <el-button class="filter-item" type="primary" @click="showImportDialog = true" size="mini">
                     导入结果
-                </el-button>
+                </el-button> -->
             </template>
         </div>
         <el-table
@@ -81,10 +81,10 @@
             <el-table-column prop="userId" label="用户ID"></el-table-column>
             <el-table-column prop="balance" label="余额"></el-table-column>
             <el-table-column prop="locked" label="锁定" width="100" align="center">
-                <template slot="header" slot-scope="{ column }">
+                <template v-slot:header="{ column }">
                     <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
                 </template>
-                <template v-slot="{ row }">
+                <template v-slot:default="{ row }">
                     <el-tag :type="row.locked ? 'danger' : 'info'">{{ row.locked ? '是' : '否' }}</el-tag>
                 </template>
             </el-table-column>

+ 25 - 0
src/main/vue/yarn.lock

@@ -4154,6 +4154,11 @@ fill-range@^7.0.1:
   dependencies:
     to-regex-range "^5.0.1"
 
+filter-obj@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
+  integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
+
 finalhandler@1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
@@ -6987,6 +6992,16 @@ query-string@^4.1.0:
     object-assign "^4.1.0"
     strict-uri-encode "^1.0.0"
 
+query-string@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.npmmirror.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1"
+  integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==
+  dependencies:
+    decode-uri-component "^0.2.0"
+    filter-obj "^1.1.0"
+    split-on-first "^1.0.0"
+    strict-uri-encode "^2.0.0"
+
 querystring-es3@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -7695,6 +7710,11 @@ spdy@^4.0.2:
     select-hose "^2.0.0"
     spdy-transport "^3.0.0"
 
+split-on-first@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
+  integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
+
 split-string@^3.0.1, split-string@^3.0.2:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@@ -7813,6 +7833,11 @@ strict-uri-encode@^1.0.0:
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
   integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==
 
+strict-uri-encode@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+  integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
+
 string-width@^3.0.0, string-width@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"

+ 38 - 6
src/test/java/com/izouma/nineth/CommonTest.java

@@ -1,23 +1,22 @@
+
 package com.izouma.nineth;
 
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.serializer.SerializerFeature;
-import com.alipay.api.AlipayApiException;
-import com.alipay.api.internal.util.AlipaySignature;
 import com.fasterxml.jackson.annotation.JsonView;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.github.kevinsawicki.http.HttpRequest;
 import com.google.common.base.Splitter;
+import com.google.common.hash.Hashing;
 import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.dto.SandPaySettle;
 import com.izouma.nineth.dto.UserWithdraw;
 import com.izouma.nineth.service.IdentityAuthService;
-import com.izouma.nineth.service.UserService;
 import com.izouma.nineth.utils.*;
 import com.izouma.nineth.web.BaseController;
 import io.ipfs.api.IPFS;
@@ -76,7 +75,6 @@ import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.math.RoundingMode;
-import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -90,10 +88,8 @@ import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
 import java.time.Duration;
 import java.time.LocalDate;
-import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.List;
@@ -101,8 +97,10 @@ import java.util.*;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 import static java.nio.file.StandardOpenOption.CREATE;
@@ -753,4 +751,38 @@ public class CommonTest {
     public void resolve() {
         System.out.println(UrlUtils.resolve("http://www.baidu.com", "123", "456"));
     }
+
+    @Test
+    public void hash() {
+        long t = System.currentTimeMillis();
+        Map<Integer, Integer> map = new HashMap<>();
+        for (int i = 0; i < 5000; i++) {
+            map.put(i, Hashing.md5().hashString(i + "abcd", StandardCharsets.UTF_8).asInt());
+        }
+        AtomicInteger i = new AtomicInteger();
+        map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(e -> {
+            System.out.println(i.incrementAndGet() + ":" + e.getKey() + " : " + e.getValue());
+        });
+        System.out.println(System.currentTimeMillis() - t);
+    }
+
+
+    public int randomize(int number, int total) {
+        Map<Integer, Integer> map = new HashMap<>();
+        for (int i = 0; i < total; i++) {
+            map.put(i, Hashing.md5().hashString(i + "abcd", StandardCharsets.UTF_8).asInt());
+        }
+        int i = 0;
+        Map<Integer, Integer> sorted = new HashMap<>();
+        for (Integer integer : map.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).collect(Collectors.toList())) {
+            sorted.put(integer, i++);
+        }
+        return sorted.get(number);
+    }
+
+
+    @Test
+    public void testrandomize() {
+        System.out.println(randomize(6, 1000));
+    }
 }