Sfoglia il codice sorgente

修改测试用例管理部分功能和新增任务仪表盘的接口

linyk 3 anni fa
parent
commit
f7dcd68022
19 ha cambiato i file con 1519 aggiunte e 0 eliminazioni
  1. 13 0
      core/src/main/java/com/mooctest/crowd/domain/repository/ITestEnvRepo.java
  2. 13 0
      core/src/main/java/com/mooctest/crowd/domain/repository/ITestToolRepo.java
  3. 49 0
      core/src/main/java/com/mooctest/crowd/domain/repository/TestToolRepo.java
  4. 29 0
      site/src/main/java/com/mooctest/crowd/site/command/TestEnvAddedCommand.java
  5. 29 0
      site/src/main/java/com/mooctest/crowd/site/command/TestEnvUpdatedCommand.java
  6. 20 0
      site/src/main/java/com/mooctest/crowd/site/command/TestToolAddedCommand.java
  7. 19 0
      site/src/main/java/com/mooctest/crowd/site/command/TestToolUpdatedCommand.java
  8. 52 0
      site/src/main/java/com/mooctest/crowd/site/configuration/RedisConfiguration.java
  9. 69 0
      site/src/main/java/com/mooctest/crowd/site/controller/TestEnvController.java
  10. 64 0
      site/src/main/java/com/mooctest/crowd/site/controller/TestToolController.java
  11. 67 0
      site/src/main/java/com/mooctest/crowd/site/controller/TesterController.java
  12. 97 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/TaskMoreInfoVO.java
  13. 39 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/TaskStatisticsVO.java
  14. 33 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/TesterTaskVO.java
  15. 16 0
      site/src/main/java/com/mooctest/crowd/site/service/TestEnvService.java
  16. 16 0
      site/src/main/java/com/mooctest/crowd/site/service/TestToolService.java
  17. 99 0
      site/src/main/java/com/mooctest/crowd/site/service/TestToolServiceImpl.java
  18. 102 0
      site/src/main/java/com/mooctest/crowd/site/service/impl/TestEnvServiceImpl.java
  19. 693 0
      site/src/main/java/com/mooctest/crowd/site/util/RedisHelper.java

+ 13 - 0
core/src/main/java/com/mooctest/crowd/domain/repository/ITestEnvRepo.java

@@ -0,0 +1,13 @@
+package com.mooctest.crowd.domain.repository;
+
+import com.mooctest.crowd.domain.domainobject.TestEnv;
+
+import java.util.List;
+
+public interface ITestEnvRepo {
+    void save(TestEnv testEnv);
+    List<TestEnv> findAllByTaskCodeAndUserId(String taskCode, Long userId);
+    void deleteById(Long id);
+    TestEnv findById(Long id);
+    List<TestEnv> findAllByTaskCode(String taskCode);
+}

+ 13 - 0
core/src/main/java/com/mooctest/crowd/domain/repository/ITestToolRepo.java

@@ -0,0 +1,13 @@
+package com.mooctest.crowd.domain.repository;
+
+import com.mooctest.crowd.domain.domainobject.TestTool;
+
+import java.util.List;
+
+public interface ITestToolRepo {
+    void save(TestTool testTool);
+    List<TestTool> findAllByTaskCodeAndUserId(String taskCode, Long userId);
+    void deleteById(Long id);
+    TestTool findById(Long id);
+    List<TestTool> findAllByTaskCode(String taskCode);
+}

+ 49 - 0
core/src/main/java/com/mooctest/crowd/domain/repository/TestToolRepo.java

@@ -0,0 +1,49 @@
+package com.mooctest.crowd.domain.repository;
+
+import com.mooctest.crowd.domain.dao.TestToolDao;
+import com.mooctest.crowd.domain.domainobject.TestEnv;
+import com.mooctest.crowd.domain.domainobject.TestTool;
+import com.mooctest.crowd.domain.model.TestEnvPO;
+import com.mooctest.crowd.domain.model.TestToolPO;
+import com.mooctest.crowd.domain.util.Converter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Component
+public class TestToolRepo implements ITestToolRepo {
+    @Autowired
+    private TestToolDao testToolDao;
+
+    @Override
+    public void save(TestTool testTool) {
+        TestToolPO testToolPO = Converter.convert(TestToolPO.class, testTool);
+        testToolDao.save(testToolPO);
+        testToolPO.setId(testToolPO.getId());
+    }
+
+    @Override
+    public List<TestTool> findAllByTaskCodeAndUserId(String taskCode, Long userId) {
+        List<TestToolPO> testToolPOs = testToolDao.findAllByTaskCodeAndUserId(taskCode, userId);
+        return testToolPOs.stream().map(testToolPO -> Converter.convert(TestTool.class, testToolPO)).collect(Collectors.toList());
+    }
+
+    @Override
+    public void deleteById(Long id) {
+        testToolDao.deleteById(id);
+    }
+
+    @Override
+    public TestTool findById(Long id) {
+        TestToolPO testToolPO = testToolDao.findById(id).orElse(null);
+        return Converter.convert(TestTool.class, testToolPO);
+    }
+
+    @Override
+    public List<TestTool> findAllByTaskCode(String taskCode) {
+        List<TestToolPO> testToolPOs = testToolDao.findAllByTaskCode(taskCode);
+        return testToolPOs.stream().map(testToolPO -> Converter.convert(TestTool.class, testToolPO)).collect(Collectors.toList());
+    }
+}

+ 29 - 0
site/src/main/java/com/mooctest/crowd/site/command/TestEnvAddedCommand.java

@@ -0,0 +1,29 @@
+package com.mooctest.crowd.site.command;
+
+import lombok.Data;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class TestEnvAddedCommand {
+    @NotEmpty(message = "任务编号不能为空")
+    private String taskCode;
+    @NotNull(message = "用户id不能为空")
+    private Long userId;
+    @NotEmpty(message = "设备名字不能为空")
+    private String devName; //设备名称
+    private String devModel; //设备型号
+    @NotNull(message = "设备数量不能为空")
+    @Min(value = 1, message = "设备数量不能小于1")
+    private Integer devCount; //设备数量
+    @NotEmpty(message = "硬件配置不能为空")
+    private String devConfig; //设备硬件配置
+    @NotEmpty(message = "操作系统不能为空")
+    private String opeSys; //操作系统
+    private String middleware; //中间件
+    private String db; //数据库
+    private String browser; //浏览器
+    private String supportSoftware; //支持软件
+}

+ 29 - 0
site/src/main/java/com/mooctest/crowd/site/command/TestEnvUpdatedCommand.java

@@ -0,0 +1,29 @@
+package com.mooctest.crowd.site.command;
+
+import lombok.Data;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class TestEnvUpdatedCommand {
+    @NotNull(message = "id不能为空")
+    private Long id;
+    @NotNull(message = "用户id不能为空")
+    private Long userId;
+    @NotEmpty(message = "设备名字不能为空")
+    private String devName; //设备名称
+    private String devModel; //设备型号
+    @NotNull(message = "设备数量不能为空")
+    @Min(value = 1, message = "设备数量不能小于1")
+    private Integer devCount; //设备数量
+    @NotEmpty(message = "硬件配置不能为空")
+    private String devConfig; //设备硬件配置
+    @NotEmpty(message = "操作系统不能为空")
+    private String opeSys; //操作系统
+    private String middleware; //中间件
+    private String db; //数据库
+    private String browser; //浏览器
+    private String supportSoftware; //支持软件
+}

+ 20 - 0
site/src/main/java/com/mooctest/crowd/site/command/TestToolAddedCommand.java

@@ -0,0 +1,20 @@
+package com.mooctest.crowd.site.command;
+
+import lombok.Data;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class TestToolAddedCommand {
+    @NotEmpty(message = "任务编号不能为空")
+    private String taskCode;
+    @NotNull(message = "用户id不能为空")
+    private Long userId;
+    @NotEmpty(message = "工具名字不能为空")
+    private String name; //名字
+    private String version; //版本
+    private String producerFrom; //生产商/来源
+    private String purpose; //用途
+}

+ 19 - 0
site/src/main/java/com/mooctest/crowd/site/command/TestToolUpdatedCommand.java

@@ -0,0 +1,19 @@
+package com.mooctest.crowd.site.command;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Data
+public class TestToolUpdatedCommand {
+    @NotNull(message = "id不能为空")
+    private Long id;
+    @NotNull(message = "用户id不能为空")
+    private Long userId;
+    @NotEmpty(message = "工具名字不能为空")
+    private String name; //名字
+    private String version; //版本
+    private String producerFrom; //生产商/来源
+    private String purpose; //用途
+}

+ 52 - 0
site/src/main/java/com/mooctest/crowd/site/configuration/RedisConfiguration.java

@@ -0,0 +1,52 @@
+package com.mooctest.crowd.site.configuration;
+
+import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@Configuration
+@AutoConfigureAfter(RedisAutoConfiguration.class)
+public class RedisConfiguration {
+    @Bean
+    @SuppressWarnings("all")
+    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
+        template.setConnectionFactory(factory);
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+//        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);  不用该序列化器,该序列化器会调用OssDynamicJsonSerializer然后存入redis,导致反序列化报错
+        GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        // key采用String的序列化方式
+        template.setKeySerializer(stringRedisSerializer);
+        // hash的key也采用String的序列化方式
+        template.setHashKeySerializer(stringRedisSerializer);
+        // value序列化方式采用jackson
+        template.setValueSerializer(fastJsonRedisSerializer);
+        // hash的value序列化方式采用jackson
+        template.setHashValueSerializer(fastJsonRedisSerializer);
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    @Bean
+    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
+        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+        container.setConnectionFactory(connectionFactory);
+        return container;
+    }
+
+}

+ 69 - 0
site/src/main/java/com/mooctest/crowd/site/controller/TestEnvController.java

@@ -0,0 +1,69 @@
+package com.mooctest.crowd.site.controller;
+
+import com.alibaba.druid.util.StringUtils;
+import com.mooctest.crowd.domain.domainobject.Defect;
+import com.mooctest.crowd.domain.domainobject.TestCase;
+import com.mooctest.crowd.domain.domainobject.TestEnv;
+import com.mooctest.crowd.domain.env.TestCaseExamStatus;
+import com.mooctest.crowd.domain.env.TestStatus;
+import com.mooctest.crowd.domain.page.DOPage;
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.command.*;
+import com.mooctest.crowd.site.data.dto.TestCaseExamCommand;
+import com.mooctest.crowd.site.data.dto.TestCaseSearchDTO;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.service.DefectService;
+import com.mooctest.crowd.site.service.TestCaseService;
+import com.mooctest.crowd.site.service.TestEnvService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import io.swagger.annotations.Api;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/testenv")
+@Api(tags = "测试环境接口")
+public class TestEnvController {
+    @Autowired
+    private TestEnvService testEnvService;
+
+    @PostMapping(value = "/")
+    @LoginRequired
+    public ResponseVO<Long> add(HttpServletRequest request, @RequestBody TestEnvAddedCommand testEnvAddedCommand) {
+        testEnvAddedCommand.setUserId(RequestUtils.getUserId(request));
+        TestEnv testEnv = testEnvService.add(testEnvAddedCommand);
+        return new ResponseVO(ServerCode.SUCCESS, testEnv.getId());
+    }
+
+    @PutMapping(value = "/{id:\\d+}")
+    @LoginRequired
+    public ResponseVO update(HttpServletRequest request, @PathVariable Long id, @Validated @RequestBody TestEnvUpdatedCommand testEnvUpdatedCommand) {
+        testEnvUpdatedCommand.setId(id);
+        testEnvUpdatedCommand.setUserId(RequestUtils.getUserId(request));
+        testEnvService.update(testEnvUpdatedCommand);
+        return new ResponseVO(ServerCode.SUCCESS, null);
+    }
+
+    @DeleteMapping(value = "/{id:\\d+}")
+    @LoginRequired
+    public ResponseVO delete(HttpServletRequest request, @PathVariable Long id) {
+        testEnvService.delete(id, RequestUtils.getUserId(request));
+        return new ResponseVO(ServerCode.SUCCESS, null);
+    }
+
+    @GetMapping(value = "/designer/{taskCode}/{designerId:\\d+}/")
+    @LoginRequired
+    public ResponseVO<List<TestEnv>> list(HttpServletRequest request, @PathVariable String taskCode,
+                                             @PathVariable Long designerId) {
+        if (designerId.equals(0L)) {
+            designerId = RequestUtils.getUserId(request);
+        }
+        List<TestEnv> testEnvs = testEnvService.findAllByTaskCodeAndUserId(taskCode, designerId, RequestUtils.getUserId(request));
+        return new ResponseVO<>(ServerCode.SUCCESS, testEnvs);
+    }
+}

+ 64 - 0
site/src/main/java/com/mooctest/crowd/site/controller/TestToolController.java

@@ -0,0 +1,64 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.domain.domainobject.TestEnv;
+import com.mooctest.crowd.domain.domainobject.TestTool;
+import com.mooctest.crowd.site.annotation.LoginRequired;
+import com.mooctest.crowd.site.command.TestEnvAddedCommand;
+import com.mooctest.crowd.site.command.TestEnvUpdatedCommand;
+import com.mooctest.crowd.site.command.TestToolAddedCommand;
+import com.mooctest.crowd.site.command.TestToolUpdatedCommand;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.service.TestEnvService;
+import com.mooctest.crowd.site.service.TestToolService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import io.swagger.annotations.Api;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/testtool")
+@Api(tags = "测试环境接口")
+public class TestToolController {
+    @Autowired
+    private TestToolService testToolService;
+
+    @PostMapping(value = "/")
+    @LoginRequired
+    public ResponseVO<Long> add(HttpServletRequest request, @RequestBody TestToolAddedCommand testToolAddedCommand) {
+        testToolAddedCommand.setUserId(RequestUtils.getUserId(request));
+        TestTool testTool = testToolService.add(testToolAddedCommand);
+        return new ResponseVO(ServerCode.SUCCESS, testTool.getId());
+    }
+
+    @PutMapping(value = "/{id:\\d+}")
+    @LoginRequired
+    public ResponseVO update(HttpServletRequest request, @PathVariable Long id, @Validated @RequestBody TestToolUpdatedCommand testToolUpdatedCommand) {
+        testToolUpdatedCommand.setId(id);
+        testToolUpdatedCommand.setUserId(RequestUtils.getUserId(request));
+        testToolService.update(testToolUpdatedCommand);
+        return new ResponseVO(ServerCode.SUCCESS, null);
+    }
+
+    @DeleteMapping(value = "/{id:\\d+}")
+    @LoginRequired
+    public ResponseVO delete(HttpServletRequest request, @PathVariable Long id) {
+        testToolService.delete(id, RequestUtils.getUserId(request));
+        return new ResponseVO(ServerCode.SUCCESS, null);
+    }
+
+    @GetMapping(value = "/designer/{taskCode}/{designerId:\\d+}/")
+    @LoginRequired
+    public ResponseVO<List<TestTool>> list(HttpServletRequest request, @PathVariable String taskCode,
+                                             @PathVariable Long designerId) {
+        if (designerId.equals(0L)) {
+            designerId = RequestUtils.getUserId(request);
+        }
+        List<TestTool> testTools = testToolService.findAllByTaskCodeAndUserId(taskCode, designerId, RequestUtils.getUserId(request));
+        return new ResponseVO<>(ServerCode.SUCCESS, testTools);
+    }
+}

+ 67 - 0
site/src/main/java/com/mooctest/crowd/site/controller/TesterController.java

@@ -0,0 +1,67 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.domain.domainobject.Defect;
+import com.mooctest.crowd.domain.domainobject.User;
+import com.mooctest.crowd.domain.env.Seriousness;
+import com.mooctest.crowd.domain.util.Converter;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.data.vo.TesterTaskVO;
+import com.mooctest.crowd.site.service.DefectService;
+import com.mooctest.crowd.site.service.UserService;
+import com.mooctest.crowd.site.util.RequestUtils;
+import io.swagger.annotations.Api;
+import org.apache.http.client.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+@RestController
+@RequestMapping("/api/tester")
+@Api(tags = "测试人员接口")
+public class TesterController {
+    @Autowired
+    private DefectService defectService;
+    @Autowired
+    private UserService userService;
+
+    @GetMapping(value = "/task/{userId}/{taskCode}")
+    public ResponseVO<TesterTaskVO> getTesterTaskInfo(HttpServletRequest request,  @PathVariable("userId") Long userId, @PathVariable("taskCode") String taskCode){
+        List<Defect> defects = defectService.findUserDefects(userId, RequestUtils.getUserId(request), taskCode, 0, 1000).getDatas();
+        List<TesterTaskVO.DefectInfoVO> defectInfoVOs = new ArrayList();
+        Map<String, Integer> defectSeriCountMap = new LinkedHashMap() {{
+            put(Seriousness.VERY_HIGH.getName(), 0);
+            put(Seriousness.HIGH.getName(), 0);
+            put(Seriousness.MID.getName(), 0);
+            put(Seriousness.LOW.getName(), 0);
+            put(Seriousness.VERY_LOW.getName(), 0);
+        }};
+        defects.stream().forEach(defect -> {
+            String seriName = defect.getSeriousness().getName();
+            TesterTaskVO.DefectInfoVO defectInfoVO = new TesterTaskVO.DefectInfoVO();
+            defectInfoVO.setId(defect.getId());
+            defectInfoVO.setDescr(defect.getDescr());
+            defectInfoVO.setCommitTime(DateUtils.formatDate(new Date(defect.getCommitTime().getTime()), "yy-MM-dd HH:mm"));
+            defectInfoVOs.add(defectInfoVO);
+            defectSeriCountMap.put(seriName, defectSeriCountMap.get(seriName) + 1);
+        });
+        List<Integer> defectSeriCounts = new ArrayList();
+        for(String key: defectSeriCountMap.keySet()) {
+            defectSeriCounts.add(defectSeriCountMap.get(key));
+        }
+        User user = userService.getById(userId);
+        TesterTaskVO.TesterInfoVO testerInfo = Converter.convert(TesterTaskVO.TesterInfoVO.class, user);
+        testerInfo.setCreateTime(DateUtils.formatDate(new Date(user.getCreateTime().getTime()), "yyyy-MM-dd HH:mm"));
+        TesterTaskVO testerTaskVO = new TesterTaskVO();
+        testerTaskVO.setTester(testerInfo);
+        testerTaskVO.setDefects(defectInfoVOs);
+        testerTaskVO.setDefectSeriCounts(defectSeriCounts);
+        return new ResponseVO<>(ServerCode.SUCCESS, testerTaskVO);
+    }
+
+}

+ 97 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/TaskMoreInfoVO.java

@@ -0,0 +1,97 @@
+package com.mooctest.crowd.site.data.vo;
+
+import com.mooctest.crowd.domain.domainobject.*;
+import lombok.Data;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+@Data
+public class TaskMoreInfoVO {
+    private ProjectInfoVO project;
+    private TaskInfoVO task;
+    private ReceivingOrgInfoVO receivingOrg;
+    private DemandUnitInfoVO demandUnit;
+    private List<TesterInfoVO> testers;
+    private List<TestCase> testCases;
+    private List<Defect> defects;
+    private List<TestEnv> testEnvs;
+    private List<TestTool> testTools;
+
+    @Data
+    public static class TaskInfoVO {
+        private Long id;
+        private String name;
+        private String code;
+        private String type;
+        private String description;
+        private String requirementFile;
+        private Long distributionType;
+        private String distributionProvince;
+        private String distributionCity;
+        private Double quotedPrice;
+        private Double fixedPrice;
+        private int status;
+        private Timestamp deadTime;
+        private Timestamp endTime;
+        private Timestamp createTime;
+    }
+
+    @Data
+    public static class ProjectInfoVO {
+        private Long id;
+        private String name;
+        private String code;
+        private Long projectDistributionTypeId;
+        private String fieldType;
+        private String applicationType;
+        private String type;
+        private String description;
+        private String projectFile;
+        private String requirementFile;
+        private String distributionProvince;
+        private String distributionCity;
+        private String valuationStandard;
+        private Double quotedPrice;
+        private Double fixedPrice;
+        private Double restPrice;
+        private String entrustUnit;
+        private int status;
+        private Timestamp deadTime;
+        private Timestamp endTime;
+        private Timestamp createTime;
+    }
+
+    @Data
+    public static class TesterInfoVO {
+        private Long id;
+        private String name;
+        private String gender;
+        private String province;
+        private String city;
+        private String photoUrl;
+        private String unit;
+        private String county;
+        private String address;
+    }
+
+    @Data
+    public static class DemandUnitInfoVO {
+        private Long id;
+        private String name;
+        private String address;
+        private String province;
+        private String city;
+        private String county;
+    }
+
+    @Data
+    public static class ReceivingOrgInfoVO {
+        private Long id;
+        private String name;
+        private String address;
+        private String province;
+        private String city;
+        private String county;
+    }
+}

+ 39 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/TaskStatisticsVO.java

@@ -0,0 +1,39 @@
+package com.mooctest.crowd.site.data.vo;
+
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class TaskStatisticsVO {
+    private int testerCount;
+    private int testCaseCount;
+    private int validTestCaseCount;
+    private int noExamedTestCaseCount;
+    private int defectCount;
+    private String taskName;
+    private String taskCode;
+    private String projectCode;
+    private String projectName;
+    private String createTime;
+    private String endTime;
+    private int status;
+    private String statusDescr;
+    private List<TesterVO> testers;
+    //发包单位
+    private String companyName;
+    //发包单位城市
+    private String companyCity;
+    private List<Integer> defectSeriCounts;
+    private Map<String, Integer> defectTypeCountMap;
+    private Map<Integer, Integer> defectTimeCountMap;
+
+    @Data
+    public static class TesterVO {
+        private long id;
+        private String name;
+        private int score;
+        private String city;
+    }
+}

+ 33 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/TesterTaskVO.java

@@ -0,0 +1,33 @@
+package com.mooctest.crowd.site.data.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class TesterTaskVO {
+    private TesterInfoVO tester;
+    private List<DefectInfoVO> defects;
+    private List<Integer> defectSeriCounts;
+
+    @Data
+    public static class TesterInfoVO {
+        private Long id;
+        private String name;
+        private String gender;
+        private String province;
+        private String city;
+        private String photoUrl;
+        private String unit;
+        private String county;
+        private String address;
+        private String createTime;
+    }
+
+    @Data
+    public static class DefectInfoVO {
+        private Long id;
+        private String descr;
+        private String commitTime;
+    }
+}

+ 16 - 0
site/src/main/java/com/mooctest/crowd/site/service/TestEnvService.java

@@ -0,0 +1,16 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.domain.domainobject.TestEnv;
+import com.mooctest.crowd.site.command.TestEnvAddedCommand;
+import com.mooctest.crowd.site.command.TestEnvUpdatedCommand;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+@Validated
+public interface TestEnvService {
+    TestEnv add(TestEnvAddedCommand testEnvAddedCommand);
+    TestEnv update(TestEnvUpdatedCommand testEnvUpdatedCommand);
+    void delete(Long id, Long userId);
+    List<TestEnv> findAllByTaskCodeAndUserId(String taskCode, Long userId, Long readUserId);
+}

+ 16 - 0
site/src/main/java/com/mooctest/crowd/site/service/TestToolService.java

@@ -0,0 +1,16 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.domain.domainobject.TestTool;
+import com.mooctest.crowd.site.command.TestToolAddedCommand;
+import com.mooctest.crowd.site.command.TestToolUpdatedCommand;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+@Validated
+public interface TestToolService {
+    TestTool add(TestToolAddedCommand testToolAddedCommand);
+    TestTool update(TestToolUpdatedCommand testToolUpdatedCommand);
+    void delete(Long id, Long userId);
+    List<TestTool> findAllByTaskCodeAndUserId(String taskCode, Long userId, Long readUserId);
+}

+ 99 - 0
site/src/main/java/com/mooctest/crowd/site/service/TestToolServiceImpl.java

@@ -0,0 +1,99 @@
+package com.mooctest.crowd.site.service;
+
+import com.mooctest.crowd.domain.dao.CrowdTestProjectDao;
+import com.mooctest.crowd.domain.dao.TaskToUserDao;
+import com.mooctest.crowd.domain.domainobject.CrowdTestTask;
+import com.mooctest.crowd.domain.domainobject.DeletedStatus;
+import com.mooctest.crowd.domain.domainobject.TestTool;
+import com.mooctest.crowd.domain.exception.BaseException;
+import com.mooctest.crowd.domain.exception.CrowdTestProjectNotExistException;
+import com.mooctest.crowd.domain.exception.CrowdTestTaskNotExistException;
+import com.mooctest.crowd.domain.model.CrowdTestProjectPO;
+import com.mooctest.crowd.domain.model.TaskToUserPO;
+import com.mooctest.crowd.domain.repository.CrowdTestTaskRepo;
+import com.mooctest.crowd.domain.repository.TestToolRepo;
+import com.mooctest.crowd.domain.util.Converter;
+import com.mooctest.crowd.site.command.TestToolAddedCommand;
+import com.mooctest.crowd.site.command.TestToolUpdatedCommand;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+public class TestToolServiceImpl implements TestToolService {
+    @Autowired
+    private TestToolRepo testToolRepo;
+    @Autowired
+    private CrowdTestTaskRepo crowdTestTaskRepo;
+    @Autowired
+    private TaskToUserDao taskToUserDao;
+    @Autowired
+    private CrowdTestProjectDao crowdTestProjectDao;
+
+    @Override
+    @Transactional
+    public TestTool add(TestToolAddedCommand testToolAddedCommand) {
+        CrowdTestTask task = crowdTestTaskRepo.findByCode(testToolAddedCommand.getTaskCode());
+        if (task == null) {
+            throw new CrowdTestTaskNotExistException();
+        }
+        if (task.getStatus() == CrowdTestTask.HAS_TIME_OUT || task.getStatus() == CrowdTestTask.HAS_FINISHED ||
+            task.getStatus() == CrowdTestTask.HAS_COMMITED) {
+            throw new BaseException("当前任务不能操作!");
+        }
+        TaskToUserPO taskToUserPO = taskToUserDao.findByUserIdAndTaskCode(testToolAddedCommand.getUserId(), testToolAddedCommand.getTaskCode());
+        if (taskToUserPO == null) {
+            throw new BaseException("您没有权限操作该任务!");
+        }
+        TestTool testTool = Converter.convert(TestTool.class, testToolAddedCommand);
+        testToolRepo.save(testTool);
+        return testTool;
+    }
+
+    @Override
+    @Transactional
+    public TestTool update(TestToolUpdatedCommand testToolUpdatedCommand) {
+        TestTool testTool = testToolRepo.findById(testToolUpdatedCommand.getId());
+        if (testTool == null) {
+            throw new BaseException("该测试工具记录不存在!");
+        }
+        if (!testTool.getUserId().equals(testToolUpdatedCommand.getUserId())) {
+            throw new BaseException("您没有权限修改该记录!");
+        }
+        Converter.copy(testTool, testToolUpdatedCommand);
+        testToolRepo.save(testTool);
+        return testTool;
+    }
+
+    @Override
+    public void delete(Long id, Long userId) {
+        TestTool testTool = testToolRepo.findById(id);
+        if (testTool == null) {
+            throw new BaseException("该测试环境记录不存在!");
+        }
+        if (!testTool.getUserId().equals(userId)) {
+            throw new BaseException("您没有权限删除该记录!");
+        }
+        testToolRepo.deleteById(id);
+    }
+
+    @Override
+    public List<TestTool> findAllByTaskCodeAndUserId(String taskCode, Long userId, Long readUserId) {
+        if (!readUserId.equals(userId)) {
+            CrowdTestTask crowdTestTask = crowdTestTaskRepo.findByCode(taskCode);
+            if (crowdTestTask == null) {
+                throw new CrowdTestTaskNotExistException();
+            }
+            CrowdTestProjectPO crowdTestProjectPO = crowdTestProjectDao.findByCodeAndIsDeleted(crowdTestTask.getCrowdTestProjectCode(), DeletedStatus.isNotDeleted);
+            if (crowdTestProjectPO == null) {
+                throw new CrowdTestProjectNotExistException();
+            }
+            if (!crowdTestProjectPO.getRegionalManagerId().equals(readUserId)) {
+                throw new BaseException("当前用户无权获取该数据");
+            }
+        }
+        return testToolRepo.findAllByTaskCodeAndUserId(taskCode, userId);
+    }
+}

+ 102 - 0
site/src/main/java/com/mooctest/crowd/site/service/impl/TestEnvServiceImpl.java

@@ -0,0 +1,102 @@
+package com.mooctest.crowd.site.service.impl;
+
+import com.mooctest.crowd.domain.dao.CrowdTestProjectDao;
+import com.mooctest.crowd.domain.dao.TaskToUserDao;
+import com.mooctest.crowd.domain.domainobject.CrowdTestTask;
+import com.mooctest.crowd.domain.domainobject.DeletedStatus;
+import com.mooctest.crowd.domain.domainobject.TaskToUser;
+import com.mooctest.crowd.domain.domainobject.TestEnv;
+import com.mooctest.crowd.domain.exception.BaseException;
+import com.mooctest.crowd.domain.exception.CrowdTestProjectNotExistException;
+import com.mooctest.crowd.domain.exception.CrowdTestTaskNotExistException;
+import com.mooctest.crowd.domain.model.CrowdTestProjectPO;
+import com.mooctest.crowd.domain.model.TaskToUserPO;
+import com.mooctest.crowd.domain.repository.CrowdTestTaskRepo;
+import com.mooctest.crowd.domain.repository.TestEnvRepo;
+import com.mooctest.crowd.domain.util.Converter;
+import com.mooctest.crowd.site.command.TestEnvAddedCommand;
+import com.mooctest.crowd.site.command.TestEnvUpdatedCommand;
+import com.mooctest.crowd.site.service.TestEnvService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+public class TestEnvServiceImpl implements TestEnvService {
+    @Autowired
+    private TestEnvRepo testEnvRepo;
+    @Autowired
+    private CrowdTestTaskRepo crowdTestTaskRepo;
+    @Autowired
+    private TaskToUserDao taskToUserDao;
+    @Autowired
+    private CrowdTestProjectDao crowdTestProjectDao;
+
+    @Override
+    @Transactional
+    public TestEnv add(TestEnvAddedCommand testEnvAddedCommand) {
+        CrowdTestTask task = crowdTestTaskRepo.findByCode(testEnvAddedCommand.getTaskCode());
+        if (task == null) {
+            throw new CrowdTestTaskNotExistException();
+        }
+        if (task.getStatus() == CrowdTestTask.HAS_TIME_OUT || task.getStatus() == CrowdTestTask.HAS_FINISHED ||
+            task.getStatus() == CrowdTestTask.HAS_COMMITED) {
+            throw new BaseException("当前任务不能操作!");
+        }
+        TaskToUserPO taskToUserPO = taskToUserDao.findByUserIdAndTaskCode(testEnvAddedCommand.getUserId(), testEnvAddedCommand.getTaskCode());
+        if (taskToUserPO == null) {
+            throw new BaseException("您没有权限操作该任务!");
+        }
+        TestEnv testEnv = Converter.convert(TestEnv.class, testEnvAddedCommand);
+        testEnvRepo.save(testEnv);
+        return testEnv;
+    }
+
+    @Override
+    @Transactional
+    public TestEnv update(TestEnvUpdatedCommand testEnvUpdatedCommand) {
+        TestEnv testEnv = testEnvRepo.findById(testEnvUpdatedCommand.getId());
+        if (testEnv == null) {
+            throw new BaseException("该测试环境记录不存在!");
+        }
+        if (!testEnv.getUserId().equals(testEnvUpdatedCommand.getUserId())) {
+            throw new BaseException("您没有权限修改该记录!");
+        }
+        Converter.copy(testEnv, testEnvUpdatedCommand);
+        testEnvRepo.save(testEnv);
+        return testEnv;
+    }
+
+    @Override
+    public void delete(Long id, Long userId) {
+        TestEnv testEnv = testEnvRepo.findById(id);
+        if (testEnv == null) {
+            throw new BaseException("该测试环境记录不存在!");
+        }
+        if (!testEnv.getUserId().equals(userId)) {
+            throw new BaseException("您没有权限删除该记录!");
+        }
+        testEnvRepo.deleteById(id);
+    }
+
+    @Override
+    public List<TestEnv> findAllByTaskCodeAndUserId(String taskCode, Long userId, Long readUserId) {
+        if (!readUserId.equals(userId)) {
+            CrowdTestTask crowdTestTask = crowdTestTaskRepo.findByCode(taskCode);
+            if (crowdTestTask == null) {
+                throw new CrowdTestTaskNotExistException();
+            }
+            CrowdTestProjectPO crowdTestProjectPO = crowdTestProjectDao.findByCodeAndIsDeleted(crowdTestTask.getCrowdTestProjectCode(), DeletedStatus.isNotDeleted);
+            if (crowdTestProjectPO == null) {
+                throw new CrowdTestProjectNotExistException();
+            }
+            if (!crowdTestProjectPO.getRegionalManagerId().equals(readUserId)) {
+                throw new BaseException("当前用户无权获取该数据");
+            }
+        }
+        return testEnvRepo.findAllByTaskCodeAndUserId(taskCode, userId);
+    }
+
+}

+ 693 - 0
site/src/main/java/com/mooctest/crowd/site/util/RedisHelper.java

@@ -0,0 +1,693 @@
+package com.mooctest.crowd.site.util;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.geo.Circle;
+import org.springframework.data.geo.Distance;
+import org.springframework.data.geo.GeoResults;
+import org.springframework.data.geo.Point;
+import org.springframework.data.redis.connection.RedisGeoCommands;
+import org.springframework.data.redis.connection.RedisStringCommands;
+import org.springframework.data.redis.connection.ReturnType;
+import org.springframework.data.redis.core.GeoOperations;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ZSetOperations;
+import org.springframework.data.redis.core.types.Expiration;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class RedisHelper {
+    public static final String UNLOCK_LUA;
+
+    static {
+        StringBuilder sb = new StringBuilder();
+        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
+        sb.append("then ");
+        sb.append("    return redis.call(\"del\",KEYS[1]) ");
+        sb.append("else ");
+        sb.append("    return 0 ");
+        sb.append("end ");
+        UNLOCK_LUA = sb.toString();
+    }
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+        // =============================common============================
+        /**
+         * 指定缓存失效时间
+         * @param key 键
+         * @param time 时间(秒)
+         * @return
+         */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 根据key 获取过期时间
+         * @param key 键 不能为null
+         * @return 时间(秒) 返回0代表为永久有效
+         */
+    public long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+        /**
+         * 判断key是否存在
+         * @param key 键
+         * @return true 存在 false不存在
+         */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 删除缓存
+         * @param key 可以传一个值 或多个
+         */
+    @SuppressWarnings("unchecked")
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete((List)CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+        // ============================String=============================
+        /**
+         * 普通缓存获取
+         * @param key 键
+         * @return 值
+         */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+        /**
+         * 普通缓存放入
+         * @param key 键
+         * @param value 值
+         * @return true成功 false失败
+         */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 普通缓存放入并设置时间
+         * @param key 键
+         * @param value 值
+         * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
+         * @return true成功 false 失败
+         */
+    public boolean set(String key, Object value, long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 递增
+         * @param key 键
+         * @param delta 要增加几(大于0)
+         * @return
+         */
+    public long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+        /**
+         * 递减
+         * @param key 键
+         * @param delta 要减少几(小于0)
+         * @return
+         */
+    public long decr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+        // ================================Map=================================
+        /**
+         * HashGet
+         * @param key 键 不能为null
+         * @param item 项 不能为null
+         * @return 值
+         */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+        /**
+         * 获取hashKey对应的所有键值
+         * @param key 键
+         * @return 对应的多个键值
+         */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+        /**
+         * HashSet
+         * @param key 键
+         * @param map 对应多个键值
+         * @return true 成功 false 失败
+         */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * HashSet 并设置时间
+         * @param key 键
+         * @param map 对应多个键值
+         * @param time 时间(秒)
+         * @return true成功 false失败
+         */
+    public boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 向一张hash表中放入数据,如果不存在将创建
+         * @param key 键
+         * @param item 项
+         * @param value 值
+         * @return true 成功 false失败
+         */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    public long hSize(String key) {
+        return redisTemplate.opsForHash().size(key);
+    }
+        /**
+         * 向一张hash表中放入数据,如果不存在将创建
+         * @param key 键
+         * @param item 项
+         * @param value 值
+         * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+         * @return true 成功 false失败
+         */
+    public boolean hset(String key, String item, Object value, long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 删除hash表中的值
+         * @param key 键 不能为null
+         * @param item 项 可以使多个 不能为null
+         */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+        /**
+         * 判断hash表中是否有该项的值
+         * @param key 键 不能为null
+         * @param item 项 不能为null
+         * @return true 存在 false不存在
+         */
+    public boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+        /**
+         * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+         * @param key 键
+         * @param item 项
+         * @param by 要增加几(大于0)
+         * @return
+         */
+    public double hincr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+        /**
+         * hash递减
+         * @param key 键
+         * @param item 项
+         * @param by 要减少记(小于0)
+         * @return
+         */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+        // ============================set=============================
+        /**
+         * 根据key获取Set中的所有值
+         * @param key 键
+         * @return
+         */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     * @param key 键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 将数据放入set缓存
+         * @param key 键
+         * @param values 值 可以是多个
+         * @return 成功个数
+         */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+        /**
+         * 将set数据放入缓存
+         * @param key 键
+         * @param time 时间(秒)
+         * @param values 值 可以是多个
+         * @return 成功个数
+         */
+    public long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0)
+            expire(key, time);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+        /**
+         * 获取set缓存的长度
+         * @param key 键
+         * @return
+         */
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    public Set<Object> sDiff(String key1, String key2) {
+        try {
+            return redisTemplate.opsForSet().difference(key1, key2);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public Set<Object> sDiff(String key1, Collection c) {
+        try {
+            return redisTemplate.opsForSet().difference(key1, c);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public long sIntersectAndStore(String key1, String key2, String key) {
+        try {
+            return redisTemplate.opsForSet().intersectAndStore(key1, key2, key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0L;
+        }
+    }
+        /**
+         * 移除值为value的
+         * @param key 键
+         * @param values 值 可以是多个
+         * @return 移除的个数
+         */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+        // ===============================list=================================
+        /**
+         * 获取list缓存的内容
+         * @param key 键
+         * @param start 开始
+         * @param end 结束 0 到 -1代表所有值
+         * @return
+         */
+    public List<Object> lGet(String key, long start, long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+        /**
+         * 获取list缓存的长度
+         * @param key 键
+         * @return
+         */
+    public long lGetListSize(String key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+        /**
+         * 通过索引 获取list中的值
+         * @param key 键
+         * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
+         * @return
+         */
+    public Object lGetIndex(String key, long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+        /**
+         * 将list放入缓存
+         * @param key 键
+         * @param value 值
+         * @return
+         */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 将list放入缓存
+         * @param key 键
+         * @param value 值
+         * @param time 时间(秒)
+         * @return
+         */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0)
+            expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 将list放入缓存
+         * @param key 键
+         * @param value 值
+         * @return
+         */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 将list放入缓存
+         *
+         * @param key 键
+         * @param value 值
+         * @param time 时间(秒)
+         * @return
+         */
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0)
+            expire(key, time);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 根据索引修改list中的某条数据
+         * @param key 键
+         * @param index 索引
+         * @param value 值
+         * @return
+         */
+    public boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+        /**
+         * 移除N个值为value
+         * @param key 键
+         * @param count 移除多少个
+         * @param value 值
+         * @return 移除的个数
+         */
+    public long lRemove(String key, long count, Object value) {
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return 0;
+        }
+    }
+
+    public boolean geoSet(String key, double latitude, double longitude, Object value) {
+        try {
+            redisTemplate.opsForGeo().add(key, new Point(longitude, latitude), value);
+            return true;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public boolean removeGeo(String key, Object...values) {
+        try {
+            GeoOperations geoOps = redisTemplate.opsForGeo();
+            geoOps.remove(key, values);
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+        return true;
+    }
+
+    public Distance distanceGeo(String key, Object member1, String member2) {
+        try {
+            GeoOperations geoOps = redisTemplate.opsForGeo();
+            return geoOps.distance(key, member1, member2);
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    public GeoResults<RedisGeoCommands.GeoLocation> radiusGeo(String key, double latitude, double longitude, double distance, Sort.Direction direction, long limit) {
+        try {
+            GeoOperations geoOps = redisTemplate.opsForGeo();
+            //设置geo查询参数
+            RedisGeoCommands.GeoRadiusCommandArgs geoRadiusArgs = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
+            geoRadiusArgs = geoRadiusArgs.includeCoordinates().includeDistance();//查询返回结果包括距离和坐标
+            if (Sort.Direction.ASC.equals(direction)) {//按查询出的坐标距离中心坐标的距离进行排序
+                geoRadiusArgs.sortAscending();
+            } else if (Sort.Direction.DESC.equals(direction)) {
+                geoRadiusArgs.sortDescending();
+            }
+            geoRadiusArgs.limit(limit);//限制查询数量
+            GeoResults<RedisGeoCommands.GeoLocation> radiusGeo = geoOps.radius(key, new Circle(new Point(longitude, latitude), new Distance(distance, RedisGeoCommands.DistanceUnit.METERS)), geoRadiusArgs);
+            return radiusGeo;
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    public List<Point> geoPosition(String key, Object...vals) {
+        GeoOperations geoOps = redisTemplate.opsForGeo();
+        return geoOps.position(key, vals);
+    }
+
+    public boolean zSet(String key, Object val, double score) {
+        redisTemplate.opsForZSet().add(key, val, score);
+        return true;
+    }
+
+    public boolean zSet(String key, Set<ZSetOperations.TypedTuple<Object>> typedTuples) {
+        redisTemplate.opsForZSet().add(key, typedTuples);
+        return true;
+    }
+
+    public long zCard(String key) {
+        return redisTemplate.opsForZSet().zCard(key);
+    }
+
+    // 升序指定起始和结束位置删除
+    public boolean zRemoveRange(String key, long index1, long index2) {
+        redisTemplate.opsForZSet().removeRange(key, index1, index2);
+        return true;
+    }
+
+    //正序
+    public Set zRange(String key, long index1, long index2) {
+        return redisTemplate.opsForZSet().range(key, index1, index2);
+    }
+
+    //倒序
+    public Set zReverseRange(String key, long index1, long index2) {
+        return redisTemplate.opsForZSet().reverseRange(key, index1, index2);
+    }
+
+    public Set zRangeByScore(String key, double score1, double score2) {
+        return redisTemplate.opsForZSet().rangeByScore(key, score1, score2);
+    }
+
+    public Set zReverseRangeByScore(String key, double score1, double score2) {
+        return redisTemplate.opsForZSet().reverseRangeByScore(key, score1, score2);
+    }
+
+    public Set<ZSetOperations.TypedTuple<Object>> zRangeByScoreWithScores(String key, double score1, double score2) {
+        Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.opsForZSet().rangeByScoreWithScores(key, score1, score2);
+        return set;
+    }
+
+    public boolean zRemove(String key, Object... objs) {
+        redisTemplate.opsForZSet().remove(key, objs);
+        return true;
+    }
+
+    public Set<String> keys(String pattern) {
+        try {
+            return redisTemplate.keys(pattern);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public boolean lock(String key, String requestId, int expireSecond) {
+        try {
+            RedisCallback<Boolean> callback = (connection) -> {
+//                RedisAsyncCommandsImpl commands = (RedisAsyncCommandsImpl) connection.getNativeConnection();
+                return connection.set(key.getBytes(), requestId.getBytes(), Expiration.seconds(expireSecond), RedisStringCommands.SetOption.SET_IF_ABSENT);
+            };
+            return redisTemplate.execute(callback);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    public boolean releaseLock(String key, String requestId) {
+        // 释放锁的时候,有可能因为持锁之后方法执行时间大于锁的有效期,此时有可能已经被另外一个线程持有锁,所以不能直接删除
+        try {
+            List<String> keys = new ArrayList<>();
+            keys.add(key);
+            List<String> args = new ArrayList<>();
+            args.add(requestId);
+            // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
+            RedisCallback<Boolean> callback = (connection) -> {
+                Boolean result = connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN, 1, key.getBytes(), requestId.getBytes());
+                return result;
+            };
+            return redisTemplate.execute(callback);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void convertAndSend(String channel, Object message) {
+        redisTemplate.convertAndSend(channel, message);
+    }
+
+}