Просмотр исходного кода

实现oss的分片上传以及部分本地分片上传、修复众测广场上获取热门任务和更多热门任务的接口错误数据、为任务增加领域类型、应用类型信息,进行接口优化

郭超 4 лет назад
Родитель
Сommit
4c56486d23
65 измененных файлов с 4651 добавлено и 537 удалено
  1. 6 7
      core/src/main/java/com/mooctest/crowd/domain/dao/CrowdTestProjectDao.java
  2. 3 3
      core/src/main/java/com/mooctest/crowd/domain/dao/CrowdTestTaskDao.java
  3. 2 0
      core/src/main/java/com/mooctest/crowd/domain/domainobject/CrowdTestTask.java
  4. 8 0
      core/src/main/java/com/mooctest/crowd/domain/exception/FileCopyException.java
  5. 8 0
      core/src/main/java/com/mooctest/crowd/domain/exception/FileDownloadException.java
  6. 8 0
      core/src/main/java/com/mooctest/crowd/domain/exception/FileUploadFailureException.java
  7. 8 0
      core/src/main/java/com/mooctest/crowd/domain/exception/IllegalStateUploadException.java
  8. 0 15
      core/src/main/java/com/mooctest/crowd/domain/repository/CommonRepo.java
  9. 29 48
      core/src/main/java/com/mooctest/crowd/domain/repository/CrowdTestProjectRepo.java
  10. 5 0
      core/src/main/java/com/mooctest/crowd/domain/repository/CrowdTestTaskRepo.java
  11. 2 0
      core/src/main/java/com/mooctest/crowd/domain/repository/ICrowdTaskRepo.java
  12. 7 6
      core/src/main/java/com/mooctest/crowd/domain/repository/ICrowdTestProjectRepo.java
  13. 6 0
      site/pom.xml
  14. 9 1
      site/src/main/java/com/mooctest/crowd/site/configuration/OSSConfiguration.java
  15. 26 0
      site/src/main/java/com/mooctest/crowd/site/constants/FileConstant.java
  16. 2 0
      site/src/main/java/com/mooctest/crowd/site/constants/UploadType.java
  17. 42 30
      site/src/main/java/com/mooctest/crowd/site/controller/CrowTestSquareController.java
  18. 1 1
      site/src/main/java/com/mooctest/crowd/site/controller/CrowdProjectController.java
  19. 7 2
      site/src/main/java/com/mooctest/crowd/site/controller/CrowdTaskController.java
  20. 75 0
      site/src/main/java/com/mooctest/crowd/site/controller/FileController.java
  21. 16 18
      site/src/main/java/com/mooctest/crowd/site/controller/UploadController.java
  22. 8 0
      site/src/main/java/com/mooctest/crowd/site/controller/advice/ExceptionAdvice.java
  23. 1 1
      site/src/main/java/com/mooctest/crowd/site/controller/interceptor/FileCheckInterceptor.java
  24. 13 0
      site/src/main/java/com/mooctest/crowd/site/data/dto/FileDownloadRequestDTO.java
  25. 25 0
      site/src/main/java/com/mooctest/crowd/site/data/dto/FileUploadDTO.java
  26. 46 0
      site/src/main/java/com/mooctest/crowd/site/data/dto/FileUploadRequestDTO.java
  27. 81 0
      site/src/main/java/com/mooctest/crowd/site/data/enums/DateType.java
  28. 32 0
      site/src/main/java/com/mooctest/crowd/site/data/enums/FileCheckMd5Status.java
  29. 3 0
      site/src/main/java/com/mooctest/crowd/site/data/vo/CrowdTaskVO.java
  30. 19 7
      site/src/main/java/com/mooctest/crowd/site/mediator/ViewMediator.java
  31. 171 133
      site/src/main/java/com/mooctest/crowd/site/mediator/impl/WebMediatorImpl.java
  32. 4 2
      site/src/main/java/com/mooctest/crowd/site/service/CrowdProjectService.java
  33. 6 4
      site/src/main/java/com/mooctest/crowd/site/service/CrowdTaskService.java
  34. 2 1
      site/src/main/java/com/mooctest/crowd/site/service/CrowdTestSquareService.java
  35. 12 0
      site/src/main/java/com/mooctest/crowd/site/service/FileService.java
  36. 242 0
      site/src/main/java/com/mooctest/crowd/site/service/MultipartUploadSample.java
  37. 7 7
      site/src/main/java/com/mooctest/crowd/site/service/UploadService.java
  38. 25 21
      site/src/main/java/com/mooctest/crowd/site/service/impl/CrowdProjectServiceImpl.java
  39. 48 15
      site/src/main/java/com/mooctest/crowd/site/service/impl/CrowdTaskServiceImpl.java
  40. 21 1
      site/src/main/java/com/mooctest/crowd/site/service/impl/CrowdTestSquareServiceImpl.java
  41. 147 1
      site/src/main/java/com/mooctest/crowd/site/service/impl/FileServiceImpl.java
  42. 26 16
      site/src/main/java/com/mooctest/crowd/site/service/impl/OSSUploadServiceImpl.java
  43. 241 3
      site/src/main/java/com/mooctest/crowd/site/service/impl/OssFileServiceImpl.java
  44. 9 0
      site/src/main/java/com/mooctest/crowd/site/strategy/SliceUploadStrategy.java
  45. 17 0
      site/src/main/java/com/mooctest/crowd/site/strategy/annotation/UploadMode.java
  46. 27 0
      site/src/main/java/com/mooctest/crowd/site/strategy/concurrent/FileCallable.java
  47. 36 0
      site/src/main/java/com/mooctest/crowd/site/strategy/config/RedisConfig.java
  48. 47 0
      site/src/main/java/com/mooctest/crowd/site/strategy/context/UploadContext.java
  49. 10 0
      site/src/main/java/com/mooctest/crowd/site/strategy/enu/UploadModeEnum.java
  50. 63 0
      site/src/main/java/com/mooctest/crowd/site/strategy/impl/MappedByteBufferUploadStrategy.java
  51. 52 0
      site/src/main/java/com/mooctest/crowd/site/strategy/impl/RandomAccessUploadStrategy.java
  52. 17 0
      site/src/main/java/com/mooctest/crowd/site/strategy/init/UploadStrategyInitializingBean.java
  53. 167 0
      site/src/main/java/com/mooctest/crowd/site/strategy/template/SliceUploadTemplate.java
  54. 0 152
      site/src/main/java/com/mooctest/crowd/site/util/FileUtil.java
  55. 0 1
      site/src/main/java/com/mooctest/crowd/site/util/GenerateFlowCodeUtil.java
  56. 944 0
      site/src/main/java/com/mooctest/crowd/site/util/file/DateUtil.java
  57. 73 0
      site/src/main/java/com/mooctest/crowd/site/util/file/FileMD5Util.java
  58. 53 0
      site/src/main/java/com/mooctest/crowd/site/util/file/FilePathUtil.java
  59. 709 0
      site/src/main/java/com/mooctest/crowd/site/util/file/FileUtil.java
  60. 670 0
      site/src/main/java/com/mooctest/crowd/site/util/file/RedisUtil.java
  61. 39 0
      site/src/main/java/com/mooctest/crowd/site/util/file/SimpleAsyncExec.java
  62. 91 0
      site/src/main/java/com/mooctest/crowd/site/util/file/SpringContextHolder.java
  63. 27 0
      site/src/main/java/com/mooctest/crowd/site/util/file/SystemUtil.java
  64. 67 0
      site/src/main/java/com/mooctest/crowd/site/util/file/YmlUtil.java
  65. 83 41
      site/src/main/resources/application.yml

+ 6 - 7
core/src/main/java/com/mooctest/crowd/domain/dao/CrowdTestProjectDao.java

@@ -34,18 +34,17 @@ public interface CrowdTestProjectDao extends CrudRepository<CrowdTestProjectPO,
     @Query(value = "SELECT * FROM crowd_test_project p WHERE CTP_STATUS = 1 and CTP_PDT_ID = 2 and CTP_IS_DELETED = 0 ORDER BY CTP_CREATE_TIME DESC", nativeQuery = true)
     List<CrowdTestProjectPO> findAll();
 
-    //这个指定了查询条数 适用于众测广场首页 这样性能会高一点。
-    @Query(value = "SELECT * FROM crowd_test_project p WHERE CTP_STATUS =1 and CTP_PDT_ID = 2 and CTP_IS_DELETED = 0 ORDER BY CTP_CREATE_TIME DESC LIMIT 6; ", nativeQuery = true)
-    List<CrowdTestProjectPO> findindexProject();
+    //这个指定了查询条数 适用于众测广场首页
+    @Query(value = "SELECT * FROM crowd_test_project p WHERE CTP_STATUS =1 and CTP_PDT_ID = 2 and CTP_IS_DELETED = 0 ORDER BY CTP_CREATE_TIME DESC LIMIT ?1", nativeQuery = true)
+    List<CrowdTestProjectPO> findSquareIndexProject(int indexCount);
 
+    //适用于众测广场 指定了查询热门项目卡片显示的数量
+    @Query(value = "SELECT * FROM crowd_test_project p WHERE CTP_STATUS !=4 and CTP_STATUS !=5 and CTP_IS_DELETED = 0 ORDER BY CTP_JOIN_COUNT DESC ,CTP_CREATE_TIME LIMIT ?1 ", nativeQuery = true)
+    List<CrowdTestProjectPO> findHotProjectCardLimitCount(int projectCount);
 
     @Query(value = "SELECT * FROM crowd_test_project p WHERE CTP_STATUS !=4 and CTP_STATUS !=5 and CTP_IS_DELETED = 0 ORDER BY CTP_JOIN_COUNT DESC ,CTP_STATUS ASC, CTP_CREATE_TIME DESC; ", nativeQuery = true)
     List<CrowdTestProjectPO> findAllHotProject();
 
-    //这个指定了查询条数 适用于众测广场首页 这样性能会高一点对应热门项目
-    @Query(value = "SELECT * FROM crowd_test_project p WHERE CTP_STATUS !=4 and CTP_STATUS !=5 and CTP_IS_DELETED = 0 ORDER BY CTP_JOIN_COUNT DESC ,CTP_CREATE_TIME LIMIT 6; ", nativeQuery = true)
-    List<CrowdTestProjectPO> findall();
-
     CrowdTestProjectPO findByIdAndIsDeleted(Long id, int isDeleted);
 
     CrowdTestProjectPO findByCodeAndIsDeleted(String code, int isDeleted);

+ 3 - 3
core/src/main/java/com/mooctest/crowd/domain/dao/CrowdTestTaskDao.java

@@ -29,8 +29,8 @@ public interface CrowdTestTaskDao extends CrudRepository<CrowdTestTaskPO, Long>,
             "CTT_STATUS,CTT_FULL_STATUS,CTT_DEAD_LINE,CTT_IS_DELETED,CTT_PARTICIPANT_COUNT,CTT_ACCEPTED_COUNT,CTT_PARTICIPANT_HAS_COMMITTED_COUNT,CTT_CREATE_TIME,CTT_END_TIME FROM  crowd_test_task t where CTT_STATUS=1 or CTT_STATUS=2  and CTT_IS_DELETED = 0 ORDER BY CTT_CREATE_TIME DESC", nativeQuery = true)
     List<CrowdTestTaskPO> findAll();
 
-    @Query(value = "select * FROM crowd_test_task WHERE (CTT_STATUS =1 or (CTT_STATUS =2 and CTT_FULL_STATUS = 0)) and CTT_DISTRIBUTION_TYPE = 2 and CTT_IS_DELETED = 0  ORDER BY CTT_CREATE_TIME DESC LIMIT 6 ", nativeQuery = true)
-    List<CrowdTestTaskPO> findindexTask();
+    @Query(value = "select * FROM crowd_test_task WHERE (CTT_STATUS =1 or (CTT_STATUS =2 and CTT_FULL_STATUS = 0)) and CTT_DISTRIBUTION_TYPE = 2 and CTT_IS_DELETED = 0  ORDER BY CTT_CREATE_TIME DESC LIMIT ?1 ", nativeQuery = true)
+    List<CrowdTestTaskPO> findIndexTaskLimitCount(int indexCount);
 
     List<CrowdTestTaskPO> findByType(String type);
 
@@ -45,7 +45,7 @@ public interface CrowdTestTaskDao extends CrudRepository<CrowdTestTaskPO, Long>,
     @Query(value = "select * FROM  crowd_test_task WHERE CTT_STATUS !=4 and CTT_STATUS !=5 and CTT_IS_DELETED = 0  ORDER BY CTT_ACCEPTED_COUNT DESC, CTT_STATUS ASC ,CTT_CREATE_TIME DESC", nativeQuery = true)
     List<CrowdTestTaskPO> findMoreHotTasksList();
 
-    List<CrowdTestTaskPO> findByNameLike(String name);
+    List<CrowdTestTaskPO> findByNameLikeAndIsDeleted(String name, int isDeleted);
 
     Page<CrowdTestTaskPO> findAll(Specification specification, Pageable pageable);
 

+ 2 - 0
core/src/main/java/com/mooctest/crowd/domain/domainobject/CrowdTestTask.java

@@ -27,6 +27,8 @@ public class CrowdTestTask {
     private String code;
     private String crowdTestProjectCode;
     private Long evaluationAgencyId;
+    private String fieldType;
+    private String applicationType;
     private String type;
     private String description;
     private String requirementFile;

+ 8 - 0
core/src/main/java/com/mooctest/crowd/domain/exception/FileCopyException.java

@@ -0,0 +1,8 @@
+package com.mooctest.crowd.domain.exception;
+
+/**
+ * @author guochao
+ * @date 2021-04-13 16:26
+ */
+public class FileCopyException extends BaseException {
+}

+ 8 - 0
core/src/main/java/com/mooctest/crowd/domain/exception/FileDownloadException.java

@@ -0,0 +1,8 @@
+package com.mooctest.crowd.domain.exception;
+
+/**
+ * @author guochao
+ * @date 2021-04-13 16:28
+ */
+public class FileDownloadException extends BaseException {
+}

+ 8 - 0
core/src/main/java/com/mooctest/crowd/domain/exception/FileUploadFailureException.java

@@ -0,0 +1,8 @@
+package com.mooctest.crowd.domain.exception;
+
+/**
+ * @author guochao
+ * @date 2021-04-13 16:34
+ */
+public class FileUploadFailureException extends BaseException {
+}

+ 8 - 0
core/src/main/java/com/mooctest/crowd/domain/exception/IllegalStateUploadException.java

@@ -0,0 +1,8 @@
+package com.mooctest.crowd.domain.exception;
+
+/**
+ * @author guochao
+ * @date 2021-04-14 10:14
+ */
+public class IllegalStateUploadException extends BaseException {
+}

+ 0 - 15
core/src/main/java/com/mooctest/crowd/domain/repository/CommonRepo.java

@@ -216,21 +216,6 @@ public class CommonRepo {
 
     private Specification<CrowdTestProjectPO> getCode(String code, String keyword) {
         return new Specification<CrowdTestProjectPO>() {
-//            @Override
-//            public Predicate toPredicate(Root<CrowdTestProjectPO> a, CriteriaQuery<?> q, CriteriaBuilder cb) {
-//                Predicate predicate = cb.conjunction();
-//                if (code != null) {
-//                    predicate.getExpressions().add(cb.equal(a.get("applicationType"), code));
-//                }
-//                if (keyword != null && keyword != "") {
-//                    predicate.getExpressions().add(
-//                            cb.like(a.<String>get("name"), "%" + StringUtils.trim(keyword) + "%")
-//                    );
-//                }
-////                q.orderBy(cb.desc(a.get("status").as(Integer.class)));
-//                return predicate;
-//            }
-
             @Override
             public Predicate toPredicate(Root<CrowdTestProjectPO> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                 //用于暂时存放查询条件的集合

+ 29 - 48
core/src/main/java/com/mooctest/crowd/domain/repository/CrowdTestProjectRepo.java

@@ -58,18 +58,18 @@ public class CrowdTestProjectRepo implements ICrowdTestProjectRepo {
     private CommonRepo commonRepo;
 
     @Override
-    public Page<CrowdTestProjectPO> findAll(Specification specification, Pageable pageable) {
-        return crowdTestProjectDao.findAll(specification, pageable);
+    public List<CrowdTestProject> findAll() {
+        return crowdTestProjectDao.findAll().stream().map(crowdTestProjectPO ->  Converter.convert(CrowdTestProject.class,crowdTestProjectPO)).collect(Collectors.toList());
     }
 
     @Override
-    public List<CrowdTestProjectPO> findAll() {
-        return crowdTestProjectDao.findAll();
+    public List<CrowdTestProject> findAllHotProject() {
+        return crowdTestProjectDao.findAllHotProject().stream().map(crowdTestProjectPO ->  Converter.convert(CrowdTestProject.class,crowdTestProjectPO)).collect(Collectors.toList());
     }
 
     @Override
-    public List<CrowdTestProjectPO> findByNameLike(String name) {
-        return     crowdTestProjectDao.findByNameLikeAndIsDeleted("%" + name + "%", DeletedStatus.isNotDeleted);
+    public List<CrowdTestProject> findByNameLike(String name) {
+        return crowdTestProjectDao.findByNameLikeAndIsDeleted("%" + name + "%", DeletedStatus.isNotDeleted).stream().map(crowdTestProjectPO -> Converter.convert(CrowdTestProject.class, crowdTestProjectPO)).collect(Collectors.toList());
     }
 
     @Override
@@ -101,6 +101,15 @@ public class CrowdTestProjectRepo implements ICrowdTestProjectRepo {
     }
 
     @Override
+    public CrowdTestProject getByProjectCodeJustInfo(String crowdTestProjectCode) throws CrowdTestProjectNotExistException {
+        CrowdTestProjectPO crowdTestProjectPO = crowdTestProjectDao.findByCodeAndIsDeleted(crowdTestProjectCode, DeletedStatus.isNotDeleted);
+        if (crowdTestProjectPO == null) {
+            throw new CrowdTestProjectNotExistException();
+        }
+        return Converter.convert(CrowdTestProject.class, crowdTestProjectPO);
+    }
+
+    @Override
     public CrowdTestProject getByProjectCode(String crowdTestProjectCode) throws CrowdTestProjectNotExistException {
         return getCrowdTestProject(null, crowdTestProjectDao.findByCodeAndIsDeleted(crowdTestProjectCode, DeletedStatus.isNotDeleted));
     }
@@ -273,6 +282,11 @@ public class CrowdTestProjectRepo implements ICrowdTestProjectRepo {
         }
     }
 
+    @Override
+    public List<CrowdTestProject> findHotProjectCardLimitCount(int projectCount) {
+        return crowdTestProjectDao.findHotProjectCardLimitCount(projectCount).stream().map(crowdTestProjectPO -> Converter.convert(CrowdTestProject.class, crowdTestProjectPO)).collect(Collectors.toList());
+    }
+
 //    @Override
 //    public void removeCrowdTestProject(String crowdTestProjectCode){
 //        CrowdTestProject crowdTestProject = getByProjectCode(crowdTestProjectCode);
@@ -288,48 +302,6 @@ public class CrowdTestProjectRepo implements ICrowdTestProjectRepo {
         crowdTestProjectDao.deleteAll(crowdTestProjectPOList);
     }
 
-
-//    /**
-//     * 新建项目
-//     * @param crowdTestProject
-//     * @return
-//     */
-//    @Override
-//    public CrowdTestProject saveCreateCrowdTestProject(CrowdTestProject crowdTestProject) {
-//        CrowdTestProjectPO crowdTestProjectPO = Converter.convert(CrowdTestProjectPO.class, crowdTestProject);
-//        CrowdTestProjectPO saveResult = crowdTestProjectDao.save(crowdTestProjectPO);
-//        return Converter.convert(CrowdTestProject.class, saveResult);
-//    }
-//
-//    /**
-//     * 在项目上新建一个任务进行保存
-//     * @param crowdTestProjectCode, crowdTestTask
-//     * @return
-//     */
-//    @Override
-//    public boolean saveCreateCrowdTestTask(String crowdTestProjectCode,CrowdTestTask crowdTestTask) {
-//        CrowdTestTaskPO crowdTestTaskPO = Converter.convert(CrowdTestTaskPO.class, crowdTestTask);
-//        crowdTestTaskDao.save(crowdTestTaskPO);
-//        return true;
-//    }
-//
-//
-//
-//    @Override
-//    public boolean saveCreateCrowdTestReport(String crowdTestProjectCode, String crowdTestTaskCode, CrowdTestReport crowdTestReport) {
-//        CrowdTestProject crowdTestProject = getByProjectCode(crowdTestProjectCode);
-//        List<CrowdTestTask> crowdTestTaskList = crowdTestProject.getCrowdTestTaskList();
-//        for(CrowdTestTask crowdTestTask : crowdTestTaskList){
-//            if(crowdTestTaskCode.equals(crowdTestTask.getCode())){
-//                CrowdTestReportPO crowdTestReportPO = Converter.convert(CrowdTestReportPO.class, crowdTestReport);
-//                crowdTestReportDao.save(crowdTestReportPO);
-//            }
-//        }
-//
-//        return true;
-//    }
-
-
     /**
      * 根据projectId删除Project
      *
@@ -403,6 +375,11 @@ public class CrowdTestProjectRepo implements ICrowdTestProjectRepo {
         return crowdTestProjectResult;
     }
 
+    public List<CrowdTestTask> getTaskBaseInfoByProjectCode(String projectCode){
+        List<CrowdTestTaskPO> taskPOS = crowdTestTaskDao.findByCrowdTestProjectCodeAndIsDeleted(projectCode, DeletedStatus.isNotDeleted);
+        return taskPOS.stream().map(crowdTestTaskPO ->  Converter.convert(CrowdTestTask.class, crowdTestTaskPO)).collect(Collectors.toList());
+    }
+
     public CrowdTestTask getTaskDetail(CrowdTestTask crowdTestTask, Long userId){
         // 检索任务中的所有已接收任务的人员信息
         List<TaskToUserPO> taskToUserPOList = taskToUserDao.findByTaskCode(crowdTestTask.getCode()).stream().filter(taskToUserPO -> taskToUserPO.getUserId().equals(userId)).collect(Collectors.toList());
@@ -467,6 +444,10 @@ public class CrowdTestProjectRepo implements ICrowdTestProjectRepo {
         return crowdTestProjectListResult;
     }
 
+    public List<CrowdTestProject> findSquareIndexProject(int indexCount){
+        return crowdTestProjectDao.findSquareIndexProject(indexCount).stream().map(projectPO -> Converter.convert(CrowdTestProject.class, projectPO)).collect(Collectors.toList());
+    }
+
     public long getAllProjectNum() {
         return crowdTestProjectDao.count();
     }

+ 5 - 0
core/src/main/java/com/mooctest/crowd/domain/repository/CrowdTestTaskRepo.java

@@ -75,4 +75,9 @@ public class CrowdTestTaskRepo implements ICrowdTaskRepo{
         return endPointDao.findAll().stream().filter(endPointPO -> endPointPO.getIsShowed() == 1).map(endPointPO -> Converter.convert(EndPoint.class, endPointPO)).collect(Collectors.toList());
     }
 
+    @Override
+    public List<CrowdTestTask> findByNameLikeAndIsDeleted(String name, int isNotDeleted) {
+        return taskDao.findByNameLikeAndIsDeleted(name, isNotDeleted).stream().map(crowdTestTaskPO -> Converter.convert(CrowdTestTask.class, crowdTestTaskPO)).collect(Collectors.toList());
+    }
+
 }

+ 2 - 0
core/src/main/java/com/mooctest/crowd/domain/repository/ICrowdTaskRepo.java

@@ -15,4 +15,6 @@ public interface ICrowdTaskRepo {
      void deleteTaskToUser(TaskToUser taskToUser);
 
      List<EndPoint> getEndPointShowList();
+
+    List<CrowdTestTask> findByNameLikeAndIsDeleted(String name, int isNotDeleted);
 }

+ 7 - 6
core/src/main/java/com/mooctest/crowd/domain/repository/ICrowdTestProjectRepo.java

@@ -2,10 +2,8 @@ package com.mooctest.crowd.domain.repository;
 import com.mooctest.crowd.domain.domainobject.CrowdTestProject;
 import com.mooctest.crowd.domain.domainobject.EndPoint;
 import com.mooctest.crowd.domain.exception.CrowdTestProjectNotExistException;
-import com.mooctest.crowd.domain.model.CrowdTestProjectPO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.domain.Specification;
 
 import java.util.List;
 
@@ -14,18 +12,20 @@ import java.util.List;
  * @date 2019/7/6 19:10
  */
 public interface ICrowdTestProjectRepo {
-    List<CrowdTestProjectPO> findByNameLike(String name);
+    Page<CrowdTestProject> findAllByPage(Pageable pageable, String keyword, int deletedStatus);
 
+    List<CrowdTestProject> findAll();
 
-    Page<CrowdTestProject> findAllByPage(Pageable pageable, String keyword, int deletedStatus);
-    Page<CrowdTestProjectPO> findAll(Specification specification,Pageable pageable);
+    List<CrowdTestProject> findAllHotProject();
 
-    List<CrowdTestProjectPO> findAll();
+    List<CrowdTestProject> findByNameLike(String name);
 
     CrowdTestProject getByID(Long id) throws CrowdTestProjectNotExistException;
 
     CrowdTestProject getByProjectCode(String crowdTestProjectCode);
 
+    CrowdTestProject getByProjectCodeJustInfo(String crowdTestProjectCode) throws CrowdTestProjectNotExistException;
+
     CrowdTestProject getByProjectCodeAndTaskCode(String crowdTestProjectCode, String taskCode) throws CrowdTestProjectNotExistException;
 
     List<CrowdTestProject> getAllCrowdTestProject();
@@ -65,4 +65,5 @@ public interface ICrowdTestProjectRepo {
 
     EndPoint updateEndPoint(String taskCode,EndPoint endPoint);
 
+    List<CrowdTestProject> findHotProjectCardLimitCount(int projectCount);
 }

+ 6 - 0
site/pom.xml

@@ -197,6 +197,12 @@
 			<version>5.3.5</version>
 			<scope>compile</scope>
 		</dependency>
+
+		<dependency>
+			<groupId>org.reflections</groupId>
+			<artifactId>reflections</artifactId>
+			<version>0.9.11</version>
+		</dependency>
 	</dependencies>
 	<build>
 		<plugins>

+ 9 - 1
site/src/main/java/com/mooctest/crowd/site/configuration/OSSConfiguration.java

@@ -1,6 +1,7 @@
 package com.mooctest.crowd.site.configuration;
 
 
+import com.aliyun.oss.ClientBuilderConfiguration;
 import com.aliyun.oss.OSS;
 import com.aliyun.oss.OSSClientBuilder;
 import lombok.Data;
@@ -28,11 +29,18 @@ public class OSSConfiguration {
     @Value("${oss.bucketName}")
     private String bucketName;
 
+    @Value("${oss.idleConnectionTime}")
+    private String idleConnectionTime;
+
     public String getBaseUrl() {
-        return "http://"+bucketName+"."+endPoint+"/";
+        return "http://" + bucketName + "." + endPoint + "/";
     }
 
     public OSS ossClient(){
         return new OSSClientBuilder().build(endPoint, accessKeyId, accessKeySecret);
     }
+
+    public OSS ossClientConf(ClientBuilderConfiguration conf){
+        return new OSSClientBuilder().build(endPoint, accessKeyId, accessKeySecret, conf);
+    }
 }

+ 26 - 0
site/src/main/java/com/mooctest/crowd/site/constants/FileConstant.java

@@ -0,0 +1,26 @@
+package com.mooctest.crowd.site.constants;
+
+public class FileConstant {
+
+  /**
+   * 保存文件所在路径的key,eg.FILE_MD5:1243jkalsjflkwaejklgjawe
+   */
+  public static final String FILE_MD5_KEY = "FILE_MD5:";
+  /**
+   * 保存上传文件的状态
+   */
+  public static final String FILE_UPLOAD_STATUS = "FILE_UPLOAD_STATUS";
+
+  public static final String TYPE_FOLDER = "folder";
+
+  public static final String MODE_ALL = "-rwx rwx rwx(0777)";
+
+  public static final String FILE_SEPARATORCHAR = "/";
+
+  public static final String FILE_ATOMIC_STATUS = "FILE_ATOMIC_STATUS:";
+
+  public static final long EXPIRE = 10;
+
+  public static final String ROOT_PATH_ID = "1";
+
+}

+ 2 - 0
site/src/main/java/com/mooctest/crowd/site/constants/UploadType.java

@@ -14,4 +14,6 @@ public class UploadType {
     public static final String AUTH_INFO = "AuthInfo/";
     public static final String EXPORT_TASK_FILE = "ExportTask/";
     public static final String OTHERS = "Others/";
+    public static final Integer SIMPLE_UPLOAD = 0;
+    public static final Integer SLICE_UPLOAD = 1;
 }

+ 42 - 30
site/src/main/java/com/mooctest/crowd/site/controller/CrowTestSquareController.java

@@ -5,7 +5,6 @@ import com.mooctest.crowd.site.data.response.ResponseVO;
 import com.mooctest.crowd.site.data.response.ServerCode;
 import com.mooctest.crowd.site.data.vo.CrowdProjectVO;
 import com.mooctest.crowd.site.data.vo.CrowdTaskVO;
-import com.mooctest.crowd.site.data.vo.CrowdTestProjectVO;
 import com.mooctest.crowd.site.data.vo.SearchConditionVO;
 import com.mooctest.crowd.site.service.CrowdProjectService;
 import com.mooctest.crowd.site.service.CrowdTaskService;
@@ -13,6 +12,7 @@ import com.mooctest.crowd.site.service.CrowdTestSquareService;
 import com.mooctest.crowd.site.util.DataUtils;
 import io.swagger.annotations.Api;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.web.bind.annotation.*;
@@ -38,52 +38,44 @@ public class CrowTestSquareController extends BaseSearchController {
     @Autowired
     private CrowdTestSquareService crowdTestSquareService;
 
+    @Value("${square.task.count}")
+    private int taskCount;
+
+    @Value("${square.project.count}")
+    private int projectCount;
+
+    @Value("${square.index.count}")
+    private int indexCount;
 
     /**
-     * 获取众测广场首页所有的数据,包装到CrowdTestSquareIndexDTO里面     *
+     * 获取众测广场首页所有的数据,包装到CrowdTestSquareIndexDTO里面*
      *
      * @return
      */
     @GetMapping("/list")
     public ResponseVO getAll() {
         CrowdTestSquareIndexDTO crowdTestSquareIndexDTO = new CrowdTestSquareIndexDTO();
-        List<CrowdTestProjectVO> list = crowdProjectService.findIndexProject();
-        List<CrowdTaskVO> crowdTestTasks = crowdTaskService.findAll();
-        crowdTestSquareIndexDTO.setCrowdTestProjectVOs(list);//众测项目
-        crowdTestSquareIndexDTO.setCrowdTestTaskVOS(crowdTestTasks);//众测任务
-        crowdTestSquareIndexDTO.setHotCrowdTaskVOs(crowdTaskService.findMoreHotTasks());//热门任务
-        crowdTestSquareIndexDTO.setHotCrowdTestProjectVOs(crowdProjectService.findAllMoreHotProjects());//热门项目
+        crowdTestSquareIndexDTO.setCrowdTestProjectVOs(crowdProjectService.findIndexProjectLimitCount(indexCount));//众测项目
+        crowdTestSquareIndexDTO.setCrowdTestTaskVOS(crowdTaskService.findIndexTaskLimitCount(indexCount));//众测任务
+//        crowdTestSquareIndexDTO.setHotCrowdTaskVOs(crowdTaskService.findMoreHotTasksList("", taskCount));//热门任务
+//        crowdTestSquareIndexDTO.setHotCrowdTestProjectVOs(crowdProjectService.findHotProjectCardLimitCount());//热门项目
         return new ResponseVO(ServerCode.SUCCESS, crowdTestSquareIndexDTO);
     }
 
-
     /**
-     * 根据测试类型筛选测试任务
-     *
+     * 获取热门测试任务、热门测试项目
      * @return
      */
-    @GetMapping("/task/{testTypeCode}")
-    public ResponseVO getAll(@PathVariable("testTypeCode") String testTypeCode) {
+    @GetMapping("/hotTaskAndProject")
+    public ResponseVO getHotTaskAndProject() {
         CrowdTestSquareIndexDTO crowdTestSquareIndexDTO = new CrowdTestSquareIndexDTO();
-        List<CrowdTaskVO> crowdTestTasks = crowdTaskService.findAllByTestTypeCode(testTypeCode);
-        crowdTestSquareIndexDTO.setCrowdTestTaskVOS(crowdTestTasks);//众测任务
-        crowdTestSquareIndexDTO.setHotCrowdTaskVOs(crowdTaskService.findMoreHotTasks());//热门任务
-        crowdTestSquareIndexDTO.setHotCrowdTestProjectVOs(crowdProjectService.findAllMoreHotProjects());//热门项目
+        crowdTestSquareIndexDTO.setHotCrowdTestProjectVOs(crowdProjectService.findHotProjectCardLimitCount(projectCount));//热门项目
+        crowdTestSquareIndexDTO.setHotCrowdTaskVOs(crowdTaskService.findMoreHotTasksList("", taskCount));//热门任务
         return new ResponseVO(ServerCode.SUCCESS, crowdTestSquareIndexDTO);
     }
 
     /**
-     * 搜索框模糊分页查询接口
-     */
-    @PostMapping("search/list")
-    public ResponseVO findByNameLike(@RequestBody SearchConditionVO searchConditionVO) {
-        return crowdTestSquareService.findByNameLike(searchConditionVO);
-    }
-
-
-    /**
      * 更多热门项目列表
-     *
      * @param searchConditionVO
      * @return
      */
@@ -96,10 +88,8 @@ public class CrowTestSquareController extends BaseSearchController {
         return new ResponseVO(ServerCode.SUCCESS, projectVOPage);
     }
 
-
     /**
      * 更多热门任务列表
-     *
      * @param searchConditionVO
      * @return
      */
@@ -107,11 +97,33 @@ public class CrowTestSquareController extends BaseSearchController {
     @PostMapping("/hotTasks/list")
     public ResponseVO findMoreHotTasksList(@RequestBody SearchConditionVO searchConditionVO) {
         Pageable pageable = this.getPageable(searchConditionVO);
-        List<CrowdTaskVO> crowdTestProjectVOList = crowdTaskService.findMoreHotTasksList(searchConditionVO.getKeyword());
+        List<CrowdTaskVO> crowdTestProjectVOList = crowdTaskService.findMoreHotTasksList(searchConditionVO.getKeyword(), 0);
         Page<CrowdTaskVO> taskVOPage = DataUtils.listToPage(crowdTestProjectVOList, pageable);
         return new ResponseVO(ServerCode.SUCCESS, taskVOPage);
     }
 
+    /**
+     * 根据测试类型筛选测试任务
+     *
+     * @return
+     */
+    @GetMapping("/task/{testTypeCode}")
+    public ResponseVO getAll(@PathVariable("testTypeCode") String testTypeCode) {
+        CrowdTestSquareIndexDTO crowdTestSquareIndexDTO = new CrowdTestSquareIndexDTO();
+        List<CrowdTaskVO> crowdTestTasks = crowdTaskService.findAllByTestTypeCode(testTypeCode);
+        crowdTestSquareIndexDTO.setCrowdTestTaskVOS(crowdTestTasks);//众测任务
+//        crowdTestSquareIndexDTO.setHotCrowdTestProjectVOs(crowdProjectService.findHotProjectCardLimitCount());//热门项目
+//        crowdTestSquareIndexDTO.setHotCrowdTaskVOs(crowdTaskService.findMoreHotTasksList("", taskCount));//热门任务
+        return new ResponseVO(ServerCode.SUCCESS, crowdTestSquareIndexDTO);
+    }
+
+    /**
+     * 搜索框模糊分页查询接口
+     */
+    @PostMapping("search/list")
+    public ResponseVO findByNameLike(@RequestBody SearchConditionVO searchConditionVO) {
+        return crowdTestSquareService.findByNameLike(searchConditionVO);
+    }
 
     @Override
     public Page<?> search(String searchCondition) {

+ 1 - 1
site/src/main/java/com/mooctest/crowd/site/controller/CrowdProjectController.java

@@ -9,7 +9,7 @@ import com.mooctest.crowd.site.command.GenerateProjectCommand;
 import com.mooctest.crowd.site.data.dto.ProjectDetailsDTO;
 import com.mooctest.crowd.site.data.vo.RegionalManagerVO;
 import com.mooctest.crowd.site.service.CrowdProjectService;
-import com.mooctest.crowd.site.util.FileUtil;
+import com.mooctest.crowd.site.util.file.FileUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;

+ 7 - 2
site/src/main/java/com/mooctest/crowd/site/controller/CrowdTaskController.java

@@ -154,10 +154,15 @@ public class CrowdTaskController{
         return new ResponseVO<>(ServerCode.SUCCESS, taskService.exportTask(projectCode, taskCode, userId));
     }
 
-    // 任务导入
+    /**
+     * 任务导入
+     * @param file
+     * @param session
+     * @return
+     */
     @RequestMapping(value = "/project/task/import", method = RequestMethod.POST)
     public ResponseVO<ProjectDetailsDTO> importTask(MultipartFile file, HttpSession session){
         Long userId = Long.parseLong((String)session.getAttribute("userId"));
-        return new ResponseVO<>(ServerCode.SUCCESS, taskService.importTask(file, userId));
+        return new ResponseVO<>(ServerCode.SUCCESS, taskService.importTask(file, userId, 0));
     }
 }

+ 75 - 0
site/src/main/java/com/mooctest/crowd/site/controller/FileController.java

@@ -0,0 +1,75 @@
+package com.mooctest.crowd.site.controller;
+
+import com.mooctest.crowd.domain.exception.FileDownloadException;
+import com.mooctest.crowd.site.data.dto.FileDownloadRequestDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+import com.mooctest.crowd.site.data.response.ResponseVO;
+import com.mooctest.crowd.site.data.response.ServerCode;
+import com.mooctest.crowd.site.service.FileService;
+import com.mooctest.crowd.site.util.file.FileUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.StopWatch;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+@Slf4j
+@Controller
+@RequestMapping(value="/")
+public class FileController {
+  @Autowired
+  private FileService fileService;
+
+  @Autowired
+  private HttpServletRequest request;
+
+  @Autowired
+  private HttpServletResponse response;
+
+  @PostMapping(value = "/upload")
+  @ResponseBody
+  public ResponseVO<FileUploadDTO> upload(FileUploadRequestDTO fileUploadRequestDTO) throws IOException {
+
+    FileUploadDTO fileUploadDTO;
+    StopWatch stopWatch = new StopWatch();
+    stopWatch.start("upload");
+
+    if (fileUploadRequestDTO.getChunk() != null && fileUploadRequestDTO.getChunks() > 0) {
+      fileUploadDTO = fileService.sliceUpload(fileUploadRequestDTO);
+    } else {
+      fileUploadDTO = fileService.upload(fileUploadRequestDTO);
+    }
+
+    stopWatch.stop();
+    log.info("{}",stopWatch.prettyPrint());
+
+    return new ResponseVO<>(ServerCode.SUCCESS, fileUploadDTO);
+  }
+
+  @RequestMapping(value = "/checkFileMd5", method = RequestMethod.POST)
+  @ResponseBody
+  public ResponseVO<FileUploadDTO> checkFileMd5(String md5, String path) throws IOException {
+    FileUploadRequestDTO param = new FileUploadRequestDTO().setPath(path).setMd5(md5);
+    FileUploadDTO fileUploadDTO = fileService.checkFileMd5(param);
+    return new ResponseVO<>(ServerCode.SUCCESS, fileUploadDTO);
+  }
+
+  @PostMapping("/download")
+  public void download(FileDownloadRequestDTO requestDTO) {
+      try {
+        FileUtil.downloadFile(requestDTO.getName(), requestDTO.getPath(), request, response);
+      } catch (FileNotFoundException e) {
+        log.error("download error:" + e.getMessage(), e);
+        throw new FileDownloadException();
+      }
+    }
+}

+ 16 - 18
site/src/main/java/com/mooctest/crowd/site/controller/UploadController.java

@@ -3,10 +3,7 @@ package com.mooctest.crowd.site.controller;
 import com.mooctest.crowd.site.service.UploadService;
 import io.swagger.annotations.Api;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 /**
@@ -22,38 +19,39 @@ public class UploadController {
     @Autowired
     private UploadService uploadService;
 
+    // 需要验证身份
     @RequestMapping(value = "/requirementfile/{userId}", method = RequestMethod.POST)
-    public String uploadRequirementDoc(MultipartFile file, @PathVariable("userId") Long userId){
-        return uploadService.uploadRequirment(file, userId);
+    public String uploadRequirementDoc(MultipartFile file, @PathVariable("userId") Long userId, @RequestParam(value = "uploadType", defaultValue = "0") String uploadType){
+        return uploadService.uploadRequirement(file, userId, Integer.parseInt(uploadType));
     }
 
     @RequestMapping(value = "/taskFile/{userId}", method = RequestMethod.POST)
-    public String uploadTaskFile(MultipartFile file, @PathVariable("userId") Long userId){
-        return uploadService.uploadTask(file, userId);
+    public String uploadTaskFile(MultipartFile file, @PathVariable("userId") Long userId, @RequestParam(value = "uploadType", defaultValue = "0") String uploadType){
+        return uploadService.uploadTask(file, userId, Integer.parseInt(uploadType));
     }
 
     @RequestMapping(value = "/apk/{userId}", method = RequestMethod.POST)
-    public String uploadApk(MultipartFile file, @PathVariable("userId") Long userId){
-        return uploadService.uploadAPK(file, userId);
+    public String uploadApk(MultipartFile file, @PathVariable("userId") Long userId, @RequestParam(value = "uploadType", defaultValue = "0") String uploadType){
+        return uploadService.uploadAPK(file, userId, Integer.parseInt(uploadType));
     }
 
     @RequestMapping(value = "/report/{userId}", method = RequestMethod.POST)
-    public String uploadReport(MultipartFile file, @PathVariable("userId") Long userId){
-        return uploadService.uploadReport(file, userId);
+    public String uploadReport(MultipartFile file, @PathVariable("userId") Long userId, @RequestParam(value = "uploadType", defaultValue = "0") String uploadType){
+        return uploadService.uploadReport(file, userId, Integer.parseInt(uploadType));
     }
 
     @RequestMapping(value = "/image/{userId}", method = RequestMethod.POST)
-    public String uploadImage(MultipartFile file, @PathVariable("userId") Long userId){
-        return uploadService.uploadImage(file, userId);
+    public String uploadImage(MultipartFile file, @PathVariable("userId") Long userId, @RequestParam(value = "uploadType", defaultValue = "0") String uploadType){
+        return uploadService.uploadImage(file, userId, Integer.parseInt(uploadType));
     }
 
     @RequestMapping(value = "/authinfo/{userId}", method = RequestMethod.POST)
-    public String uploadAuthInfo(MultipartFile file, @PathVariable("userId") Long userId){
-        return uploadService.uploadAuthInfo(file, userId);
+    public String uploadAuthInfo(MultipartFile file, @PathVariable("userId") Long userId, @RequestParam(value = "uploadType", defaultValue = "0") String uploadType){
+        return uploadService.uploadAuthInfo(file, userId, Integer.parseInt(uploadType));
     }
 
     @RequestMapping(value = "/generalFile", method = RequestMethod.POST)
-    public String uploadGeneralFile(MultipartFile file){
-        return uploadService.uploadGeneralFile(file);
+    public String uploadGeneralFile(MultipartFile file, @RequestParam(value = "uploadType", defaultValue = "0") String uploadType){
+        return uploadService.uploadGeneralFile(file, Integer.parseInt(uploadType));
     }
 }

+ 8 - 0
site/src/main/java/com/mooctest/crowd/site/controller/advice/ExceptionAdvice.java

@@ -61,6 +61,14 @@ public class ExceptionAdvice {
             return "导出的任务文件所在的文件夹不存在。";
         } else if(e instanceof FileIsEmptyException){
             return "文件不存在或文件是空文件,请重新上传。";
+        } else if(e instanceof FileCopyException){
+            return "复制文件错误。";
+        } else if(e instanceof FileDownloadException){
+            return "文件下载失败。";
+        } else if(e instanceof FileUploadFailureException){
+            return "文件上传失败。";
+        } else if(e instanceof IllegalStateUploadException){
+            return "文件上传失败,由于存在部分分片未上传完成。";
         } else {
             return e.getMessage();
         }

+ 1 - 1
site/src/main/java/com/mooctest/crowd/site/controller/interceptor/FileCheckInterceptor.java

@@ -1,7 +1,7 @@
 package com.mooctest.crowd.site.controller.interceptor;
 
 import com.mooctest.crowd.domain.exception.BaseException;
-import com.mooctest.crowd.site.util.FileUtil;
+import com.mooctest.crowd.site.util.file.FileUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.springframework.web.multipart.MultipartFile;

+ 13 - 0
site/src/main/java/com/mooctest/crowd/site/data/dto/FileDownloadRequestDTO.java

@@ -0,0 +1,13 @@
+package com.mooctest.crowd.site.data.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class FileDownloadRequestDTO {
+    private String path;
+    private String name;
+}

+ 25 - 0
site/src/main/java/com/mooctest/crowd/site/data/dto/FileUploadDTO.java

@@ -0,0 +1,25 @@
+package com.mooctest.crowd.site.data.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Map;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class FileUploadDTO {
+  private String path;
+  private Integer mtime;
+  private boolean uploadComplete;
+  private int code;
+  private Map<Integer,String> chunkMd5Info;
+  private List<Integer> missChunks;
+  private long size;
+  private String fileExt;
+  private String fileId;
+}

+ 46 - 0
site/src/main/java/com/mooctest/crowd/site/data/dto/FileUploadRequestDTO.java

@@ -0,0 +1,46 @@
+package com.mooctest.crowd.site.data.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import org.springframework.web.multipart.MultipartFile;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+@Builder
+public class FileUploadRequestDTO {
+    //上传文件到指定目录
+    private String path;
+    //上传文件的文件名称
+    private String name;
+    //任务ID
+    private String id;
+    //总分片数量
+    private Integer chunks;
+    //当前为第几块分片
+    private Integer chunk;
+    //按多大的文件粒度进行分片
+    private Long chunkSize;
+    //分片对象
+    private MultipartFile file;
+    // MD5
+    private String md5;
+    //当前分片大小
+    private Long size = 0L;
+
+    public FileUploadRequestDTO(MultipartFile file){
+        this.path = "/Users/guochao/Desktop/project/data/cofortest/";
+        this.name = "test2";
+        this.id = "id001";
+        this.chunks = 1;
+        this.chunk = 1;
+        this.chunkSize = 1L * 1024 * 1024;
+        this.file = file;
+        this.md5 = "md5";
+    }
+
+}

+ 81 - 0
site/src/main/java/com/mooctest/crowd/site/data/enums/DateType.java

@@ -0,0 +1,81 @@
+package com.mooctest.crowd.site.data.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 日期类型
+ *
+ *
+ */
+public enum DateType {
+    /**
+     * 年(yyyy)
+     */
+    YEAR("1", "yyyy"),
+    /**
+     * 年月(yyyy-MM)
+     */
+    MONTH("2", "yyyy-MM"),
+
+    UP_MONTH_FOR_YEAR("12", "yyyy-MM"),
+    /**
+     * 年周(yyyy|ww)
+     * 每年第一周的起始时间为当年第一天,每年最后一周的结束时间为当当年最后一天
+     */
+    YEAR_WEEK("3", "yyyy|ww"),
+
+    UP_YEAR_WEEK_FOR_YEAR("13", "yyyy|ww"),
+    /**
+     * 年月周(yyyy-MM|W)
+     * 每月第一周的起始时间为当月第一天,每月最后一周的结束时间为当月最后一天
+     */
+    MONTH_WEEK("4", "yyyy-MM|W"),
+
+    UP_MONTH_WEEK_FOR_YEAR("14", "yyyy-MM|W"),
+    /**
+     * 年月日(yyyy-MM-dd)
+     */
+    DAY("5", "yyyy-MM-dd"),
+
+    UP_DAY_FOR_YEAR("15", "yyyy-MM-dd");
+
+    private String code;
+
+    private String pattern;
+
+    DateType(String code, String pattern) {
+        this.code = code;
+        this.pattern = pattern;
+    }
+
+    @JsonValue
+    public String getCode() {
+        return code;
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    public static DateType getInstance(String name) {
+        return valueOf(name.toUpperCase());
+    }
+
+    private static Map<String, DateType> enumMap = new HashMap<>();
+
+    static {
+        DateType[] enums = values();
+        for (DateType dateType : enums) {
+            enumMap.put(dateType.getCode(), dateType);
+        }
+    }
+
+    @JsonCreator
+    public static DateType getInstanceByCode(String code) {
+        return enumMap.get(code);
+    }
+}

+ 32 - 0
site/src/main/java/com/mooctest/crowd/site/data/enums/FileCheckMd5Status.java

@@ -0,0 +1,32 @@
+package com.mooctest.crowd.site.data.enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum FileCheckMd5Status {
+
+    FILE_UPLOADED(200, "文件已存在!"),
+
+    FILE_NO_UPLOAD(404, "该文件没有上传过。"),
+
+    FILE_UPLOAD_SOME(206, "该文件上传了一部分。");
+
+
+    private final int value;
+
+    private final String reasonPhrase;
+
+
+    FileCheckMd5Status(int value, String reasonPhrase) {
+        this.value = value;
+        this.reasonPhrase = reasonPhrase;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public String getReasonPhrase() {
+        return reasonPhrase;
+    }
+}

+ 3 - 0
site/src/main/java/com/mooctest/crowd/site/data/vo/CrowdTaskVO.java

@@ -31,6 +31,8 @@ public class CrowdTaskVO implements Serializable{
     private String projectId;
     private String title;
     private String description;
+    private String fieldType;
+    private String applicationType;
     private Double quotePrice;
     private Double fixedPrice;
     private String requirementFile;
@@ -59,6 +61,7 @@ public class CrowdTaskVO implements Serializable{
     private String exportUrl;
 
     public CrowdTaskVO(CrowdTestTask task){
+        BeanUtils.copyProperties(task, this);
         id = task.getCode();
         code = task.getCode();
         projectId = task.getCrowdTestProjectCode();

+ 19 - 7
site/src/main/java/com/mooctest/crowd/site/mediator/ViewMediator.java

@@ -1,6 +1,7 @@
 package com.mooctest.crowd.site.mediator;
 
 import com.mooctest.crowd.domain.domainobject.CrowdTestProject;
+import com.mooctest.crowd.domain.domainobject.CrowdTestTask;
 import com.mooctest.crowd.domain.domainobject.User;
 import com.mooctest.crowd.domain.exception.AccountNotExistException;
 import com.mooctest.crowd.domain.exception.BadRequestException;
@@ -15,6 +16,7 @@ import org.codehaus.jettison.json.JSONException;
 import org.springframework.data.domain.Pageable;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @Author: xuexb
@@ -23,8 +25,6 @@ import java.util.List;
 public interface ViewMediator {
     List<CrowdTestProjectVO> crowdTestProjects();
 
-    List<CrowdTaskVO> findIndexTask();
-
     void saveEnterpriseRole(User user, ApplyEnterpriseAuthCommand applyEnterpriseAuthCommand);
 
     void saveUserRole(User user, ApplyPersonalAuthCommand command);
@@ -56,6 +56,20 @@ public interface ViewMediator {
 
     TaskSquareDTO renderTaskSquare();
 
+    CrowdProjectVO changeFieldAndApplicationAndTestType(CrowdProjectVO projectVO, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap);
+
+    CrowdTestProject changeFieldAndApplicationAndTestType(CrowdTestProject project, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap);
+
+    CrowdTestTask changeFieldAndApplicationAndTestTypeByTask(CrowdTestTask task, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap);
+
+    CrowdTestTask changeTypeByProjectAndTask(CrowdTestProject project, CrowdTestTask task, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap);
+
+    CrowdTestTask changeTypeByProjectAndTask(CrowdTestProject project, CrowdTestTask task);
+
     ProjectDetailsDTO renderProjectDetails(CrowdTestProject project, User user);
 
     ReportDetailsDTO renderTaskReportDetails(String projectCode, String taskCode, String reportCode, Long userId);
@@ -86,12 +100,9 @@ public interface ViewMediator {
     List<CrowdTestProjectVO> AllByPage();
 
     //热门项目
-    List<CrowdTestProjectVO> hotCrowdTestProjects();
-
+    List<CrowdTestProjectVO> hotProjectCardLimitCount(int projectCount);
 
-    List<CrowdTestProjectVO> indexCrowdTestProjects();
-
-    List<CrowdTaskVO> crowdTaskVos();
+    List<CrowdTaskVO> findIndexTaskLimitCount(int indexCount);
 
     List<CrowdTaskVO> crowdTaskVOSByTestTypeCode(String testTypeCode);
 
@@ -132,4 +143,5 @@ public interface ViewMediator {
     List<UserTaskCountVO> getCount();
 
     void  jumpPublicTesting(String projectCode, String taskCode, Long userId);
+
 }

+ 171 - 133
site/src/main/java/com/mooctest/crowd/site/mediator/impl/WebMediatorImpl.java

@@ -50,6 +50,9 @@ public class WebMediatorImpl implements ViewMediator {
     private CrowdTestProjectRepo projectRepo;
 
     @Autowired
+    private CrowdTestTaskRepo taskRepo;
+
+    @Autowired
     private UserRepo userRepo;
 
     @Autowired
@@ -138,24 +141,13 @@ public class WebMediatorImpl implements ViewMediator {
 
     @Override
     public List<CrowdTestProjectVO> crowdTestProjects() {
-        List<CrowdTestProjectVO> authingList = new ArrayList<>();
-        authingList.addAll(projectDao.findAll().stream().map(crowdTestProjectPO -> {
+        List<CrowdTestProjectVO> resultList = new ArrayList<>();
+        resultList.addAll(projectDao.findAll().stream().map(crowdTestProjectPO -> {
             CrowdTestProject crowdTestProject = new CrowdTestProject();
             BeanUtils.copyProperties(crowdTestProjectPO, crowdTestProject);
             return new CrowdTestProjectVO(crowdTestProject);
         }).collect(Collectors.toList()));
-        return authingList;
-    }
-
-    @Override
-    public List<CrowdTaskVO> findIndexTask() {
-        List<CrowdTaskVO> authingList = new ArrayList<>();
-        authingList.addAll(taskDao.findindexTask().stream().map(crowdTestProjectPO -> {
-            CrowdTestTask crowdTestTask = new CrowdTestTask();
-            BeanUtils.copyProperties(crowdTestProjectPO, crowdTestTask);
-            return new CrowdTaskVO(crowdTestTask);
-        }).collect(Collectors.toList()));
-        return authingList;
+        return resultList;
     }
 
     @Override
@@ -372,7 +364,6 @@ public class WebMediatorImpl implements ViewMediator {
         }
 
         // 获取热门任务
-
         List<CrowdTaskVO> taskVOList = taskDao.findMoreHotTasks().stream().map(crowdTestTaskPO -> {
             CrowdTestTask task = new CrowdTestTask();
             BeanUtils.copyProperties(crowdTestTaskPO, task);
@@ -613,9 +604,10 @@ public class WebMediatorImpl implements ViewMediator {
         User user = userRepo.getByID(userId);
         //我的众测 - 项目相关信息
         
-        // 获取应用、测试类型code 和 name 的map
+        // 获取领域、应用、测试类型code 和 name 的map
+        Map<String, String> fieldMap = commonRepo.getFieldCodeNameMap();
         Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
-        Map<String, String> typeMap = commonRepo.getTypeCodeNameMap();
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
 
         //获取我创建的项目列表
         List<CrowdProjectVO> myProjects = projectDao.findByUserIdAndIsDeleted(userId, DeletedStatus.isNotDeleted)
@@ -626,8 +618,8 @@ public class WebMediatorImpl implements ViewMediator {
 //                    themeStatusService.updateStatus(project);
                     if (project.getStatus() == CrowdTestProjectStatus.HAS_COMMITED)
                         projectVO.setNeedHandle(true);
-                    // 应用类型值的转换
-                    projectVO = changeApplicationType(projectVO, applicationMap);
+                    // 领域类型、应用类型值的转换
+                    projectVO = changeFieldAndApplicationAndTestType(projectVO, applicationMap, fieldMap, testMap);
 
                     // 判断项目是否截止
                     if (projectVO.getDatetime().getTime() <= System.currentTimeMillis() && project.getStatus() < CrowdTestProjectStatus.HAS_FINISHED) {
@@ -655,7 +647,7 @@ public class WebMediatorImpl implements ViewMediator {
                 BeanUtils.copyProperties(crowdTestProjectPO, project);
                 CrowdProjectVO projectVO = new CrowdProjectVO(project);
                 // 应用类型值的转换
-                projectVO = changeApplicationType(projectVO, applicationMap);
+                projectVO = changeFieldAndApplicationAndTestType(projectVO, applicationMap, fieldMap, testMap);
 
 //                List<CrowdTestTaskPO> tasksOfProject = taskDao.findByCrowdTestProjectCodeAndIsDeleted(project.getCode(), DeletedStatus.isNotDeleted);
 //                if (tasksOfProject != null && tasksOfProject.stream().anyMatch(task -> task.getStatus() == CrowdTestTaskStatus.HAS_COMMITED)) {
@@ -678,7 +670,7 @@ public class WebMediatorImpl implements ViewMediator {
                         CrowdTestProject project = new CrowdTestProject();
                         BeanUtils.copyProperties(crowdTestProjectPO, project);
                         CrowdProjectVO projectVO = new CrowdProjectVO(project);
-                        projectVO = changeApplicationType(projectVO, applicationMap);
+                        projectVO = changeFieldAndApplicationAndTestType(projectVO, applicationMap, fieldMap, testMap);
                         return projectVO;
                     }).collect(Collectors.toList()));
             myCrowdDTO.setAcceptableProjectNoticeCount(Long.parseLong("" + acceptableProject.size()));
@@ -707,7 +699,7 @@ public class WebMediatorImpl implements ViewMediator {
                                 CrowdTestTask task = new CrowdTestTask();
                                 BeanUtils.copyProperties(crowdTestTaskPO, task);
                                 // 测试类型的转换
-                                task.setType(typeMap.get(task.getType()));
+                                task.setType(testMap.get(task.getType()));
                                 // 判断任务是否截止
                                 if (task.getDeadTime().getTime() <= System.currentTimeMillis() && task.getStatus() < CrowdTestTaskStatus.HAS_FINISHED) {
                                     task.setStatus(CrowdTestTaskStatus.HAS_TIME_OUT);
@@ -788,23 +780,99 @@ public class WebMediatorImpl implements ViewMediator {
         return userDataDTO;
     }
 
-    public CrowdProjectVO changeApplicationType(CrowdProjectVO projectVO, Map<String, String> applicationMap) {
+    /**
+     * 项目领域、应用、测试类型的转换
+     * @param projectVO
+     * @param applicationMap
+     * @param fieldMap
+     * @param testMap
+     * @return
+     */
+    @Override
+    public CrowdProjectVO changeFieldAndApplicationAndTestType(CrowdProjectVO projectVO, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap) {
+        // 领域类型值的转换
+        projectVO.setField(fieldMap.get(projectVO.getField()));
         // 应用类型值的转换
-        String applicationName = applicationMap.get(projectVO.getPlatform());
-        projectVO.setPlatform(applicationName);
+        projectVO.setPlatform(applicationMap.get(projectVO.getPlatform()));
+        // 测试类型值的转换
+        projectVO.setType(projectVO.getType().stream().map(testType -> testMap.get(testType)).collect(Collectors.toList()));
         return projectVO;
     }
 
+    @Override
+    public CrowdTestProject changeFieldAndApplicationAndTestType(CrowdTestProject project, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap) {
+        // 领域类型值的转换
+        project.setFieldType(fieldMap.get(project.getFieldType()));
+        // 应用类型值的转换
+        project.setApplicationType(applicationMap.get(project.getApplicationType()));
+        // 测试类型值的转换
+        if(project.getType().contains("[")){
+            List<String> testTypeCodeList = (List<String>)JSONArray.parse(project.getType());
+            project.setType(testTypeCodeList.stream().map(testType -> '"'+ testMap.get(testType) + '"').collect(Collectors.toList()).toString());
+        }else{
+            project.setType(testMap.get(project.getType()));
+        }
+        return project;
+    }
+
+    @Override
+    public CrowdTestTask changeFieldAndApplicationAndTestTypeByTask(CrowdTestTask task, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap) {
+        // 领域类型值的转换
+        task.setFieldType(fieldMap.get(task.getFieldType()));
+        // 应用类型值的转换
+        task.setApplicationType(applicationMap.get(task.getApplicationType()));
+        // 测试类型值的转换
+        task.setType(testMap.get(task.getType()));
+        return task;
+    }
+
+    @Override
+    public CrowdTestTask changeTypeByProjectAndTask(CrowdTestProject project, CrowdTestTask task, Map<String, String> applicationMap
+            , Map<String, String> fieldMap, Map<String, String> testMap) {
+        // 获取项目的领域、应用类型赋值给任务
+        task.setFieldType(project.getFieldType());
+        task.setApplicationType(project.getApplicationType());
+        // 领域类型值的转换
+        task.setFieldType(fieldMap.get(task.getFieldType()));
+        // 应用类型值的转换
+        task.setApplicationType(applicationMap.get(task.getApplicationType()));
+        // 测试类型值的转换
+        task.setType(testMap.get(task.getType()));
+        return task;
+    }
+
+    @Override
+    public CrowdTestTask changeTypeByProjectAndTask(CrowdTestProject project, CrowdTestTask task) {
+        // 获取领域、应用、测试类型code 和 name 的map
+        Map<String, String> fieldMap = commonRepo.getFieldCodeNameMap();
+        Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+
+        // 获取项目的领域、应用类型赋值给任务
+        task.setFieldType(project.getFieldType());
+        task.setApplicationType(project.getApplicationType());
+        // 领域类型值的转换
+        task.setFieldType(fieldMap.get(task.getFieldType()));
+        // 应用类型值的转换
+        task.setApplicationType(applicationMap.get(task.getApplicationType()));
+        // 测试类型值的转换
+        task.setType(testMap.get(task.getType()));
+        return task;
+    }
+
 
     @Override
     public TaskSquareDTO renderTaskSquare() {
-        Map<String, String> typeMap = commonRepo.getTypeCodeNameMap();
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
 
         List<CrowdTaskVO> taskVOList = taskDao.findAllByIsDeleted(0).stream().map(crowdTestTaskPO -> {
             CrowdTestTask task = CrowdTestProjectFactory.defaultTask();
             BeanUtils.copyProperties(crowdTestTaskPO, task);
             // 测试类型的转换
-            task.setType(typeMap.get(task.getType()));
+            task.setType(testMap.get(task.getType()));
             return task;
         }).collect(Collectors.toList()).stream().filter(crowdTestTask -> crowdTestTask.getStatus() == CrowdTestTaskStatus.HAS_RELEASED ||
                 (crowdTestTask.getStatus() == CrowdTestTaskStatus.HAS_RECEIVED && crowdTestTask.getFullStatus() == CrowdTestTaskAcceptStatus.NOT_FULL))
@@ -818,8 +886,10 @@ public class WebMediatorImpl implements ViewMediator {
     @Override
     public ProjectDetailsDTO renderProjectDetails(CrowdTestProject project, User user) {
 
+        Map<String, String> fieldMap = commonRepo.getFieldCodeNameMap();
         Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
-        Map<String, String> typeMap = commonRepo.getTypeCodeNameMap();
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+
         boolean saveFlag = false;
         // 判断项目是否截止
         if (project.getDeadTime().getTime() <= System.currentTimeMillis() && project.getStatus() < CrowdTestProjectStatus.HAS_FINISHED) {
@@ -837,15 +907,13 @@ public class WebMediatorImpl implements ViewMediator {
                 saveFlag = true;
             }
 
+            // 类型的转换
+            this.changeTypeByProjectAndTask(project, crowdTestTask, applicationMap, fieldMap, testMap);
             CrowdTaskVO crowdTaskVO = new CrowdTaskVO(crowdTestTask);
             if (crowdTestTask.getDistributionType() == 0) {
                 EvaluationAgencyPO agencyPO = agencyDao.findByUserId(crowdTestTask.getEvaluationAgencyId());
                 crowdTaskVO.setInstitution(agencyPO == null ? "该机构已注销" : agencyPO.getEvaluationAgencyName());
             }
-            // 测试类型的转换
-            crowdTaskVO.setServiceType(typeMap.get(crowdTaskVO.getServiceType()));
-//            String typeName = commonRepo.getTypeNameByCode(crowdTaskVO.getServiceType());
-//            crowdTaskVO.setServiceType(typeName);
             taskVOList.add(crowdTaskVO);
         }
 
@@ -865,22 +933,8 @@ public class WebMediatorImpl implements ViewMediator {
         progress.add(new PieChartDataVO("进行中", crowdTestTaskList
                 .stream().filter(task -> task.getStatus() > CrowdTestTaskStatus.HAS_CREATED && task.getStatus() < CrowdTestTaskStatus.HAS_FINISHED).count()));
 
-        // 应用类型值的转换
-        projectVO = changeApplicationType(projectVO, applicationMap);
-
-        // 领域类型值的转换
-
-        Optional<FieldPO> fieldPO = fieldDao.findByCode(projectVO.getField());
-        if (fieldPO.isPresent()) {
-            projectVO.setField(fieldPO.get().getName());
-        } else {
-            throw new HttpBadRequestException("领域类型有误");
-        }
-
-        // 测试类型的转换
-        List<String> testStringList = projectVO.getType().stream().map(testType -> typeMap.get(testType)).collect(Collectors.toList());
-
-        projectVO.setType(testStringList);
+        // 项目类型值的转换
+        projectVO = changeFieldAndApplicationAndTestType(projectVO, applicationMap, fieldMap, testMap);
 
         projectDetailsDTO.setProjectDetails(projectVO);
         projectDetailsDTO.setTaskList(taskVOList);
@@ -900,7 +954,7 @@ public class WebMediatorImpl implements ViewMediator {
                 .stream().filter(crowdTestTask -> crowdTestTask.getCode().equals(taskCode)).findFirst();
         if (!task.isPresent())
             throw new CrowdTestTaskNotExistException();
-        System.out.println("renderTaskReportDetails userID: " + userId);
+        log.info("renderTaskReportDetails userID: " + userId);
         User user = userRepo.getByID(userId);
         ReportDetailsDTO reportDetailsDTO = new ReportDetailsDTO();
         if (userId.equals(projectRepo.getByProjectCode(projectCode).getRegionalManagerId())) {
@@ -949,6 +1003,10 @@ public class WebMediatorImpl implements ViewMediator {
 
     @Override
     public TaskDetailsDTO renderTaskDetails(String projectCode, String taskCode, Long userId) {
+//        Map<String, String> fieldMap = commonRepo.getFieldCodeNameMap();
+//        Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
+//        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+
         TaskDetailsDTO taskDetailsDTO = new TaskDetailsDTO();
         CrowdTestProject project = projectRepo.getByProjectCode(projectCode);
 
@@ -963,23 +1021,27 @@ public class WebMediatorImpl implements ViewMediator {
         if (!task.isPresent())
             throw new CrowdTestTaskNotExistException();
 
+        CrowdTestTask crowdTestTask = task.get();
+
         // 判断任务是否截止
-        if (task.get().getDeadTime().getTime() <= System.currentTimeMillis() && task.get().getStatus() < CrowdTestTaskStatus.HAS_FINISHED) {
-            task.get().setStatus(CrowdTestTaskStatus.HAS_TIME_OUT);
+        if (crowdTestTask.getDeadTime().getTime() <= System.currentTimeMillis() && crowdTestTask.getStatus() < CrowdTestTaskStatus.HAS_FINISHED) {
+            crowdTestTask.setStatus(CrowdTestTaskStatus.HAS_TIME_OUT);
             saveFlag = true;
         }
 
-        CrowdTaskVO taskVO = new CrowdTaskVO(task.get());
-        // 测试类型的转换
-//        String typeName = commonRepo.getTypeNameByCode(taskVO.getServiceType());
-//        taskVO.setServiceType(typeName);
+        // 获取项目的领域、应用类型赋值给任务
+        crowdTestTask.setFieldType(project.getFieldType());
+        crowdTestTask.setApplicationType(project.getApplicationType());
+
+//        this.changeTypeByProjectAndTask(project, crowdTestTask, applicationMap, fieldMap, testMap);
+        CrowdTaskVO taskVO = new CrowdTaskVO(crowdTestTask);
 
         log.info("renderTaskDetails userId:" + userId);
         if (userId == null) {
-            taskDetailsDTO.setTaskOperationControl(this.initTaskPermission(project, task.get(), null));
+            taskDetailsDTO.setTaskOperationControl(this.initTaskPermission(project, crowdTestTask, null));
         } else {
             User user = userRepo.getByID(userId);
-            taskDetailsDTO.setTaskOperationControl(this.initTaskPermission(project, task.get(), user));
+            taskDetailsDTO.setTaskOperationControl(this.initTaskPermission(project, crowdTestTask, user));
             // 区域管理员视图
             List<TaskToUserVO> taskToUserVOS = new ArrayList<>();
             if (userId.equals(project.getRegionalManagerId())) {
@@ -989,7 +1051,7 @@ public class WebMediatorImpl implements ViewMediator {
                     taskVO.setEndPointVO(new EndPointVO(Converter.convert(EndPoint.class, endPointPO.get())));
                 }
 
-                taskToUserVOS = task.get().getAcceptedUserList().stream().map(taskToUser -> {
+                taskToUserVOS = crowdTestTask.getAcceptedUserList().stream().map(taskToUser -> {
                     TaskToUserVO taskToUserVO = new TaskToUserVO(taskToUser);
                     taskToUserVO.setUserVO(Converter.convert(UserVO.class, taskToUser.getUser()));
                     // 获取每个人员的报告信息
@@ -1007,7 +1069,7 @@ public class WebMediatorImpl implements ViewMediator {
 //                if(!taskToUserOptional.isPresent())
 //                    throw new HttpBadRequestException("当前用户没有接收此任务!");
 
-                taskToUserVOS = task.get().getAcceptedUserList().stream()
+                taskToUserVOS = crowdTestTask.getAcceptedUserList().stream()
                         .filter(taskToUser -> taskToUser.getUserId().equals(userId)).map(taskToUser -> {
                             TaskToUserVO taskToUserVO = new TaskToUserVO(taskToUser);
                             taskToUserVO.setUserVO(Converter.convert(UserVO.class, taskToUser.getUser()));
@@ -1028,7 +1090,7 @@ public class WebMediatorImpl implements ViewMediator {
             }
             taskDetailsDTO.setAcceptedUserList(taskToUserVOS);
 
-            Optional<EndPointPO> endPointPOOptional = endPointDao.findByTaskCode(task.get().getCode());
+            Optional<EndPointPO> endPointPOOptional = endPointDao.findByTaskCode(crowdTestTask.getCode());
             // 判断是否具有配置项,提供跳转url
             if(endPointPOOptional.isPresent()){
                 // 获取测试类型对应的跳转url
@@ -1110,91 +1172,90 @@ public class WebMediatorImpl implements ViewMediator {
 
     @Override
     public List<BaseAuthVO> renderAuthingList() {
-        List<BaseAuthVO> authingList = new ArrayList<>();
-        authingList.addAll(personalAuthenticationDao.findByIsAuthentication(AuthenticationStatus.isAuthenIng).stream().map(personalAuthenticationPO -> {
+        List<BaseAuthVO> resultList = new ArrayList<>();
+        resultList.addAll(personalAuthenticationDao.findByIsAuthentication(AuthenticationStatus.isAuthenIng).stream().map(personalAuthenticationPO -> {
             PersonalAuthentication personalAuthentication = new PersonalAuthentication();
             BeanUtils.copyProperties(personalAuthenticationPO, personalAuthentication);
             return new BaseAuthVO(personalAuthentication);
         }).collect(Collectors.toList()));
 
-//        authingList.addAll(enterpriseAuthenticationDao.findByIsAuthentication(AuthenticationStatus.isAuthenIng).stream().map(enterpriseAuthenticationPO -> {
+//        resultList.addAll(enterpriseAuthenticationDao.findByIsAuthentication(AuthenticationStatus.isAuthenIng).stream().map(enterpriseAuthenticationPO -> {
 //            EnterpriseAuthentication enterpriseAuthentication = new EnterpriseAuthentication();
 //            BeanUtils.copyProperties(enterpriseAuthenticationPO, enterpriseAuthentication);
 //            return new BaseAuthVO(enterpriseAuthentication);
 //        }).collect(Collectors.toList()));
 
-        authingList.addAll(agencyDao.findByIsAuthentication(AuthenticationStatus.isAuthenIng).stream().map(evaluationAgencyPO -> {
+        resultList.addAll(agencyDao.findByIsAuthentication(AuthenticationStatus.isAuthenIng).stream().map(evaluationAgencyPO -> {
             EvaluationAgency agency = new EvaluationAgency();
             BeanUtils.copyProperties(evaluationAgencyPO, agency);
             return new BaseAuthVO(agency);
         }).collect(Collectors.toList()));
-        if(authingList.size() > 0 && authingList != null) {
-            authingList.sort(Comparator.comparing(BaseAuthVO::getApplytime).reversed());
+        if(resultList.size() > 0 && resultList != null) {
+            resultList.sort(Comparator.comparing(BaseAuthVO::getApplytime).reversed());
         }
-        return authingList;
+        return resultList;
     }
 
     @Override
     public List<BaseAuthVO> renderAuthedList() {
-        List<BaseAuthVO> authingList = new ArrayList<>();
-        authingList.addAll(personalAuthenticationDao.findByIsAuthenticationIsNot(AuthenticationStatus.isAuthenIng).stream().map(personalAuthenticationPO -> {
+        List<BaseAuthVO> resultList = new ArrayList<>();
+        resultList.addAll(personalAuthenticationDao.findByIsAuthenticationIsNot(AuthenticationStatus.isAuthenIng).stream().map(personalAuthenticationPO -> {
             PersonalAuthentication personalAuthentication = new PersonalAuthentication();
             BeanUtils.copyProperties(personalAuthenticationPO, personalAuthentication);
             return new BaseAuthVO(personalAuthentication);
         }).collect(Collectors.toList()));
 
-//        authingList.addAll(enterpriseAuthenticationDao.findByIsAuthenticationIsNot(AuthenticationStatus.isAuthenIng).stream().map(enterpriseAuthenticationPO -> {
+//        resultList.addAll(enterpriseAuthenticationDao.findByIsAuthenticationIsNot(AuthenticationStatus.isAuthenIng).stream().map(enterpriseAuthenticationPO -> {
 //            EnterpriseAuthentication enterpriseAuthentication = new EnterpriseAuthentication();
 //            BeanUtils.copyProperties(enterpriseAuthenticationPO, enterpriseAuthentication);
 //            return new BaseAuthVO(enterpriseAuthentication);
 //        }).collect(Collectors.toList()));
 
-        authingList.addAll(agencyDao.findByIsAuthenticationIsNot(AuthenticationStatus.isAuthenIng).stream().map(evaluationAgencyPO -> {
+        resultList.addAll(agencyDao.findByIsAuthenticationIsNot(AuthenticationStatus.isAuthenIng).stream().map(evaluationAgencyPO -> {
             EvaluationAgency agency = new EvaluationAgency();
             BeanUtils.copyProperties(evaluationAgencyPO, agency);
             return new BaseAuthVO(agency);
         }).collect(Collectors.toList()));
-        if(authingList.size() > 0 && authingList != null){
-            authingList.sort(Comparator.comparing(BaseAuthVO::getApplytime).reversed());
+        if(resultList.size() > 0 && resultList != null){
+            resultList.sort(Comparator.comparing(BaseAuthVO::getApplytime).reversed());
         }
-        return authingList;
+        return resultList;
 
     }
 
     @Override
     public List<CrowdTestProjectVO> findByNameLike(String name) {
         Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
-        List<CrowdTestProjectVO> authingList = new ArrayList<>();
-        authingList.addAll(projectDao.findByNameLikeAndIsDeleted("%" + name + "%", DeletedStatus.isNotDeleted).stream().map(crowdTestProjectPO -> {
-            CrowdTestProject crowdTestProject = new CrowdTestProject();
-            BeanUtils.copyProperties(crowdTestProjectPO, crowdTestProject);
+        List<CrowdTestProjectVO> resultList = new ArrayList<>();
+        resultList.addAll(projectRepo.findByNameLike("%" + name + "%").stream().map(crowdTestProject -> {
             // 项目测试类型转换
             CrowdTestProjectVO crowdTestProjectVO = new CrowdTestProjectVO(crowdTestProject);
-            crowdTestProjectVO.setApplicationType(applicationMap.get(crowdTestProjectPO.getApplicationType()));
+            crowdTestProjectVO.setApplicationType(applicationMap.get(crowdTestProject.getApplicationType()));
             return crowdTestProjectVO;
         }).collect(Collectors.toList()).stream().sorted(Comparator.comparing(CrowdTestProjectVO::getCreateTime).reversed()).collect(Collectors.toList()));
-        return authingList;
+        return resultList;
     }
 
+    /**
+     * 查看所有测试任务相关
+     * @param name
+     * @return
+     */
     @Override
     public List<CrowdTaskVO> findTaskByNameLike(String name) {
-        List<CrowdTaskVO> authingList = new ArrayList<>();
-        authingList.addAll(taskDao.findByNameLike("%" + name + "%").stream().map(crowdTestTaskPO -> {
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+        return taskRepo.findByNameLikeAndIsDeleted("%" + name +"%", DeletedStatus.isNotDeleted).stream().map(crowdTestTask -> {
             //根据code值查询出来该任务的type
-            Optional<TestTypePO> serviceType = testTypeDao.findByCode(crowdTestTaskPO.getType());
-            CrowdTestTask crowdTestTask = new CrowdTestTask();
-            BeanUtils.copyProperties(crowdTestTaskPO, crowdTestTask);
-            crowdTestTask.setType(serviceType.get().getName());
+            crowdTestTask.setType(testMap.get(crowdTestTask.getType()));
             return new CrowdTaskVO(crowdTestTask);
-        }).collect(Collectors.toList()).stream().sorted(Comparator.comparing(CrowdTaskVO::getCreateTime).reversed()).collect(Collectors.toList()));
-        return authingList;
+        }).sorted(Comparator.comparing(CrowdTaskVO::getStatus).thenComparing(CrowdTaskVO::getCreateTime)).collect(Collectors.toList());
     }
 
     @Override
     public List<CrowdTestProjectVO> AllByPage(){
-        List<CrowdTestProjectVO> authingList = new ArrayList<>();
+        List<CrowdTestProjectVO> resultList = new ArrayList<>();
         Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
-        authingList.addAll(projectDao.findAll().stream().map(crowdTestProjectPO ->{
+        resultList.addAll(projectDao.findAll().stream().map(crowdTestProjectPO ->{
             CrowdTestProject crowdTestProject = new CrowdTestProject();
             BeanUtils.copyProperties(crowdTestProjectPO, crowdTestProject);
             themeStatusService.updateStatus(crowdTestProject);
@@ -1203,7 +1264,7 @@ public class WebMediatorImpl implements ViewMediator {
             crowdTestProjectVO.setApplicationType(applicationMap.get(crowdTestProjectPO.getApplicationType()));
             return crowdTestProjectVO;
         }).collect(Collectors.toList()));
-        return authingList;
+        return resultList;
     }
 
     /**
@@ -1211,37 +1272,12 @@ public class WebMediatorImpl implements ViewMediator {
      * @return
      */
     @Override
-    public List<CrowdTestProjectVO> hotCrowdTestProjects() {
-        List<CrowdTestProjectVO> authingList = new ArrayList<>();
-        authingList.addAll(projectDao.findall().stream().map(crowdTestProjectPO -> {
-            CrowdTestProject crowdTestProject = new CrowdTestProject();
-            BeanUtils.copyProperties(crowdTestProjectPO, crowdTestProject);
-            return new CrowdTestProjectVO(crowdTestProject);
-        }).collect(Collectors.toList()));
-        return authingList;
-    }
-
-    @Override
-    //众测广场首页6条项目
-    public List<CrowdTestProjectVO>  indexCrowdTestProjects() {
-        List<CrowdTestProjectVO> authingList = new ArrayList<>();
-        authingList.addAll(projectDao.findindexProject().stream().map(crowdTestProjectPO -> {
-            CrowdTestProject crowdTestProject = new CrowdTestProject();
-            BeanUtils.copyProperties(crowdTestProjectPO, crowdTestProject);
-            // 应用类型值的转换
-            Optional<ApplicationTypePO> applicationTypePO = applicationTypeDao.findByCode(crowdTestProject.getApplicationType());
-            if (applicationTypePO.isPresent()) {
-                crowdTestProject.setApplicationType(applicationTypePO.get().getName());
-            } else {
-                throw new HttpBadRequestException("应用类型有误");
-            }
-            return new CrowdTestProjectVO(crowdTestProject);
-
-        }).collect(Collectors.toList()));
-        return authingList;
+    public List<CrowdTestProjectVO> hotProjectCardLimitCount(int projectCount) {
+        List<CrowdTestProjectVO> resultList = new ArrayList<>();
+        resultList.addAll(projectRepo.findHotProjectCardLimitCount(projectCount).stream().map(CrowdTestProjectVO::new).collect(Collectors.toList()));
+        return resultList;
     }
 
-
     @Override
     public List<CrowdTestProjectVO> crowdTestProjectsWaitingAccept() {
         return projectDao.findAll().stream().filter(crowdTestProjectPO -> crowdTestProjectPO.getStatus() == CrowdTestProjectStatus.HAS_RELEASED
@@ -1284,42 +1320,44 @@ public class WebMediatorImpl implements ViewMediator {
     }
 
     /**
-     * 众测广场首页展示的众测任务
+     * 众测广场首页展示的6个众测任务
      * @return
+     * @param indexCount
      */
     @Override
-    public List<CrowdTaskVO> crowdTaskVos(){
-        List<CrowdTaskVO> authingList = new ArrayList<>();
-        Map<String, String> typeMap = commonRepo.getTypeCodeNameMap();
-        authingList.addAll(taskDao.findindexTask().stream().filter(crowdTestTaskPO -> crowdTestTaskPO.getDeadTime().getTime() > System.currentTimeMillis()).map(crowdTestTaskPO -> {
+    public List<CrowdTaskVO> findIndexTaskLimitCount(int indexCount){
+        List<CrowdTaskVO> resultList = new ArrayList<>();
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+
+        resultList.addAll(taskDao.findIndexTaskLimitCount(indexCount).stream().filter(crowdTestTaskPO -> crowdTestTaskPO.getDeadTime().getTime() > System.currentTimeMillis()).map(crowdTestTaskPO -> {
             CrowdTestTask crowdTestTask = new CrowdTestTask();
             BeanUtils.copyProperties(crowdTestTaskPO, crowdTestTask);
-            crowdTestTask.setType(typeMap.get(crowdTestTaskPO.getType()));
+            crowdTestTask.setType(testMap.get(crowdTestTaskPO.getType()));
             return new CrowdTaskVO(crowdTestTask);
         }).collect(Collectors.toList()));
-        return authingList;
+        return resultList;
     }
 
     @Override
     public List<CrowdTaskVO> crowdTaskVOSByTestTypeCode(String testTypeCode) {
-        Map<String, String> typeMap = commonRepo.getTypeCodeNameMap();
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
         return taskDao.findByType(testTypeCode).stream().map(crowdTestTaskPO -> {
             CrowdTestTask crowdTestTask = new CrowdTestTask();
             BeanUtils.copyProperties(crowdTestTaskPO, crowdTestTask);
-            crowdTestTask.setType(typeMap.get(crowdTestTaskPO.getType()));
+            crowdTestTask.setType(testMap.get(crowdTestTaskPO.getType()));
             return new CrowdTaskVO(crowdTestTask);
         }).collect(Collectors.toList());
     }
 
+    // ???
     @Override
     public List<CrowdTaskVO> crowdTaskVosWaitingAccept() {
-        List<CrowdTestTaskPO> canAcceptTask = taskDao.findCanAcceptTask();
-        List<CrowdTaskVO> taskVOList = canAcceptTask.stream().filter(crowdTestTaskPO -> crowdTestTaskPO.getDeadTime().getTime() > System.currentTimeMillis()).map(crowdTestTaskPO -> {
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+        List<CrowdTaskVO> taskVOList = taskDao.findCanAcceptTask().stream().filter(crowdTestTaskPO -> crowdTestTaskPO.getDeadTime().getTime() > System.currentTimeMillis()).map(crowdTestTaskPO -> {
             //根据code值查询出来该任务的type
-            Optional<TestTypePO> serviceType = testTypeDao.findByCode(crowdTestTaskPO.getType());
             CrowdTestTask crowdTestTask = new CrowdTestTask();
             BeanUtils.copyProperties(crowdTestTaskPO, crowdTestTask);
-            crowdTestTask.setType(serviceType.get().getName());
+            crowdTestTask.setType(testMap.get(crowdTestTaskPO.getType()));
             return new CrowdTaskVO(crowdTestTask);
         }).collect(Collectors.toList());
         return taskVOList;

+ 4 - 2
site/src/main/java/com/mooctest/crowd/site/service/CrowdProjectService.java

@@ -19,14 +19,16 @@ import java.util.List;
  */
 public interface CrowdProjectService {
 
-    List<CrowdTestProjectVO>    findIndexProject();
+    List<CrowdTestProjectVO>  findIndexProjectLimitCount(int indexCount);
 
-    List<CrowdTestProjectVO>  findAllMoreHotProjects();
+    List<CrowdTestProjectVO>  findHotProjectCardLimitCount(int projectCount);
 
     List<CrowdProjectVO>  findAllMoreHotProjectList(String keyword);
 
     List<CrowdTestProjectVO> findByNameLike(String name);
 
+    List<CrowdTestProjectVO> findSquareProjectByNameLike(String name);
+
     List<CrowdTestProjectVO> findAll(Pageable pageable);
 
     List<CrowdTestProjectVO> findAll();

+ 6 - 4
site/src/main/java/com/mooctest/crowd/site/service/CrowdTaskService.java

@@ -19,11 +19,13 @@ public interface CrowdTaskService {
 
     List<CrowdTaskVO> findMoreHotTasks();
 
-     List<CrowdTaskVO> findAll();
+    List<CrowdTaskVO> findIndexTaskLimitCount(int indexCount);
 
     List<CrowdTaskVO> findAllByTestTypeCode(String testTypeCode);
 
-     List<CrowdTaskVO> findByNameLike(String name);
+    List<CrowdTaskVO> findSquareTaskByNameLike(String name);
+
+    List<CrowdTaskVO> findByNameLike(String name);
 
     TaskDetailsDTO getTaskDetails(String projectCode, String taskCode, Long userId);
 
@@ -41,7 +43,7 @@ public interface CrowdTaskService {
 
     TaskDetailsDTO confirmFinish(String projectCode, String taskCode, Long userId);
 
-    List<CrowdTaskVO> findMoreHotTasksList(String keyword);
+    List<CrowdTaskVO> findMoreHotTasksList(String keyword, int taskCount);
 
     void  jumpPublicTesting(String projectCode, String taskCode, Long userId);
 
@@ -51,5 +53,5 @@ public interface CrowdTaskService {
 
     String exportTask(String projectCode, String taskCode, Long userId);
 
-    ProjectDetailsDTO importTask(MultipartFile file, Long userId);
+    ProjectDetailsDTO importTask(MultipartFile file, Long userId, int uploadType);
 }

+ 2 - 1
site/src/main/java/com/mooctest/crowd/site/service/CrowdTestSquareService.java

@@ -5,6 +5,7 @@ import com.mooctest.crowd.site.data.vo.SearchConditionVO;
 
 public interface CrowdTestSquareService {
 
-    ResponseVO  findByNameLike(SearchConditionVO searchConditionVO);
+    ResponseVO findByNameLike(SearchConditionVO searchConditionVO);
 
+    ResponseVO findAllByNameLike(SearchConditionVO searchConditionVO);
 }

+ 12 - 0
site/src/main/java/com/mooctest/crowd/site/service/FileService.java

@@ -1,8 +1,20 @@
 package com.mooctest.crowd.site.service;
 
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+
 public interface FileService {
 
     String uploadFile(String fileName, MultipartFile file);
+
+    String sliceUploadFile(String fileName, MultipartFile file);
+
+    FileUploadDTO upload(FileUploadRequestDTO fileUploadRequestDTO);
+
+    FileUploadDTO sliceUpload(FileUploadRequestDTO fileUploadRequestDTO);
+
+    FileUploadDTO checkFileMd5(FileUploadRequestDTO fileUploadRequestDTO) throws IOException;
 }

+ 242 - 0
site/src/main/java/com/mooctest/crowd/site/service/MultipartUploadSample.java

@@ -0,0 +1,242 @@
+package com.mooctest.crowd.site.service;
+
+import com.aliyun.oss.*;
+import com.aliyun.oss.model.*;
+import com.mooctest.crowd.site.configuration.OSSConfiguration;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This sample demonstrates how to upload multiparts to Aliyun OSS
+ * using the OSS SDK for Java.
+ */
+@Slf4j
+public class MultipartUploadSample {
+
+	@Autowired
+	private static OSSConfiguration ossConfig;
+
+	private static OSS client = null;
+
+//	@Value("${oss.bucketName}")
+	private static String bucketName = "mooctest-crowd-service";
+
+	// 指定分块上传到 OOS 上的路径,即 对象键。例如对象键为 folder/picture.jpg
+	private static String key = "picture.txt";
+
+
+//	@Value("${file.save.path}")
+	private static String localFilePath = "/Users/guochao/Desktop/project/data/cofortest/";
+
+	private static ExecutorService executorService = Executors.newFixedThreadPool(5);
+	private static List<PartETag> partETags = Collections.synchronizedList(new ArrayList<PartETag>());
+
+	public static void main(String[] args) throws IOException {
+		/*
+		 * Constructs a client instance with your account for accessing OSS
+		 */
+		ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
+		conf.setIdleConnectionTime(1000);
+//		client = ossConfig.ossClientConf(conf);
+		client = new OSSClientBuilder().build("oss-cn-hangzhou.aliyuncs.com", "LTAI4FdrT3HsfdR5edBVN7ws", "yroxrpm46DzTyzHrLBZzS3MRNIicP6", conf);
+		try {
+			/*
+			 * Claim a upload id firstly
+			 */
+			String uploadId = claimUploadId();
+			log.info("Claiming a new upload id " + uploadId + "\n");
+
+			/*
+			 * Calculate how many parts to be divided
+			 */
+			final long partSize = 1 * 1024 * 1024L;   // 5MB
+			final File sampleFile = new File(localFilePath + "RequirementDoc/test.txt");
+			long fileLength = sampleFile.length();
+			int partCount = (int) (fileLength / partSize);
+			if (fileLength % partSize != 0) {
+				partCount++;
+			}
+			if (partCount > 10000) {
+				throw new RuntimeException("Total parts count should not exceed 10000");
+			} else {
+				log.info("Total parts count " + partCount + "\n");
+			}
+
+			/*
+			 * Upload multiparts to your bucket
+			 */
+			log.info("Begin to upload multiparts to OSS from a file\n");
+			for (int i = 0; i < partCount; i++) {
+				long startPos = i * partSize;
+				long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
+				executorService.execute(new PartUploader(sampleFile, startPos, curPartSize, i + 1, uploadId));
+			}
+
+			/*
+			 * Waiting for all parts finished
+			 */
+			executorService.shutdown();
+			while (!executorService.isTerminated()) {
+				try {
+					executorService.awaitTermination(5, TimeUnit.SECONDS);
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+				}
+			}
+
+			/*
+			 * Verify whether all parts are finished
+			 */
+			if (partETags.size() != partCount) {
+				throw new IllegalStateException("Upload multiparts fail due to some parts are not finished yet");
+			} else {
+				log.info("Succeed to complete multiparts into an object named " + key + "\n");
+			}
+
+			/*
+			 * View all parts uploaded recently
+			 */
+			listAllParts(uploadId);
+
+			/*
+			 * Complete to upload multiparts
+			 */
+			completeMultipartUpload(uploadId);
+
+			/*
+			 * Fetch the object that newly created at the step below.
+			 */
+			log.info("Fetching an object");
+			client.getObject(new GetObjectRequest(bucketName, key), sampleFile);
+			System.out.println("http://"+bucketName+".oss-cn-hangzhou.aliyuncs.com/" + key);
+//			return ossConfig.getBaseUrl()+fileName;
+		} catch (OSSException oe) {
+			log.info("Caught an OSSException, which means your request made it to OSS, "
+					+ "but was rejected with an error response for some reason.");
+			log.info("Error Message: " + oe.getErrorMessage());
+			log.info("Error Code:       " + oe.getErrorCode());
+			log.info("Request ID:      " + oe.getRequestId());
+			log.info("Host ID:           " + oe.getHostId());
+		} catch (ClientException ce) {
+			log.info("Caught an ClientException, which means the client encountered "
+					+ "a serious internal problem while trying to communicate with OSS, "
+					+ "such as not being able to access the network.");
+			log.info("Error Message: " + ce.getMessage());
+		} finally {
+			/*
+			 * Do not forget to shut down the client finally to release all allocated resources.
+			 */
+			if (client != null) {
+				client.shutdown();
+			}
+		}
+	}
+
+	private static class PartUploader implements Runnable {
+
+		private File localFile;
+		private long startPos;
+
+		private long partSize;
+		private int partNumber;
+		private String uploadId;
+
+		public PartUploader(File localFile, long startPos, long partSize, int partNumber, String uploadId) {
+			this.localFile = localFile;
+			this.startPos = startPos;
+			this.partSize = partSize;
+			this.partNumber = partNumber;
+			this.uploadId = uploadId;
+		}
+
+		@Override
+		public void run() {
+			InputStream instream = null;
+			try {
+				instream = new FileInputStream(this.localFile);
+				instream.skip(this.startPos);
+
+				UploadPartRequest uploadPartRequest = new UploadPartRequest();
+				uploadPartRequest.setBucketName(bucketName);
+				uploadPartRequest.setKey(key);
+				uploadPartRequest.setUploadId(this.uploadId);
+				uploadPartRequest.setInputStream(instream);
+				uploadPartRequest.setPartSize(this.partSize);
+				uploadPartRequest.setPartNumber(this.partNumber);
+
+				UploadPartResult uploadPartResult = client.uploadPart(uploadPartRequest);
+				log.info("Part#" + this.partNumber + " done\n");
+				synchronized (partETags) {
+					partETags.add(uploadPartResult.getPartETag());
+				}
+			} catch (Exception e) {
+				e.printStackTrace();
+			} finally {
+				if (instream != null) {
+					try {
+						instream.close();
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
+
+	private static File createSampleFile() throws IOException {
+		File file = File.createTempFile("oss-java-sdk-", ".txt");
+		file.deleteOnExit();
+
+		Writer writer = new OutputStreamWriter(new FileOutputStream(file));
+		for (int i = 0; i < 100; i++) {
+			writer.write("abcdefghijklmnopqrstuvwxyz\n");
+			writer.write("0123456789011234567890\n");
+		}
+		writer.close();
+
+		return file;
+	}
+
+	private static String claimUploadId() {
+		InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);
+		InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
+		return result.getUploadId();
+	}
+
+	private static void completeMultipartUpload(String uploadId) {
+		// Make part numbers in ascending order
+		Collections.sort(partETags, new Comparator<PartETag>() {
+
+			@Override
+			public int compare(PartETag p1, PartETag p2) {
+				return p1.getPartNumber() - p2.getPartNumber();
+			}
+		});
+
+		log.info("Completing to upload multiparts\n");
+		CompleteMultipartUploadRequest completeMultipartUploadRequest =
+				new CompleteMultipartUploadRequest(bucketName, key, uploadId, partETags);
+		client.completeMultipartUpload(completeMultipartUploadRequest);
+	}
+
+	private static void listAllParts(String uploadId) {
+		log.info("Listing all parts......");
+		ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, key, uploadId);
+		PartListing partListing = client.listParts(listPartsRequest);
+
+		int partCount = partListing.getParts().size();
+		for (int i = 0; i < partCount; i++) {
+			PartSummary partSummary = partListing.getParts().get(i);
+			log.info("\tPart#" + partSummary.getPartNumber() + ", ETag=" + partSummary.getETag());
+		}
+	}
+}

+ 7 - 7
site/src/main/java/com/mooctest/crowd/site/service/UploadService.java

@@ -8,17 +8,17 @@ import org.springframework.web.multipart.MultipartFile;
  * @date 2019-08-05 15:41
  */
 public interface UploadService {
-    String uploadImage(MultipartFile file, Long userId);
+    String uploadImage(MultipartFile file, Long userId, int uploadType);
 
-    String uploadTask(MultipartFile file, Long userId);
+    String uploadTask(MultipartFile file, Long userId, int uploadType);
 
-    String uploadReport(MultipartFile file, Long userId);
+    String uploadReport(MultipartFile file, Long userId, int uploadType);
 
-    String uploadRequirment(MultipartFile file, Long userId);
+    String uploadRequirement(MultipartFile file, Long userId, int uploadType);
 
-    String uploadAPK(MultipartFile file, Long userId);
+    String uploadAPK(MultipartFile file, Long userId, int uploadType);
 
-    String uploadAuthInfo(MultipartFile file, Long userId);
+    String uploadAuthInfo(MultipartFile file, Long userId, int uploadType);
 
-    String uploadGeneralFile(MultipartFile file);
+    String uploadGeneralFile(MultipartFile file, int uploadType);
 }

+ 25 - 21
site/src/main/java/com/mooctest/crowd/site/service/impl/CrowdProjectServiceImpl.java

@@ -30,7 +30,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
@@ -92,46 +91,43 @@ public class CrowdProjectServiceImpl implements CrowdProjectService {
     private ThemeSchedulerService themeSchedulerService;
 
     /**
-     * 众测广场首页展示的项目
-     *
+     * 众测广场首页展示的默认为6个的项目
      * @return
+     * @param indexCount
      */
-
     @Override
-    public List<CrowdTestProjectVO> findIndexProject() {
+    public List<CrowdTestProjectVO> findIndexProjectLimitCount(int indexCount) {
         Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
-        List<CrowdTestProjectVO> authingList = new ArrayList<>();
-        authingList.addAll(projectDao.findindexProject().stream().map(crowdTestProjectPO -> {
-            CrowdTestProject crowdTestProject = new CrowdTestProject();
-            BeanUtils.copyProperties(crowdTestProjectPO, crowdTestProject);
+        List<CrowdTestProjectVO> resultList = new ArrayList<>();
+        resultList.addAll(projectRepo.findSquareIndexProject(indexCount).stream().map(crowdTestProject -> {
             // 项目测试类型转换
             CrowdTestProjectVO crowdTestProjectVO = new CrowdTestProjectVO(crowdTestProject);
-            crowdTestProjectVO.setApplicationType(applicationMap.get(crowdTestProjectPO.getApplicationType()));
+            crowdTestProjectVO.setApplicationType(applicationMap.get(crowdTestProject.getApplicationType()));
             return crowdTestProjectVO;
         }).collect(Collectors.toList()));
-        return authingList;
+        return resultList;
     }
 
-
     /**
      * 更多热门项目
-     *
      * @return
      */
-
     @Override
-    public List<CrowdTestProjectVO> findAllMoreHotProjects() {
-        return viewMediator.hotCrowdTestProjects();
+    public List<CrowdTestProjectVO> findHotProjectCardLimitCount(int projectCount) {
+        return viewMediator.hotProjectCardLimitCount(projectCount);
     }
 
     @Override
     public List<CrowdProjectVO> findAllMoreHotProjectList(String keyword) {
+        // 获取领域、应用、测试类型code 和 name 的map
+        Map<String, String> fieldMap = commonRepo.getFieldCodeNameMap();
         Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
-        List<CrowdProjectVO> crowdProjectVOS = projectDao.findAllHotProject().stream().map(crowdTestProjectPO -> {
-            CrowdTestProject crowdTestProject = new CrowdTestProject();
-            BeanUtils.copyProperties(crowdTestProjectPO, crowdTestProject);
-            // 应用类型值的转换
-            crowdTestProject.setApplicationType(applicationMap.get(crowdTestProject.getApplicationType()));
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+
+        List<CrowdProjectVO> crowdProjectVOS = projectRepo.findAllHotProject().stream().map(crowdTestProject -> {
+            // 领域、应用、测试类型值的转换
+//            crowdTestProject.setApplicationType(applicationMap.get(crowdTestProject.getApplicationType()));
+            viewMediator.changeFieldAndApplicationAndTestType(crowdTestProject, applicationMap, fieldMap, testMap);
             // 判断项目是否截止
             if (crowdTestProject.getDeadTime().getTime() <= System.currentTimeMillis() && crowdTestProject.getStatus() < CrowdTestProjectStatus.HAS_FINISHED) {
                 crowdTestProject.setStatus(CrowdTestProjectStatus.HAS_TIME_OUT);
@@ -146,6 +142,14 @@ public class CrowdProjectServiceImpl implements CrowdProjectService {
     }
 
     @Override
+    public List<CrowdTestProjectVO> findSquareProjectByNameLike(String name) {
+        if (name == null || name.trim().equals("")) {
+            return viewMediator.AllByPage();
+        }
+        return viewMediator.findByNameLike(name);
+    }
+
+    @Override
     public List<CrowdTestProjectVO> findByNameLike(String name) {
         if (name == null || name.trim().equals("")) {
             return viewMediator.AllByPage();

+ 48 - 15
site/src/main/java/com/mooctest/crowd/site/service/impl/CrowdTaskServiceImpl.java

@@ -30,7 +30,6 @@ import net.lingala.zip4j.exception.ZipException;
 import net.lingala.zip4j.model.ZipParameters;
 import net.lingala.zip4j.util.Zip4jConstants;
 import org.jetbrains.annotations.NotNull;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.mock.web.MockMultipartFile;
@@ -38,9 +37,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.*;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -106,22 +103,48 @@ public class CrowdTaskServiceImpl implements CrowdTaskService {
     }
 
     @Override
-    public List<CrowdTaskVO> findMoreHotTasksList(String keyword) {
-        Map<String, String> typeMap = commonRepo.getTypeCodeNameMap();
-        List<CrowdTaskVO> crowdTaskVOS = taskDao.findMoreHotTasksList().stream().map(crowdTestTaskPO -> {
-            CrowdTestTask task = new CrowdTestTask();
-            BeanUtils.copyProperties(crowdTestTaskPO, task);
-            task.setType(typeMap.get(task.getType()));
+    public List<CrowdTaskVO> findMoreHotTasksList(String keyword, int taskCount) {
+        // 获取领域、应用、测试类型code 和 name 的map
+        Map<String, String> fieldMap = commonRepo.getFieldCodeNameMap();
+        Map<String, String> applicationMap = commonRepo.getApplicationCodeNameMap();
+        Map<String, String> testMap = commonRepo.getTypeCodeNameMap();
+
+        // 热门任务指的是未被接收/进行中的项目
+        List<CrowdTestProject> allHotProject = projectRepo.findAllHotProject();
+
+        // 获取热门任务下的所有测试任务
+        List<CrowdTestTask> taskListInProject = new ArrayList<>();
+        allHotProject.stream().forEach(crowdTestProject -> {
+            List<CrowdTestTask> crowdTestTasks = projectRepo.getTaskBaseInfoByProjectCode(crowdTestProject.getCode());
+            crowdTestTasks.stream().forEach(crowdTestTask -> {
+                viewMediator.changeTypeByProjectAndTask(crowdTestProject, crowdTestTask, applicationMap, fieldMap, testMap);
+                taskListInProject.add(crowdTestTask);
+            });
+        });
+
+        // 过滤掉已结束、已截止、已删除的任务
+        List<CrowdTaskVO> crowdTaskVOS = taskListInProject.stream().filter(crowdTestTask -> crowdTestTask.getStatus()!=CrowdTestTaskStatus.HAS_FINISHED
+                && crowdTestTask.getStatus()!=CrowdTestTaskStatus.HAS_TIME_OUT && crowdTestTask.getIsDeleted()==DeletedStatus.isNotDeleted).map(task -> {
             // 判断任务是否截止
             if (task.getDeadTime().getTime() <= System.currentTimeMillis() && task.getStatus() < CrowdTestTaskStatus.HAS_FINISHED) {
                 task.setStatus(CrowdTestTaskStatus.HAS_TIME_OUT);
             }
             return new CrowdTaskVO(task);
         }).collect(Collectors.toList());
+
         if (keyword != null && keyword != "") {
             crowdTaskVOS = crowdTaskVOS.stream().filter(crowdTaskVO -> crowdTaskVO.getTitle().contains(keyword)).collect(Collectors.toList());
         }
-        return crowdTaskVOS;
+        // 对结果进行排序 接收任务、任务状态、任务创建时间
+        List<CrowdTaskVO> taskVOS;
+        taskVOS = crowdTaskVOS.stream().filter(crowdTaskVO -> crowdTaskVO.getStatus() != CrowdTestTaskStatus.HAS_TIME_OUT)
+                .sorted(Comparator.comparing(CrowdTaskVO::getAcceptedCount).reversed()
+                        .thenComparing(CrowdTaskVO::getStatus).thenComparing(CrowdTaskVO::getCreateTime)).collect(Collectors.toList());
+
+        if(taskCount != 0){
+            taskVOS = taskVOS.stream().limit(taskCount).collect(Collectors.toList());
+        }
+        return taskVOS;
     }
 
     @Override
@@ -130,8 +153,8 @@ public class CrowdTaskServiceImpl implements CrowdTaskService {
     }
 
     @Override
-    public List<CrowdTaskVO> findAll() {
-        return viewMediator.crowdTaskVos();
+    public List<CrowdTaskVO> findIndexTaskLimitCount(int indexCount) {
+        return viewMediator.findIndexTaskLimitCount(indexCount);
     }
 
     @Override
@@ -140,6 +163,16 @@ public class CrowdTaskServiceImpl implements CrowdTaskService {
     }
 
     @Override
+    public List<CrowdTaskVO> findSquareTaskByNameLike(String name) {
+        if (name == null || name.trim().equals("")) {
+            return viewMediator.crowdTaskVosWaitingAccept();
+        } else if (name != null) {
+            return viewMediator.findTaskByNameLike(name);
+        }
+        return null;
+    }
+
+    @Override
     public List<CrowdTaskVO> findByNameLike(String name) {
         if (name == null || name.trim().equals("")) {
             return viewMediator.crowdTaskVosWaitingAccept();
@@ -336,7 +369,7 @@ public class CrowdTaskServiceImpl implements CrowdTaskService {
             MultipartFile multipartFile = new MockMultipartFile(downloadFile.getName(), downloadFile.getName(), "", inputStream);
             // 文件上传至oss
             if(usingOss){
-                downloadUrl = uploadService.uploadTask(multipartFile, userId);
+                downloadUrl = uploadService.uploadTask(multipartFile, userId, 0);
             }
         } catch (IOException e) {
             log.info("----------copy error{}---------", e);
@@ -373,7 +406,7 @@ public class CrowdTaskServiceImpl implements CrowdTaskService {
     }
 
     @Override
-    public ProjectDetailsDTO importTask(MultipartFile file, Long userId) {
+    public ProjectDetailsDTO importTask(MultipartFile file, Long userId, int uploadType) {
         log.info("导入的文件为:" + file.getOriginalFilename());
         // 将文件内容读出变成字符串
         String fileString = fileToString(file);

+ 21 - 1
site/src/main/java/com/mooctest/crowd/site/service/impl/CrowdTestSquareServiceImpl.java

@@ -39,7 +39,6 @@ public class CrowdTestSquareServiceImpl implements CrowdTestSquareService {
 
     /**
      * 任务和项目的模糊查询
-     *
      * @param searchConditionVO
      * @return
      */
@@ -48,6 +47,27 @@ public class CrowdTestSquareServiceImpl implements CrowdTestSquareService {
         Pageable pageable = this.getPageable(searchConditionVO);
         String keyword = searchConditionVO.getKeyword();
         if (searchConditionVO.getColumnFilters().get(0).getType().equals("project")) {
+            List<CrowdTestProjectVO> list = crowdProjectService.findSquareProjectByNameLike(keyword);
+            Page<CrowdTestProjectVO> projectVOPage = DataUtils.listToPage(list, pageable);
+            return new ResponseVO<>(ServerCode.SUCCESS, projectVOPage);
+        } else if (searchConditionVO.getColumnFilters().get(0).getType().equals("task")) {
+            List<CrowdTaskVO> taskVOList = crowdTaskService.findByNameLike(keyword);
+            Page<CrowdTaskVO> crowdTaskVOPage = DataUtils.listToPage(taskVOList, pageable);
+            return new ResponseVO(ServerCode.SUCCESS, crowdTaskVOPage);
+        }
+        return null;
+    }
+
+    /**
+     * 所有任务和项目的模糊查询
+     * @param searchConditionVO
+     * @return
+     */
+    @Override
+    public ResponseVO findAllByNameLike(SearchConditionVO searchConditionVO) {
+        Pageable pageable = this.getPageable(searchConditionVO);
+        String keyword = searchConditionVO.getKeyword();
+        if (searchConditionVO.getColumnFilters().get(0).getType().equals("project")) {
             List<CrowdTestProjectVO> list = crowdProjectService.findByNameLike(keyword);
             Page<CrowdTestProjectVO> projectVOPage = DataUtils.listToPage(list, pageable);
             return new ResponseVO<>(ServerCode.SUCCESS, projectVOPage);

+ 147 - 1
site/src/main/java/com/mooctest/crowd/site/service/impl/FileServiceImpl.java

@@ -1,24 +1,52 @@
 package com.mooctest.crowd.site.service.impl;
 
+import com.mooctest.crowd.domain.exception.FileIsEmptyException;
+import com.mooctest.crowd.domain.exception.FileUploadFailureException;
+import com.mooctest.crowd.site.constants.FileConstant;
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+import com.mooctest.crowd.site.data.enums.FileCheckMd5Status;
 import com.mooctest.crowd.site.service.FileService;
-import com.mooctest.crowd.site.util.FileUtil;
+import com.mooctest.crowd.site.strategy.concurrent.FileCallable;
+import com.mooctest.crowd.site.strategy.enu.UploadModeEnum;
+import com.mooctest.crowd.site.util.file.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @author guochao
  * @date 2020-12-25 14:51
  */
+@Slf4j
 @Service
 @ConditionalOnExpression("${feature.client.oss}==false")
 public class FileServiceImpl implements FileService {
     @Value("${file.save.path}")
     private String fileSaveRootPath;
 
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @Autowired
+    private FilePathUtil filePathUtil;
+
+    private AtomicInteger atomicInteger = new AtomicInteger(0);
+
     @Override
     public String uploadFile(String fileName, MultipartFile file) {
 
@@ -37,4 +65,122 @@ public class FileServiceImpl implements FileService {
         }
         return null;
     }
+
+    @Override
+    public String sliceUploadFile(String fileName, MultipartFile file) {
+        return null;
+    }
+
+    private final ExecutorService executorService = Executors.newFixedThreadPool(
+            // ??
+            Integer.valueOf("25"),(r)->{
+                String threadName = "uploadPool-"+atomicInteger.getAndIncrement();
+                Thread thread = new Thread(r);
+                thread.setName(threadName);
+                return thread;
+            });
+
+    private final CompletionService<FileUploadDTO> completionService = new ExecutorCompletionService<>(executorService,
+            new LinkedBlockingDeque<>(Integer.valueOf("100")));
+
+//            Integer.valueOf(YmlUtil.getValue("upload.thread.maxSize").toString()),(r)->{
+//                String threadName = "uploadPool-"+atomicInteger.getAndIncrement();
+//                Thread thread = new Thread(r);
+//                thread.setName(threadName);
+//                return thread;
+//            });
+
+//    private final CompletionService<FileUploadDTO> completionService = new ExecutorCompletionService<>(executorService,
+//            new LinkedBlockingDeque<>(Integer.valueOf(YmlUtil.getValue("upload.queue.maxSize").toString())));
+
+
+    @Override
+    public FileUploadDTO upload(FileUploadRequestDTO param){
+
+        if (Objects.isNull(param.getFile())) {
+            throw new FileIsEmptyException();
+        }
+        param.setPath(FileUtil.withoutHeadAndTailDiagonal(param.getPath()));
+        String md5 = FileMD5Util.getFileMD5(param.getFile());
+        param.setMd5(md5);
+
+        String filePath = filePathUtil.getPath(param);
+        File targetFile = new File(filePath);
+        if (!targetFile.exists()) {
+            targetFile.mkdirs();
+        }
+        String path = filePath + FileConstant.FILE_SEPARATORCHAR + param.getFile().getOriginalFilename();
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(path);
+            out.write(param.getFile().getBytes());
+            out.flush();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        FileUtil.close(out);
+
+        redisUtil.hset(FileConstant.FILE_UPLOAD_STATUS, md5, "true");
+
+        return FileUploadDTO.builder().path(path).mtime(DateUtil.getCurrentTimeStamp()).uploadComplete(true).build();
+    }
+
+    @Override
+    public FileUploadDTO sliceUpload(FileUploadRequestDTO fileUploadRequestDTO){
+        try {
+            completionService.submit(new FileCallable(UploadModeEnum.RANDOM_ACCESS,fileUploadRequestDTO));
+            FileUploadDTO fileUploadDTO = completionService.take().get();
+            return fileUploadDTO;
+        } catch (InterruptedException e) {
+            log.error(e.getMessage(),e);
+            throw new FileUploadFailureException();
+//            throw new BizException(e.getMessage(),406);
+        } catch (ExecutionException e) {
+            log.error(e.getMessage(),e);
+            throw new FileUploadFailureException();
+//            throw new BizException(e.getMessage(),406);
+        }
+    }
+
+    @Override
+    public FileUploadDTO checkFileMd5(FileUploadRequestDTO param) throws IOException{
+        Object uploadProgressObj = redisUtil.hget(FileConstant.FILE_UPLOAD_STATUS, param.getMd5());
+        if (uploadProgressObj == null) {
+            FileUploadDTO fileMd5DTO = FileUploadDTO.builder()
+                    .code(FileCheckMd5Status.FILE_NO_UPLOAD.getValue()).build();
+            return fileMd5DTO;
+        }
+        String processingStr = uploadProgressObj.toString();
+        boolean processing = Boolean.parseBoolean(processingStr);
+        String value = String.valueOf(redisUtil.get(FileConstant.FILE_MD5_KEY + param.getMd5()));
+        return fillFileUploadDTO(param, processing, value);
+    }
+
+    /**
+     * 填充返回文件内容信息
+     */
+    private FileUploadDTO fillFileUploadDTO(FileUploadRequestDTO param, boolean processing,
+                                            String value) throws IOException {
+
+        if (processing) {
+            param.setPath(FileUtil.withoutHeadAndTailDiagonal(param.getPath()));
+            String path = filePathUtil.getPath(param);
+            return FileUploadDTO.builder().code(FileCheckMd5Status.FILE_UPLOADED.getValue())
+                    .path(path).build();
+        } else {
+            File confFile = new File(value);
+            byte[] completeList = FileUtils.readFileToByteArray(confFile);
+            List<Integer> missChunkList = new LinkedList<>();
+            for (int i = 0; i < completeList.length; i++) {
+                if (completeList[i] != Byte.MAX_VALUE) {
+                    missChunkList.add(i);
+                }
+            }
+            return FileUploadDTO.builder().code(FileCheckMd5Status.FILE_UPLOAD_SOME.getValue())
+                    .missChunks(missChunkList).build();
+        }
+    }
+
 }

+ 26 - 16
site/src/main/java/com/mooctest/crowd/site/service/impl/OSSUploadServiceImpl.java

@@ -1,6 +1,8 @@
 package com.mooctest.crowd.site.service.impl;
 
 import com.mooctest.crowd.site.constants.UploadType;
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
 import com.mooctest.crowd.site.service.FileService;
 import com.mooctest.crowd.site.service.UploadService;
 import lombok.extern.slf4j.Slf4j;
@@ -25,67 +27,75 @@ public class OSSUploadServiceImpl implements UploadService {
     private FileService fileService;
 
     @Override
-    public String uploadImage(MultipartFile file, Long userId) {
+    public String uploadImage(MultipartFile file, Long userId, int uploadType) {
         String fileName = UploadType.IMAGE+userId+"_"+UUID.randomUUID()+"_"
                 +System.currentTimeMillis()+""
                 +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."), file.getOriginalFilename().length());
-        return doUpload(fileName, file);
+        return doUpload(fileName, file, uploadType);
     }
 
     @Override
-    public String uploadReport(MultipartFile file, Long userId) {
+    public String uploadReport(MultipartFile file, Long userId, int uploadType) {
         String fileName = UploadType.REPORT+file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf("."))+"_"
                 +userId+"_"
                 +System.currentTimeMillis()+""
                 +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."), file.getOriginalFilename().length());
-        return doUpload(fileName, file);
+        return doUpload(fileName, file, uploadType);
     }
 
     @Override
-    public String uploadRequirment(MultipartFile file, Long userId) {
+    public String uploadRequirement(MultipartFile file, Long userId, int uploadType) {
         String fileName = UploadType.REQUIREMENT_FILE+file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf("."))+"_"
                 +userId+"_"
                 +System.currentTimeMillis()+""
                 +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."), file.getOriginalFilename().length());
-        return doUpload(fileName, file);
+        return doUpload(fileName, file, uploadType);
     }
 
     @Override
-    public String uploadTask(MultipartFile file, Long userId) {
+    public String uploadTask(MultipartFile file, Long userId, int uploadType) {
         String fileName = UploadType.EXPORT_TASK_FILE+file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf("."))+"_"
                 +userId+"_"
                 +System.currentTimeMillis()+""
                 +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."), file.getOriginalFilename().length());
-        return doUpload(fileName, file);
+        return doUpload(fileName, file, uploadType);
     }
 
     @Override
-    public String uploadAPK(MultipartFile file, Long userId) {
+    public String uploadAPK(MultipartFile file, Long userId, int uploadType) {
         String fileName = UploadType.APK+file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf("."))+"_"
                 +userId+"_"
                 +System.currentTimeMillis()+""
                 +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."), file.getOriginalFilename().length());
-        return doUpload(fileName, file);
+        return doUpload(fileName, file, uploadType);
     }
 
     @Override
-    public String uploadAuthInfo(MultipartFile file, Long userId) {
+    public String uploadAuthInfo(MultipartFile file, Long userId, int uploadType) {
         String fileName = UploadType.AUTH_INFO+userId+"/"+file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf("."))+"_"
                 +userId+"_"
                 +System.currentTimeMillis()+""
                 +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."), file.getOriginalFilename().length());
-        return doUpload(fileName, file);
+        return doUpload(fileName, file, uploadType);
     }
 
     @Override
-    public String uploadGeneralFile(MultipartFile file){
+    public String uploadGeneralFile(MultipartFile file, int uploadType){
         String fileName = UploadType.OTHERS+file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf("."))+"_"
                 +System.currentTimeMillis()+""
                 +file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."), file.getOriginalFilename().length());
-        return doUpload(fileName, file);
+        return doUpload(fileName, file, uploadType);
     }
 
-    private String doUpload(String fileName, MultipartFile file){
-        return fileService.uploadFile(fileName, file);
+    private String doUpload(String fileName, MultipartFile file, int uploadType){
+//        if(uploadType == UploadType.SIMPLE_UPLOAD){
+//            return fileService.uploadFile(fileName, file);
+//        }else{
+//            System.out.println("分片上传");
+//            return fileService.sliceUploadFile(fileName, file);
+//        }
+        FileUploadRequestDTO fileUploadRequestDTO = new FileUploadRequestDTO(file);
+        FileUploadDTO upload = fileService.sliceUpload(fileUploadRequestDTO);
+        return "";
     }
 }

+ 241 - 3
site/src/main/java/com/mooctest/crowd/site/service/impl/OssFileServiceImpl.java

@@ -1,18 +1,40 @@
 package com.mooctest.crowd.site.service.impl;
 
+import com.aliyun.oss.ClientBuilderConfiguration;
+import com.aliyun.oss.ClientException;
 import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSException;
+import com.aliyun.oss.model.*;
 import com.mooctest.crowd.domain.exception.BaseException;
+import com.mooctest.crowd.domain.exception.IllegalStateUploadException;
 import com.mooctest.crowd.site.configuration.OSSConfiguration;
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
 import com.mooctest.crowd.site.service.FileService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
 /**
  * @author guochao
  * @date 2020-12-25 14:51
  */
+@Slf4j
 @Service
 @ConditionalOnExpression("${feature.client.oss}==true")
 public class OssFileServiceImpl implements FileService {
@@ -20,17 +42,233 @@ public class OssFileServiceImpl implements FileService {
     @Autowired
     private OSSConfiguration ossConfig;
 
+    private OSS client;
+
+    private String key;
+
+    @Value("${upload.thread.maxSize}")
+    private String uploadThreadMaxSize;
+
+    @Value("${upload.chunkSize}")
+    private String uploadChunkSize;
+
+    @Value("${upload.chunkMaxPartCount}")
+    private String uploadChunkMaxPartCount;
+
+    private ExecutorService executorService;
+
+    private List<PartETag> partETags = Collections.synchronizedList(new ArrayList<PartETag>());
+
     @Override
     public String uploadFile(String fileName, MultipartFile file){
-        OSS ossClient = ossConfig.ossClient();
+        client = ossConfig.ossClient();
         try {
-            ossClient.putObject(ossConfig.getBucketName(), fileName, file.getInputStream());
+            client.putObject(ossConfig.getBucketName(), fileName, file.getInputStream());
             return ossConfig.getBaseUrl()+fileName;
         } catch (Exception e) {
             throw new BaseException(e.getMessage());
         } finally {
-            ossClient.shutdown();
+            client.shutdown();
         }
+    }
+
+    @Override
+    public String sliceUploadFile(String fileName, MultipartFile file){
+        key = fileName;
+        executorService = Executors.newFixedThreadPool(Integer.parseInt(uploadThreadMaxSize));
+
+        ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
+        // 连接空闲超时时间,超时则关闭连接(单位:毫秒)。默认为60000毫秒。
+        conf.setIdleConnectionTime(Long.parseLong(ossConfig.getIdleConnectionTime()));
+        client = ossConfig.ossClientConf(conf);
+
+        try {
+            // 获取文件后缀并新建一个类型为File的targetFile,用于后续MultipartFile转File
+            String prefix=fileName.substring(fileName.lastIndexOf("."));
+            final File targetFile = File.createTempFile(fileName, prefix);
+
+            // 声明一个上传ID,uploadId
+            String uploadId = claimUploadId();
+            log.info("声明一个上传ID:" + uploadId);
+
+            // 分片上传的每片的大小
+            final long partSize = Integer.parseInt(uploadChunkSize) * 1024 * 1024L;   // 5MB
+
+            // MultipartFile转成File
+            file.transferTo(targetFile);
+
+            long fileLength = targetFile.length();
+            // 计算分片的数量
+            int partCount = (int) (fileLength / partSize);
+            if (fileLength % partSize != 0) {
+                partCount++;
+            }
+            // 校验是否超过限制的最大分片数量
+            if (partCount > Integer.parseInt(uploadChunkMaxPartCount)) {
+                throw new RuntimeException("分片数量不能超过" + uploadChunkMaxPartCount);
+            } else {
+                log.info("分片数量为: " + partCount);
+            }
+
+            // 分片上传文件到OSS
+            log.info("------------开始分片上传文件到OSS------------");
+            // 重新初始化,否则会一直add导致数量不一致
+            partETags = Collections.synchronizedList(new ArrayList<PartETag>());
+            for (int i = 0; i < partCount; i++) {
+                long startPos = i * partSize;
+                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
+                executorService.execute(new PartUploader(targetFile, startPos, curPartSize, i + 1, uploadId));
+            }
+
+            // 等待所有分片上传完成
+            executorService.shutdown();
+            while (!executorService.isTerminated()) {
+                try {
+                    executorService.awaitTermination(1, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+
+            log.info("partETags的数量:" + partETags.size());
+            // 判断是否所有的分片上传完成
+            if (partETags.size() != partCount) {
+                throw new IllegalStateUploadException();
+            } else {
+                log.info("----------文件分片上传成功 " + fileName + "----------");
+            }
+
+            // View all parts uploaded recently
+//            listAllParts(uploadId);
+
+            // Complete to upload multiparts
+            completeMultipartUpload(uploadId);
+
+            // Fetch the object that newly created at the step below.
+//            log.info("Fetching an object");
+//            client.getObject(new GetObjectRequest(ossConfig.getBucketName(), key), targetFile);
+            return ossConfig.getBaseUrl() + key;
+        } catch (OSSException oe) {
+            log.info("Caught an OSSException, which means your request made it to OSS, "
+                    + "but was rejected with an error response for some reason.");
+            log.info("Error Message: " + oe.getErrorMessage());
+            log.info("Error Code:       " + oe.getErrorCode());
+            log.info("Request ID:      " + oe.getRequestId());
+            log.info("Host ID:           " + oe.getHostId());
+        } catch (ClientException ce) {
+            log.info("Caught an ClientException, which means the client encountered "
+                    + "a serious internal problem while trying to communicate with OSS, "
+                    + "such as not being able to access the network.");
+            log.info("Error Message: " + ce.getMessage());
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            /*
+             * Do not forget to shut down the client finally to release all allocated resources.
+             */
+            if (client != null) {
+                client.shutdown();
+            }
+        }
+        return "";
+    }
 
+    private class PartUploader implements Runnable {
+
+        private File localFile;
+        private long startPos;
+        private long partSize;
+        private int partNumber;
+        private String uploadId;
+
+        public PartUploader(File localFile, long startPos, long partSize, int partNumber, String uploadId) {
+            this.localFile = localFile;
+            this.startPos = startPos;
+            this.partSize = partSize;
+            this.partNumber = partNumber;
+            this.uploadId = uploadId;
+        }
+
+        @Override
+        public void run() {
+            InputStream instream = null;
+            try {
+                instream = new FileInputStream(this.localFile);
+                instream.skip(this.startPos);
+
+                UploadPartRequest uploadPartRequest = new UploadPartRequest();
+                uploadPartRequest.setBucketName(ossConfig.getBucketName());
+                uploadPartRequest.setKey(key);
+                uploadPartRequest.setUploadId(this.uploadId);
+                uploadPartRequest.setInputStream(instream);
+                uploadPartRequest.setPartSize(this.partSize);
+                uploadPartRequest.setPartNumber(this.partNumber);
+
+                UploadPartResult uploadPartResult = client.uploadPart(uploadPartRequest);
+                log.info("分片 " + this.partNumber + " 完成");
+
+                synchronized (partETags) {
+                    partETags.add(uploadPartResult.getPartETag());
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                if (instream != null) {
+                    try {
+                        instream.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+    private String claimUploadId() {
+        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(ossConfig.getBucketName(), key);
+        InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
+        return result.getUploadId();
+    }
+
+    private void completeMultipartUpload(String uploadId) {
+        // Make part numbers in ascending order
+        Collections.sort(partETags, new Comparator<PartETag>() {
+            @Override
+            public int compare(PartETag p1, PartETag p2) {
+                return p1.getPartNumber() - p2.getPartNumber();
+            }
+        });
+
+        log.info("Completing to upload multiparts");
+        CompleteMultipartUploadRequest completeMultipartUploadRequest =
+                new CompleteMultipartUploadRequest(ossConfig.getBucketName(), key, uploadId, partETags);
+        client.completeMultipartUpload(completeMultipartUploadRequest);
+    }
+
+    private void listAllParts(String uploadId) {
+        log.info("Listing all parts......");
+        ListPartsRequest listPartsRequest = new ListPartsRequest(ossConfig.getBucketName(), key, uploadId);
+        PartListing partListing = client.listParts(listPartsRequest);
+
+        int partCount = partListing.getParts().size();
+        for (int i = 0; i < partCount; i++) {
+            PartSummary partSummary = partListing.getParts().get(i);
+            log.info("\tPart " + partSummary.getPartNumber() + ", ETag=" + partSummary.getETag());
+        }
+    }
+
+    @Override
+    public FileUploadDTO upload(FileUploadRequestDTO fileUploadRequestDTO) {
+        return null;
+    }
+
+    @Override
+    public FileUploadDTO sliceUpload(FileUploadRequestDTO fileUploadRequestDTO) {
+        return null;
+    }
+
+    @Override
+    public FileUploadDTO checkFileMd5(FileUploadRequestDTO fileUploadRequestDTO) throws IOException {
+        return null;
     }
 }

+ 9 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/SliceUploadStrategy.java

@@ -0,0 +1,9 @@
+package com.mooctest.crowd.site.strategy;
+
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+
+public interface SliceUploadStrategy {
+
+  FileUploadDTO sliceUpload(FileUploadRequestDTO param);
+}

+ 17 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/annotation/UploadMode.java

@@ -0,0 +1,17 @@
+package com.mooctest.crowd.site.strategy.annotation;
+
+import com.mooctest.crowd.site.strategy.enu.UploadModeEnum;
+import org.springframework.stereotype.Component;
+
+import java.lang.annotation.*;
+
+@Target(value = ElementType.TYPE)
+@Retention(value = RetentionPolicy.RUNTIME)
+@Documented
+@Component
+@Inherited
+public @interface UploadMode {
+
+  UploadModeEnum mode();
+
+}

+ 27 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/concurrent/FileCallable.java

@@ -0,0 +1,27 @@
+package com.mooctest.crowd.site.strategy.concurrent;
+
+
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+import com.mooctest.crowd.site.strategy.context.UploadContext;
+import com.mooctest.crowd.site.strategy.enu.UploadModeEnum;
+
+import java.util.concurrent.Callable;
+
+public class FileCallable implements Callable<FileUploadDTO> {
+
+  private UploadModeEnum mode;
+  private FileUploadRequestDTO param;
+
+  public FileCallable(UploadModeEnum mode, FileUploadRequestDTO param) {
+    this.mode = mode;
+    this.param = param;
+  }
+
+  @Override
+  public FileUploadDTO call(){
+    FileUploadDTO fileUploadDTO = UploadContext.INSTANCE.getInstance(mode).sliceUpload(param);
+    return fileUploadDTO;
+  }
+
+}

+ 36 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/config/RedisConfig.java

@@ -0,0 +1,36 @@
+package com.mooctest.crowd.site.strategy.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@Configuration
+public class RedisConfig {
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
+        template.setConnectionFactory(factory);
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        // key采用String的序列化方式
+        template.setKeySerializer(stringRedisSerializer);
+        // hash的key也采用String的序列化方式
+        template.setHashKeySerializer(stringRedisSerializer);
+        // value序列化方式采用jackson
+        template.setValueSerializer(jackson2JsonRedisSerializer);
+        // hash的value序列化方式采用jackson
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+        template.afterPropertiesSet();
+        return template;
+    }
+}

+ 47 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/context/UploadContext.java

@@ -0,0 +1,47 @@
+package com.mooctest.crowd.site.strategy.context;
+
+
+import com.mooctest.crowd.site.strategy.SliceUploadStrategy;
+import com.mooctest.crowd.site.strategy.annotation.UploadMode;
+import com.mooctest.crowd.site.strategy.enu.UploadModeEnum;
+import com.mooctest.crowd.site.util.file.SpringContextHolder;
+import org.apache.commons.collections4.CollectionUtils;
+import org.reflections.Reflections;
+import org.springframework.util.Assert;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public enum UploadContext {
+   INSTANCE;
+
+   private static final String PACKAGE_NAME = "com.mooctest.crowd.site.strategy.impl";
+
+   private Map<UploadModeEnum,Class<SliceUploadStrategy>> uploadStrategyMap = new ConcurrentHashMap<>();
+
+
+   public void init(){
+     Reflections reflections = new Reflections(PACKAGE_NAME);
+     Set<Class<?>> clzSet = reflections.getTypesAnnotatedWith(UploadMode.class);
+     if(CollectionUtils.isNotEmpty(clzSet)){
+       for (Class<?> clz : clzSet) {
+         UploadMode uploadMode = clz.getAnnotation(UploadMode.class);
+         uploadStrategyMap.put(uploadMode.mode(), (Class<SliceUploadStrategy>) clz);
+       }
+     }
+   }
+
+   public SliceUploadStrategy getInstance(UploadModeEnum mode){
+     return this.getStrategyByType(mode);
+
+   }
+
+
+   private SliceUploadStrategy getStrategyByType(UploadModeEnum mode){
+     Class<SliceUploadStrategy> clz = uploadStrategyMap.get(mode);
+     Assert.notNull(clz,"mode:"+mode+"can not found class,please checked");
+     return SpringContextHolder.getBean(clz);
+   }
+
+}

+ 10 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/enu/UploadModeEnum.java

@@ -0,0 +1,10 @@
+package com.mooctest.crowd.site.strategy.enu;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public enum UploadModeEnum {
+  RANDOM_ACCESS,MAPPED_BYTEBUFFER
+}

+ 63 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/impl/MappedByteBufferUploadStrategy.java

@@ -0,0 +1,63 @@
+package com.mooctest.crowd.site.strategy.impl;
+
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+import com.mooctest.crowd.site.strategy.annotation.UploadMode;
+import com.mooctest.crowd.site.strategy.enu.UploadModeEnum;
+import com.mooctest.crowd.site.strategy.template.SliceUploadTemplate;
+import com.mooctest.crowd.site.util.file.FilePathUtil;
+import com.mooctest.crowd.site.util.file.FileUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.Objects;
+
+@UploadMode(mode = UploadModeEnum.MAPPED_BYTEBUFFER)
+@Slf4j
+public class MappedByteBufferUploadStrategy extends SliceUploadTemplate {
+
+  @Autowired
+  private FilePathUtil filePathUtil;
+
+  @Value("${upload.chunkSize}")
+  private long defaultChunkSize;
+
+  @Override
+  public boolean upload(FileUploadRequestDTO param) {
+
+    RandomAccessFile tempRaf = null;
+    FileChannel fileChannel = null;
+    MappedByteBuffer mappedByteBuffer = null;
+    try {
+      String uploadDirPath = filePathUtil.getPath(param);
+      File tmpFile = super.createTmpFile(param);
+      tempRaf = new RandomAccessFile(tmpFile, "rw");
+      fileChannel = tempRaf.getChannel();
+
+      long chunkSize = Objects.isNull(param.getChunkSize()) ? defaultChunkSize * 1024 * 1024
+          : param.getChunkSize();
+      //写入该分片数据
+      long offset = chunkSize * param.getChunk();
+      byte[] fileData = param.getFile().getBytes();
+      mappedByteBuffer = fileChannel
+          .map(FileChannel.MapMode.READ_WRITE, offset, fileData.length);
+      mappedByteBuffer.put(fileData);
+      boolean isOk = super.checkAndSetUploadProgress(param, uploadDirPath);
+      return isOk;
+
+    } catch (IOException e) {
+      log.error(e.getMessage(), e);
+    } finally {
+      FileUtil.freedMappedByteBuffer(mappedByteBuffer);
+      FileUtil.close(fileChannel);
+      FileUtil.close(tempRaf);
+    }
+    return false;
+  }
+
+}

+ 52 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/impl/RandomAccessUploadStrategy.java

@@ -0,0 +1,52 @@
+package com.mooctest.crowd.site.strategy.impl;
+
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+import com.mooctest.crowd.site.strategy.annotation.UploadMode;
+import com.mooctest.crowd.site.strategy.enu.UploadModeEnum;
+import com.mooctest.crowd.site.strategy.template.SliceUploadTemplate;
+import com.mooctest.crowd.site.util.file.FilePathUtil;
+import com.mooctest.crowd.site.util.file.FileUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Objects;
+
+@UploadMode(mode = UploadModeEnum.RANDOM_ACCESS)
+@Slf4j
+public class RandomAccessUploadStrategy extends SliceUploadTemplate {
+
+  @Autowired
+  private FilePathUtil filePathUtil;
+
+  @Value("${upload.chunkSize}")
+  private long defaultChunkSize;
+
+  @Override
+  public boolean upload(FileUploadRequestDTO param) {
+    RandomAccessFile accessTmpFile = null;
+    try {
+      String uploadDirPath = filePathUtil.getPath(param);
+      File tmpFile = super.createTmpFile(param);
+      accessTmpFile = new RandomAccessFile(tmpFile, "rw");
+      //这个必须与前端设定的值一致
+      long chunkSize = Objects.isNull(param.getChunkSize()) ? defaultChunkSize * 1024 * 1024
+          : param.getChunkSize();
+      long offset = chunkSize * param.getChunk();
+      //定位到该分片的偏移量
+      accessTmpFile.seek(offset);
+      //写入该分片数据
+      accessTmpFile.write(param.getFile().getBytes());
+      boolean isOk = super.checkAndSetUploadProgress(param, uploadDirPath);
+      return isOk;
+    } catch (IOException e) {
+      log.error(e.getMessage(), e);
+    } finally {
+      FileUtil.close(accessTmpFile);
+    }
+    return false;
+  }
+}

+ 17 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/init/UploadStrategyInitializingBean.java

@@ -0,0 +1,17 @@
+package com.mooctest.crowd.site.strategy.init;
+
+import com.mooctest.crowd.site.strategy.context.UploadContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class UploadStrategyInitializingBean implements InitializingBean {
+
+  @Override
+  public void afterPropertiesSet() throws Exception {
+    log.info("init uploadStrategy ...");
+    UploadContext.INSTANCE.init();
+  }
+}

+ 167 - 0
site/src/main/java/com/mooctest/crowd/site/strategy/template/SliceUploadTemplate.java

@@ -0,0 +1,167 @@
+package com.mooctest.crowd.site.strategy.template;
+
+import com.mooctest.crowd.site.constants.FileConstant;
+import com.mooctest.crowd.site.data.dto.FileUploadDTO;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+import com.mooctest.crowd.site.strategy.SliceUploadStrategy;
+import com.mooctest.crowd.site.util.file.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+public abstract class SliceUploadTemplate implements SliceUploadStrategy {
+
+  public abstract boolean upload(FileUploadRequestDTO param);
+
+  protected File createTmpFile(FileUploadRequestDTO param) {
+
+    FilePathUtil filePathUtil = SpringContextHolder.getBean(FilePathUtil.class);
+    param.setPath(FileUtil.withoutHeadAndTailDiagonal(param.getPath()));
+    String fileName = param.getFile().getOriginalFilename();
+    String uploadDirPath = filePathUtil.getPath(param);
+    String tempFileName = fileName + "_tmp";
+    File tmpDir = new File(uploadDirPath);
+    File tmpFile = new File(uploadDirPath, tempFileName);
+    if (!tmpDir.exists()) {
+      tmpDir.mkdirs();
+    }
+    return tmpFile;
+  }
+
+  @Override
+  public FileUploadDTO sliceUpload(FileUploadRequestDTO param) {
+
+    boolean isOk = this.upload(param);
+    if (isOk) {
+      File tmpFile = this.createTmpFile(param);
+      FileUploadDTO fileUploadDTO = this.saveAndFileUploadDTO(param.getFile().getOriginalFilename(), tmpFile);
+      return fileUploadDTO;
+    }
+    String md5 = FileMD5Util.getFileMD5(param.getFile());
+
+    Map<Integer, String> map = new HashMap<>();
+    map.put(param.getChunk(), md5);
+    return FileUploadDTO.builder().chunkMd5Info(map).build();
+  }
+
+  /**
+   * 检查并修改文件上传进度
+   */
+  public boolean checkAndSetUploadProgress(FileUploadRequestDTO param, String uploadDirPath) {
+
+    String fileName = param.getFile().getOriginalFilename();
+    File confFile = new File(uploadDirPath, fileName + ".conf");
+    byte isComplete = 0;
+    RandomAccessFile accessConfFile = null;
+    try {
+      accessConfFile = new RandomAccessFile(confFile, "rw");
+      //把该分段标记为 true 表示完成
+      log.info("set part " + param.getChunk() + " complete");
+      //创建conf文件文件长度为总分片数,每上传一个分块即向conf文件中写入一个127,那么没上传的位置就是默认0,已上传的就是Byte.MAX_VALUE 127
+      accessConfFile.setLength(param.getChunks());
+      accessConfFile.seek(param.getChunk());
+      accessConfFile.write(Byte.MAX_VALUE);
+
+      //completeList 检查是否全部完成,如果数组里是否全部都是127(全部分片都成功上传)
+      byte[] completeList = FileUtils.readFileToByteArray(confFile);
+      isComplete = Byte.MAX_VALUE;
+      for (int i = 0; i < completeList.length && isComplete == Byte.MAX_VALUE; i++) {
+        //与运算, 如果有部分没有完成则 isComplete 不是 Byte.MAX_VALUE
+        isComplete = (byte) (isComplete & completeList[i]);
+        log.info("check part " + i + " complete?:" + completeList[i]);
+      }
+
+    } catch (IOException e) {
+      log.error(e.getMessage(), e);
+    } finally {
+      FileUtil.close(accessConfFile);
+    }
+    boolean isOk = setUploadProgress2Redis(param, uploadDirPath, fileName, confFile, isComplete);
+    return isOk;
+  }
+
+  /**
+   * 把上传进度信息存进redis
+   */
+  private boolean setUploadProgress2Redis(FileUploadRequestDTO param, String uploadDirPath, String fileName, File confFile, byte isComplete) {
+
+    RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
+    if (isComplete == Byte.MAX_VALUE) {
+      redisUtil.hset(FileConstant.FILE_UPLOAD_STATUS, param.getMd5(), "true");
+      redisUtil.del(FileConstant.FILE_MD5_KEY + param.getMd5());
+      confFile.delete();
+      return true;
+    } else {
+      if (!redisUtil.hHasKey(FileConstant.FILE_UPLOAD_STATUS, param.getMd5())) {
+        redisUtil.hset(FileConstant.FILE_UPLOAD_STATUS, param.getMd5(), "false");
+        redisUtil.set(FileConstant.FILE_MD5_KEY + param.getMd5(),
+            uploadDirPath + FileConstant.FILE_SEPARATORCHAR + fileName + ".conf");
+      }
+
+      return false;
+    }
+  }
+
+  /**
+   * 保存文件操作
+   */
+  public FileUploadDTO saveAndFileUploadDTO(String fileName, File tmpFile) {
+
+    FileUploadDTO fileUploadDTO = null;
+
+    try {
+
+      fileUploadDTO = renameFile(tmpFile, fileName);
+      if (fileUploadDTO.isUploadComplete()) {
+        System.out
+            .println("upload complete !!" + fileUploadDTO.isUploadComplete() + " name=" + fileName);
+        //TODO 保存文件信息到数据库
+
+      }
+
+    } catch (Exception e) {
+      log.error(e.getMessage(), e);
+    } finally {
+
+    }
+    return fileUploadDTO;
+  }
+
+  /**
+   * 文件重命名
+   *
+   * @param toBeRenamed 将要修改名字的文件
+   * @param toFileNewName 新的名字
+   */
+  private FileUploadDTO renameFile(File toBeRenamed, String toFileNewName) {
+    //检查要重命名的文件是否存在,是否是文件
+    FileUploadDTO fileUploadDTO = new FileUploadDTO();
+    if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
+      log.info("File does not exist: {}", toBeRenamed.getName());
+      fileUploadDTO.setUploadComplete(false);
+      return fileUploadDTO;
+    }
+    String ext = FileUtil.getExtension(toFileNewName);
+    String p = toBeRenamed.getParent();
+    String filePath = p + FileConstant.FILE_SEPARATORCHAR + toFileNewName;
+    File newFile = new File(filePath);
+    //修改文件名
+    boolean uploadFlag = toBeRenamed.renameTo(newFile);
+
+    fileUploadDTO.setMtime(DateUtil.getCurrentTimeStamp());
+    fileUploadDTO.setUploadComplete(uploadFlag);
+    fileUploadDTO.setPath(filePath);
+    fileUploadDTO.setSize(newFile.length());
+    fileUploadDTO.setFileExt(ext);
+    fileUploadDTO.setFileId(toFileNewName);
+
+    return fileUploadDTO;
+  }
+
+}

+ 0 - 152
site/src/main/java/com/mooctest/crowd/site/util/FileUtil.java

@@ -1,152 +0,0 @@
-package com.mooctest.crowd.site.util;
-
-import cn.afterturn.easypoi.excel.ExcelImportUtil;
-import cn.afterturn.easypoi.excel.entity.ImportParams;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.*;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-/**
- * @author: Diors.Po
- * @Email: 171256175@qq.com
- * @date 2019-08-05 19:57
- */
-@Slf4j
-public class FileUtil {
-    public static final Map<String, String> FILE_TYPE_MAP = new HashMap<>(); //本系统中合法的文件类型
-
-    static {
-
-        //图片
-        FILE_TYPE_MAP.put("ffd8ff", "jpg"); //JPEG (jpg)
-        FILE_TYPE_MAP.put("89504e", "png"); //PNG (png)
-        FILE_TYPE_MAP.put("474946", "gif"); //GIF (gif)
-
-        FILE_TYPE_MAP.put("49492a", "tif"); //TIFF (tif)
-        FILE_TYPE_MAP.put("492049", "tif");
-        FILE_TYPE_MAP.put("4d4d00", "tif");
-
-        FILE_TYPE_MAP.put("424d22", "bmp"); //16色位图(bmp)
-        FILE_TYPE_MAP.put("424d82", "bmp"); //24位位图(bmp)
-        FILE_TYPE_MAP.put("424d8e", "bmp"); //256色位图(bmp)
-
-        //压缩文件
-        FILE_TYPE_MAP.put("504b03", "zip");
-        FILE_TYPE_MAP.put("504b05", "zip");
-        FILE_TYPE_MAP.put("504b07", "zip");
-        FILE_TYPE_MAP.put("526172", "rar");
-
-        //文档
-        FILE_TYPE_MAP.put("255044", "pdf"); //Adobe Acrobat (pdf)
-        FILE_TYPE_MAP.put("504b03", "docx");//docx文件,xlsx等一致
-        FILE_TYPE_MAP.put("d0cf11", "doc/dot"); //MS Excel 注意:word、msi 和 excel的文件头一样
-        FILE_TYPE_MAP.put("0d444f", "doc/dot");
-        FILE_TYPE_MAP.put("cf11e0", "doc/dot");
-        FILE_TYPE_MAP.put("dba52d", "doc/dot");
-        FILE_TYPE_MAP.put("d0cf11", "wps/wks");//WPS文字wps、表格et、演示dps都是一样的
-        FILE_TYPE_MAP.put("0e574b", "wps/wks");
-        FILE_TYPE_MAP.put("ff0002", "wps/wks");
-
-        //exe可执行文件
-        FILE_TYPE_MAP.put("4d5a90", "exe");
-    }
-
-    public static String save(String dirPath, MultipartFile inputFile, String fileName) throws IOException {
-        byte[] bs = new byte[1024];
-        int len;
-
-        File saveDir = new File(dirPath);
-        if (!saveDir.exists()) {
-            saveDir.mkdirs();
-        }
-        if(fileName.equals("")){
-            fileName = inputFile.getOriginalFilename();
-        }
-        String saveFilePath = saveDir.getPath() + "/"  + fileName;
-        OutputStream os = new FileOutputStream( saveFilePath );
-        InputStream is = inputFile.getInputStream();
-        while ((len = is.read(bs)) != -1) {
-            os.write(bs, 0, len);
-        }
-
-        os.close();
-        is.close();
-
-        return saveFilePath.replaceAll("\\\\", "/" );
-    }
-
-    public static boolean checkFile(InputStream inputStream){
-        try {
-            byte[] headBytes = new byte[3];
-            inputStream.read(headBytes, 0, headBytes.length);
-            String headStr = bytesToHexString(headBytes).toLowerCase();
-            log.info("The Head of File: "+headStr);
-//            log.info("Whether the map contain the " + headStr +": " + FILE_TYPE_MAP.containsKey(headStr));
-            return FILE_TYPE_MAP.containsKey(headStr);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return true;
-    }
-
-    public static boolean checkExcel(InputStream inputStream){
-        String headStr = getHeadStr(inputStream);
-        log.info("The Head of File: "+headStr);
-        String name = FILE_TYPE_MAP.get(headStr);
-        if (name == null || (!name.contains("doc")&&(!name.contains("wps"))))
-            return false;
-        return true;
-    }
-
-    public static String getHeadStr(InputStream inputStream){
-        try {
-            byte[] headBytes = new byte[3];
-            inputStream.read(headBytes, 0, headBytes.length);
-            return bytesToHexString(headBytes).toLowerCase();
-        } catch (IOException e) {
-            throw new RuntimeException("读取文件头出错!", e);
-        }
-    }
-
-    private static String bytesToHexString(byte[] src) {
-        StringBuilder builder = new StringBuilder();
-        if (src == null || src.length <= 0) {
-            return null;
-        }
-        String hv;
-        for (int i = 0; i < src.length; i++) {
-            // 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式,并转换为大写
-            hv = Integer.toHexString(src[i] & 0xFF).toUpperCase();
-            if (hv.length() < 2) {
-                builder.append(0);
-            }
-            builder.append(hv);
-        }
-        System.out.println("HexString: " + builder.toString());
-        return builder.toString();
-    }
-
-    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass){
-        if (file == null){
-            return null;
-        }
-        ImportParams params = new ImportParams();
-        params.setTitleRows(titleRows);
-        params.setHeadRows(headerRows);
-        List<T> list = null;
-        try {
-            list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);
-        }catch (NoSuchElementException e){
-            log.info("excel文件不能为空");
-            throw new RuntimeException("excel文件不能为空");
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-        return list;
-    }
-}

+ 0 - 1
site/src/main/java/com/mooctest/crowd/site/util/GenerateFlowCodeUtil.java

@@ -1,7 +1,6 @@
 package com.mooctest.crowd.site.util;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.ComponentScan;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 

+ 944 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/DateUtil.java

@@ -0,0 +1,944 @@
+package com.mooctest.crowd.site.util.file;
+
+import com.mooctest.crowd.site.data.enums.DateType;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.FastDateFormat;
+
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Slf4j
+public class DateUtil {
+
+  public static final String DATE_PATTERN = "yyyy-MM-dd";
+
+  public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+  public static final String DATE_MONTH_PATTERN = "yyyy-MM";
+
+  public static final String DATE_HOUR_MINUTE_PATTERN = "HH:mm";
+
+  public static final String DATE_HOUR_MINUTE_SECOND_PATTERN = "HH:mm:ss";
+
+  public static final String DATE_TIME_WITHOUT_SECOND_PATTERN = "yyyy-MM-dd HH:mm";
+
+  public static final String DATE_TIME_WITH_HOUR_PATTERN = "yyyy-MM-dd HH:00:00";
+
+  public static final String DATE_TIME_START_PATTERN = "yyyy-MM-dd 00:00:00";
+
+  public static final String DATE_TIME_END_PATTERN = "yyyy-MM-dd 23:59:59";
+
+
+
+
+  /**
+   * 设置一周的第一天是什么; 例如美国的是SUNDAY,中国的是MONDAY。
+   */
+  private static final int firstDayOfWeek = Calendar.MONDAY;
+  /**
+   * 一周有几天
+   */
+  public static final int wholeDayOfWeek = 7;
+
+  public static void main(String[] args){
+
+  }
+
+
+  public static Integer dateToTimestamp(Date time) {
+    Timestamp ts = new Timestamp(time.getTime());
+
+    return (int) ((ts.getTime()) / 1000);
+  }
+
+
+  public static Integer getCurrentTimeStamp(){
+    Timestamp ts = new Timestamp(new Date().getTime());
+
+    return (int) ((ts.getTime()) / 1000);
+  }
+
+  public static Integer getTimeStamp(Date date){
+    Timestamp ts = new Timestamp(date.getTime());
+
+    return (int) ((ts.getTime()) / 1000);
+  }
+
+
+  public static String getDateStr(Date date){
+    return FastDateFormat.getInstance(DATE_PATTERN).format(date);
+  }
+
+  public static String getStartDate(String startDate){
+    Date start = strToDate(startDate,DATE_PATTERN);
+    return dateToStr(start,DATE_TIME_START_PATTERN);
+  }
+
+  public static String getEndDate(String endDate){
+    Date end = strToDate(endDate,DATE_PATTERN);
+    return dateToStr(end,DATE_TIME_END_PATTERN);
+  }
+
+  public static String getHourMinuteStr(Date date){
+    return  DateFormat.getTimeInstance().format(date);
+  }
+
+  public static String getDateTimeStr(Date date){
+    return FastDateFormat.getInstance(DATE_TIME_PATTERN).format(date);
+  }
+
+  public static String getHourMinuteStr(String time){
+     Date date = strToDate(time,DATE_HOUR_MINUTE_PATTERN);
+     return dateToStr(date,DATE_HOUR_MINUTE_PATTERN);
+  }
+
+  /**
+   * 日期转字符串
+   *
+   * @param date
+   * @return
+   */
+  public static String dateToStr(Date date) {
+    return dateToStr(date, null);
+  }
+
+  /**
+   * 日期转字符串
+   *
+   * @param date
+   * @param pattern
+   * @return
+   */
+  public static String dateToStr(Date date, String pattern) {
+    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+    if (pattern != null) {
+      sdf.applyPattern(pattern);
+    }
+    return sdf.format(date);
+  }
+
+  /**
+   * 字符串转日期
+   *
+   * @param dateStr
+   * @param pattern
+   * @return
+   */
+  public static Date strToDate(String dateStr, String pattern) {
+    Date date = null;
+    SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+    try {
+      date = sdf.parse(dateStr);
+    } catch (ParseException e) {
+      log.error(e.getMessage(),e);
+    }
+    return date;
+  }
+
+  public static String dateToStrByType(Date date, DateType dateType) {
+    String dateStr;
+    switch (dateType) {
+      case DAY:
+        dateStr = dateToStr(date, "yyyy-MM-dd");
+        break;
+      case MONTH:
+        dateStr = dateToStr(date, "yyyy-MM");
+        break;
+      case YEAR:
+        dateStr = dateToStr(date, "yyyy");
+        break;
+      case YEAR_WEEK:
+        dateStr = dateToStr(date, "yyyy") + "|" + String.format("%02d", getWhichWeekOfYear(date));
+        break;
+      case MONTH_WEEK:
+        dateStr = dateToStr(date, "yyyy-MM") + "|" + getWhichWeekOfMonth(date);
+        break;
+      default:
+        dateStr = null;
+        break;
+    }
+    return dateStr;
+  }
+
+  public static Date strToDateByType(String dateStr, DateType dateType) {
+    Date date;
+    Calendar c;
+    switch (dateType) {
+      case DAY:
+        date = strToDate(dateStr, "yyyy-MM-dd");
+        break;
+      case MONTH:
+        date = strToDate(dateStr, "yyyy-MM");
+        break;
+      case YEAR:
+        date = strToDate(dateStr, "yyyy");
+        break;
+      case YEAR_WEEK:
+        String[] arr = StringUtils.split(dateStr, "|");
+        int year = Integer.parseInt(arr[0]);
+        c = Calendar.getInstance();
+        c.setFirstDayOfWeek(firstDayOfWeek);
+        c.setTime(strToDate(arr[0], "yyyy"));
+        c.set(Calendar.WEEK_OF_YEAR, Integer.parseInt(arr[1]));
+        // 2012年1月1日为星期日,则Java某一周转换为日期后都会是星期日,所以依然会出现跨年周
+        // 如:2102|54周会转成2013年1月6日星期日,实际2012年12月31日星期一才是第54周。
+        if (c.get(Calendar.YEAR) > year) {
+          c.setTime(strToDate(arr[0], "yyyy"));
+          c.set(Calendar.DAY_OF_YEAR, c.getActualMaximum(Calendar.DAY_OF_YEAR));
+        }
+        date = c.getTime();
+        break;
+      case MONTH_WEEK:
+        arr = StringUtils.split(dateStr, "|");
+        String[] ym = StringUtils.split(arr[0], "-");
+        year = Integer.parseInt(ym[0]);
+        int month = Integer.parseInt(ym[1]);
+        c = Calendar.getInstance();
+        c.setFirstDayOfWeek(firstDayOfWeek);
+        c.setTime(strToDate(arr[0], "yyyy-MM"));
+        c.set(Calendar.WEEK_OF_MONTH, Integer.parseInt(arr[1]));
+        // 2012年12月1日为星期六,则Java某一周转换为日期后都会是星期六,所以依然会出现跨月周
+        // 如:2102-12|6周会转成2013年1月5日星期六,实际2012年12月31日星期一才是第6周。
+        if (c.get(Calendar.YEAR) > year || c.get(Calendar.MONTH) + 1 > month) {
+          c.setTime(strToDate(arr[0], "yyyy-MM"));
+          c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+        }
+        date = c.getTime();
+        break;
+      default:
+        date = null;
+        break;
+    }
+    return date;
+  }
+
+  /**
+   * 获取起始时间(日周月年)
+   *
+   * @param date
+   * @param dateType
+   * @return
+   */
+  public static Date getStartDate(Date date, DateType dateType) {
+    return getStartDate(date, dateType, 0);
+  }
+
+  /**
+   * 获取起始时间(日周月年)
+   *
+   * @param date
+   * @param dateType
+   * @param interval
+   * @return
+   */
+  public static Date getStartDate(Date date, DateType dateType, int interval) {
+    Calendar c = Calendar.getInstance();
+    c.setFirstDayOfWeek(firstDayOfWeek);
+    c.setTime(date);
+    Calendar temp;
+    switch (dateType) {
+      case DAY:
+        c.add(Calendar.DAY_OF_MONTH, interval);
+        break;
+      case MONTH:
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
+        c.add(Calendar.MONTH, interval);
+        break;
+      case YEAR:
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
+        c.set(Calendar.MONTH, c.getActualMinimum(Calendar.MONTH));
+        c.add(Calendar.YEAR, interval);
+        break;
+      case YEAR_WEEK:
+        date = getYearWeekDateByInterval(date, interval);
+        c.setTime(date);
+        int tempYear = c.get(Calendar.YEAR);
+        // 设置周的起始时间
+        c.set(Calendar.DAY_OF_WEEK, firstDayOfWeek);
+        // 跨年周处理
+        if (c.get(Calendar.YEAR) != tempYear) {
+          temp = (Calendar) c.clone();
+          temp.add(Calendar.DAY_OF_WEEK, wholeDayOfWeek - 1);
+          if (c.get(Calendar.YEAR) < temp.get(Calendar.YEAR)) {
+            c = temp;
+            c.set(Calendar.DAY_OF_YEAR, c.getActualMinimum(Calendar.DAY_OF_YEAR));
+          }
+        }
+        break;
+      case MONTH_WEEK:
+        date = getMonthWeekDateByInterval(date, interval);
+        c.setTime(date);
+        tempYear = c.get(Calendar.YEAR);
+        int tempMonth = c.get(Calendar.MONTH);
+        // 设置周的起始时间
+        c.set(Calendar.DAY_OF_WEEK, firstDayOfWeek);
+        // 跨月周处理
+        if (c.get(Calendar.YEAR) != tempYear || c.get(Calendar.MONTH) != tempMonth) {
+          temp = (Calendar) c.clone();
+          temp.add(Calendar.DAY_OF_WEEK, wholeDayOfWeek - 1);
+          if (c.get(Calendar.YEAR) < temp.get(Calendar.YEAR) || c.get(Calendar.MONTH) < temp.get(Calendar.MONTH)) {
+            c = temp;
+            c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
+          }
+        }
+        break;
+      case UP_DAY_FOR_YEAR:
+        if (interval != 0) {
+          date = getStartDate(date, DateType.DAY, interval);
+        }
+        return getStartDate(date, DateType.YEAR);
+      case UP_MONTH_FOR_YEAR:
+        if (interval != 0) {
+          date = getStartDate(date, DateType.MONTH, interval);
+        }
+        return getStartDate(date, DateType.YEAR);
+      case UP_YEAR_WEEK_FOR_YEAR:
+        if (interval != 0) {
+          date = getStartDate(date, DateType.YEAR_WEEK, interval);
+        }
+        return getStartDate(date, DateType.YEAR);
+      case UP_MONTH_WEEK_FOR_YEAR:
+        if (interval != 0) {
+          date = getStartDate(date, DateType.MONTH_WEEK, interval);
+        }
+        return getStartDate(date, DateType.YEAR);
+      default:
+        break;
+    }
+    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
+    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
+    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
+    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
+    return c.getTime();
+  }
+
+  /**
+   * 获取结束时间(日周月年)
+   *
+   * @param date
+   * @param dateType
+   * @return
+   */
+  public static Date getEndDate(Date date, DateType dateType) {
+    return getEndDate(date, dateType, 0);
+  }
+
+  /**
+   * 获取结束时间(日周月年)
+   *
+   * @param date
+   * @param dateType
+   * @param interval
+   * @return
+   */
+  public static Date getEndDate(Date date, DateType dateType, int interval) {
+    Calendar c = Calendar.getInstance();
+    c.setFirstDayOfWeek(firstDayOfWeek);
+    c.setTime(date);
+    Calendar temp;
+    switch (dateType) {
+      case DAY:
+      case UP_DAY_FOR_YEAR:
+        c.add(Calendar.DAY_OF_MONTH, interval);
+        break;
+      case MONTH:
+      case UP_MONTH_FOR_YEAR:
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
+        c.add(Calendar.MONTH, interval);
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+        break;
+      case YEAR:
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
+        c.set(Calendar.MONTH, c.getActualMinimum(Calendar.MONTH));
+        c.add(Calendar.YEAR, interval);
+        c.set(Calendar.MONTH, c.getActualMaximum(Calendar.MONTH));
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+        break;
+      case YEAR_WEEK:
+      case UP_YEAR_WEEK_FOR_YEAR:
+        date = getYearWeekDateByInterval(date, interval);
+        c.setTime(date);
+        int tempYear = c.get(Calendar.YEAR);
+        // 设置周的截止时间
+        c.set(Calendar.DAY_OF_WEEK, firstDayOfWeek);
+        c.add(Calendar.DAY_OF_WEEK, wholeDayOfWeek - 1);
+        // 跨年周处理
+        if (c.get(Calendar.YEAR) != tempYear) {
+          temp = (Calendar) c.clone();
+          temp.set(Calendar.DAY_OF_WEEK, firstDayOfWeek);
+          if (c.get(Calendar.YEAR) > temp.get(Calendar.YEAR)) {
+            c = temp;
+            c.set(Calendar.DAY_OF_YEAR, c.getActualMaximum(Calendar.DAY_OF_YEAR));
+          }
+        }
+        break;
+      case MONTH_WEEK:
+      case UP_MONTH_WEEK_FOR_YEAR:
+        date = getMonthWeekDateByInterval(date, interval);
+        c.setTime(date);
+        tempYear = c.get(Calendar.YEAR);
+        int tempMonth = c.get(Calendar.MONTH);
+        // 设置周的截止时间
+        c.set(Calendar.DAY_OF_WEEK, firstDayOfWeek);
+        c.add(Calendar.DAY_OF_WEEK, wholeDayOfWeek - 1);
+        // 跨月周处理
+        if (c.get(Calendar.YEAR) != tempYear || c.get(Calendar.MONTH) != tempMonth) {
+          temp = (Calendar) c.clone();
+          temp.set(Calendar.DAY_OF_WEEK, firstDayOfWeek);
+          if (c.get(Calendar.YEAR) > temp.get(Calendar.YEAR) || c.get(Calendar.MONTH) > temp.get(Calendar.MONTH)) {
+            c = temp;
+            c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+          }
+        }
+        break;
+      default:
+        break;
+    }
+    c.set(Calendar.HOUR_OF_DAY, c.getActualMaximum(Calendar.HOUR_OF_DAY));
+    c.set(Calendar.MINUTE, c.getActualMaximum(Calendar.MINUTE));
+    c.set(Calendar.SECOND, c.getActualMaximum(Calendar.SECOND));
+    c.set(Calendar.MILLISECOND, c.getActualMaximum(Calendar.MILLISECOND));
+    return c.getTime();
+  }
+
+  /**
+   * 获取两个日期的区间天数(例:6号~8号,其区间天数为三天)
+   *
+   * @param startDate
+   * @param endDate
+   * @return
+   */
+  public static int getIntervalDays(Date startDate, Date endDate) {
+    Calendar c = Calendar.getInstance();
+    c.setTime(startDate);
+    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
+    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
+    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
+    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
+    long start = c.getTimeInMillis();
+    c.setTime(endDate);
+    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
+    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
+    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
+    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
+    long end = c.getTimeInMillis();
+    int days = (int) ((end - start) / (24 * 60 * 60 * 1000));
+    days = Math.abs(days) + 1;
+    return days;
+  }
+
+  /**
+   * 获取某个日期所在月份的每一天
+   *
+   * @param date
+   * @return
+   */
+  public static List<Date> listDateOfMonth(Date date) {
+    Date startDate = getStartDate(date, DateType.MONTH);
+    Date endDate = getEndDate(date, DateType.MONTH);
+    return listDateOfInterval(startDate, endDate);
+  }
+
+
+  /**
+   * 获取某个日期所在周的每一天
+   *
+   * @param date
+   * @return
+   */
+  public static List<Date> listDateOfWeek(Date date) {
+    Date startDate = getStartDate(date, DateType.MONTH_WEEK);
+    Date endDate = getEndDate(date, DateType.MONTH_WEEK);
+    return listDateOfInterval(startDate, endDate);
+  }
+
+
+  /**
+   * 获取某段时间的每一天
+   *
+   * @param startDate
+   * @param endDate
+   * @return
+   */
+  public static List<Date> listDateOfInterval(Date startDate, Date endDate) {
+    List<Date> days = new ArrayList<>();
+    Calendar c = Calendar.getInstance();
+    c.setTime(startDate);
+    while (c.getTimeInMillis() <= endDate.getTime()) {
+      days.add(c.getTime());
+      c.add(Calendar.DAY_OF_MONTH, 1);
+    }
+    return days;
+  }
+
+  /**
+   * 根据日期类型获取时间段内格式化后的日期字符串集合
+   *
+   * @param startDate
+   * @param endDate
+   * @param dateType
+   * @return
+   */
+  public static List<String> listFormatDate(Date startDate, Date endDate, DateType dateType) {
+    List<String> dates = new ArrayList<>();
+    endDate = DateUtil.getStartDate(endDate, dateType);
+    int interval = 0;
+    while (true) {
+      Date tempDate = DateUtil.getStartDate(startDate, dateType, interval);
+      String tempDateStr = dateToStrByType(tempDate, dateType);
+      dates.add(tempDateStr);
+      // 中断条件
+      if (tempDate.getTime() == endDate.getTime()) {
+        break;
+      } else if (tempDate.getTime() > endDate.getTime()) {
+        interval--;
+      } else if (tempDate.getTime() < endDate.getTime()) {
+        interval++;
+      }
+    }
+    return dates;
+  }
+
+  /**
+   * 获取年份周的前几周或后几周的时间
+   *
+   * @param date
+   * @param interval
+   * @return
+   */
+  public static Date getYearWeekDateByInterval(Date date, int interval) {
+    if (interval < 0) {
+      int minWeekOfYear = 1;
+
+      Calendar c = Calendar.getInstance();
+      c.setFirstDayOfWeek(firstDayOfWeek);
+      c.setTime(date);
+      int weekOfYear = (int) Math.ceil((c.get(Calendar.DAY_OF_YEAR) + wholeDayOfWeek - getDayOfWeek(c.getTime())) / (double) wholeDayOfWeek);
+      int tempInterval = (weekOfYear + interval) - minWeekOfYear;
+      if (tempInterval < 0) {
+        c.add(Calendar.YEAR, -1);
+        c.set(Calendar.DAY_OF_YEAR, c.getActualMaximum(Calendar.DAY_OF_YEAR));
+        date = getYearWeekDateByInterval(c.getTime(), tempInterval + 1);
+      } else if (tempInterval > 0) {
+        c.add(Calendar.WEEK_OF_YEAR, interval);
+        date = c.getTime();
+      } else {
+        c.set(Calendar.DAY_OF_YEAR, c.getActualMinimum(Calendar.DAY_OF_YEAR));
+        date = c.getTime();
+      }
+    } else if (interval > 0) {
+      Calendar temp = Calendar.getInstance();
+      temp.setFirstDayOfWeek(firstDayOfWeek);
+      temp.setTime(date);
+      temp.set(Calendar.DAY_OF_YEAR, temp.getActualMaximum(Calendar.DAY_OF_YEAR));
+      int maxWeekOfYear = (int) Math.ceil((temp.get(Calendar.DAY_OF_YEAR) + wholeDayOfWeek - getDayOfWeek(temp.getTime())) / (double) wholeDayOfWeek);
+
+      Calendar c = Calendar.getInstance();
+      c.setFirstDayOfWeek(firstDayOfWeek);
+      c.setTime(date);
+      int weekOfYear = (int) Math.ceil((c.get(Calendar.DAY_OF_YEAR) + wholeDayOfWeek - getDayOfWeek(c.getTime())) / (double) wholeDayOfWeek);
+      int tempInterval = (weekOfYear + interval) - maxWeekOfYear;
+      if (tempInterval > 0) {
+        c.add(Calendar.YEAR, 1);
+        c.set(Calendar.DAY_OF_YEAR, c.getActualMinimum(Calendar.DAY_OF_YEAR));
+        date = getYearWeekDateByInterval(c.getTime(), tempInterval - 1);
+      } else if (tempInterval < 0) {
+        c.add(Calendar.WEEK_OF_YEAR, interval);
+        date = c.getTime();
+      } else {
+        c.set(Calendar.DAY_OF_YEAR, c.getActualMaximum(Calendar.DAY_OF_YEAR));
+        date = c.getTime();
+      }
+    }
+    return date;
+  }
+
+  /**
+   * 获取月份周的前几周或后几周的时间
+   *
+   * @param date
+   * @param interval
+   * @return
+   */
+  public static Date getMonthWeekDateByInterval(Date date, int interval) {
+    if (interval < 0) {
+      Calendar c = Calendar.getInstance();
+      c.setFirstDayOfWeek(firstDayOfWeek);
+      c.setTime(date);
+      int minWeekOfMonth = 1;
+      int weekOfMonth = c.get(Calendar.WEEK_OF_MONTH);
+      int tempInterval = (weekOfMonth + interval) - minWeekOfMonth;
+      if (tempInterval < 0) {
+        c.add(Calendar.MONTH, -1);
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+        date = getMonthWeekDateByInterval(c.getTime(), tempInterval + 1);
+      } else if (tempInterval > 0) {
+        c.add(Calendar.WEEK_OF_MONTH, interval);
+        date = c.getTime();
+      } else {
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
+        date = c.getTime();
+      }
+    } else if (interval > 0) {
+      Calendar c = Calendar.getInstance();
+      c.setFirstDayOfWeek(firstDayOfWeek);
+      c.setTime(date);
+      int maxWeekOfMonth = c.getActualMaximum(Calendar.WEEK_OF_MONTH);
+      int weekOfMonth = c.get(Calendar.WEEK_OF_MONTH);
+      int tempInterval = (weekOfMonth + interval) - maxWeekOfMonth;
+      if (tempInterval > 0) {
+        c.add(Calendar.MONTH, 1);
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH));
+        date = getMonthWeekDateByInterval(c.getTime(), tempInterval - 1);
+      } else if (tempInterval < 0) {
+        c.add(Calendar.WEEK_OF_MONTH, interval);
+        date = c.getTime();
+      } else {
+        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+        date = c.getTime();
+      }
+    }
+    return date;
+  }
+
+  /**
+   * 获取当前日期为年的第几周
+   *
+   * @param date
+   * @return
+   */
+  public static int getWhichWeekOfYear(Date date) {
+    String year = dateToStr(date, "yyyy");
+    Calendar c = Calendar.getInstance();
+    c.setFirstDayOfWeek(firstDayOfWeek);
+    c.setTime(date);
+    int weekOfYear = (int) Math.ceil((c.get(Calendar.DAY_OF_YEAR) + wholeDayOfWeek - getDayOfWeek(c.getTime())) / (double) wholeDayOfWeek);
+    return weekOfYear;
+  }
+
+  /**
+   * 获取一年共有几周
+   *
+   * @param date
+   * @return
+   */
+  public static int getWholeWeekOfYear(Date date) {
+    Date endDateOfMonth = DateUtil.getEndDate(date, DateType.YEAR);
+    return getWhichWeekOfYear(endDateOfMonth);
+  }
+
+  /**
+   * 获取当前日期为月的第几周
+   *
+   * @param date
+   * @return
+   */
+  public static int getWhichWeekOfMonth(Date date) {
+    Calendar c = Calendar.getInstance();
+    c.setFirstDayOfWeek(firstDayOfWeek);
+    c.setTime(date);
+    int whichWeek = c.get(Calendar.WEEK_OF_MONTH);
+    return whichWeek;
+  }
+
+  /**
+   * 获取一个月共有几周
+   *
+   * @param date
+   * @return
+   */
+  public static int getWholeWeekOfMonth(Date date) {
+    Date endDateOfMonth = DateUtil.getEndDate(date, DateType.MONTH);
+    return getWhichWeekOfMonth(endDateOfMonth);
+  }
+
+  /**
+   * 获取当前日期为一周的第几天
+   *
+   * @param date
+   * @return
+   */
+  public static int getDayOfWeek(Date date) {
+    Calendar c = Calendar.getInstance();
+    c.setTime(date);
+    int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - firstDayOfWeek + 1;
+    if (dayOfWeek <= 0) {
+      dayOfWeek += wholeDayOfWeek;
+    }
+    return dayOfWeek;
+  }
+
+  /**
+   * 获取月的某一周实际有几天
+   *
+   * @param year
+   * @param month
+   * @param week
+   * @return
+   */
+  public static int getWholeDayOfMonthWeek(int year, int month, int week) {
+    String dateStr = year + "-" + month;
+    Calendar c = Calendar.getInstance();
+    c.setFirstDayOfWeek(firstDayOfWeek);
+
+    /*
+     * c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH)); c.set(Calendar.MONTH, month - 1); System.out.println(c.getTime()); // 这一行难道可以刷新设置月份后的时间
+     * c.set(Calendar.YEAR, year);
+     */
+    c.setTime(strToDate(dateStr, "yyyy-M"));
+    c.set(Calendar.WEEK_OF_MONTH, week);
+    if (c.get(Calendar.YEAR) > year || c.get(Calendar.MONTH) + 1 > month) {
+      /*
+       * c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH)); c.set(Calendar.MONTH, month - 1); c.set(Calendar.YEAR, year);
+       */
+      c.setTime(strToDate(dateStr, "yyyy-M"));
+      c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
+    }
+    Date date = c.getTime();
+    Date startDate = getStartDate(date, DateType.MONTH_WEEK);
+    Date endDate = getEndDate(date, DateType.MONTH_WEEK);
+    int days = getIntervalDays(startDate, endDate);
+    return days;
+  }
+
+  /**
+   * 获取backupMonth,如果传入日期的截止时间大于当前月份第一天则返回空字符串,否则以结尾时间的月份作为backupMonth
+   *
+   * @param date
+   * @param dateType
+   * @return
+   */
+  public static String getBackupMonth(Date date, DateType dateType) {
+    date = getEndDate(date, dateType);
+    Date currentMonth = DateUtil.getStartDate(new Date(), DateType.MONTH);
+    String dateStr = DateUtil.dateToStr(date, "yyyy-MM");
+    if (date.compareTo(currentMonth) >= 0) {
+      dateStr = "";
+    }
+    return dateStr;
+  }
+
+
+  public static String getDateStrByDate(Date d, SimpleDateFormat sf) {
+    String str = null;
+    try {
+      str = sf.format(d);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return str;
+  }
+
+  public static String getDateStrByDate(Date d) {
+    SimpleDateFormat yyyy_mm_ddFormat = new SimpleDateFormat("yyyy-MM-dd");
+    return getDateStrByDate(d, yyyy_mm_ddFormat);
+  }
+
+  public static String getDateMonthStrByDate(Date d) {
+    SimpleDateFormat yyyy_mmFormat = new SimpleDateFormat("yyyy-MM");
+    return getDateStrByDate(d, yyyy_mmFormat);
+  }
+
+  public static Date getDateByStr(String str) {
+    SimpleDateFormat yyyy_mm_ddFormat = new SimpleDateFormat("yyyy-MM-dd");
+    Date d = null;
+    try {
+      d = yyyy_mm_ddFormat.parse(str);
+    } catch (ParseException e) {
+      e.printStackTrace();
+    }
+    return d;
+  }
+
+  public static Date getMaxMonthDate(Date date) {
+    Calendar calendar = Calendar.getInstance();
+    calendar.setTime(date);
+    calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+    return calendar.getTime();
+  }
+
+  public static Date getMinMonthDate(Date date) {
+    Calendar calendar = Calendar.getInstance();
+    calendar.setTime(date);
+    calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
+    return calendar.getTime();
+  }
+
+  public static String getYesterday(SimpleDateFormat sd) {
+    Date date = new Date();// 取时间
+    Calendar calendar = new GregorianCalendar();
+    calendar.setTime(date);
+    calendar.add(Calendar.DATE, -1);// 把日期往前减少一天,若想把日期向后推一天则将负数改为正数
+    date = calendar.getTime();
+    if (sd == null)
+      sd = new SimpleDateFormat("yyyy-MM-dd");
+    String dateStr = sd.format(date);
+    // System.out.println("*******得到明天的日期*******" + dateStr);
+    return dateStr;
+  }
+
+  /**
+   * 得到几天前的时间
+   *
+   * @param d
+   * @param day
+   * @return
+   */
+  public static Date getDateBefore(Date d, int day) {
+    Calendar now = Calendar.getInstance();
+    now.setTime(d);
+    now.set(Calendar.DATE, now.get(Calendar.DATE) - day);
+    return now.getTime();
+  }
+
+  /**
+   * 得到几天后的时间
+   *
+   * @param d
+   * @param day
+   * @return
+   */
+  public static Date getDateAfter(Date d, int day) {
+    Calendar now = Calendar.getInstance();
+    now.setTime(d);
+    now.set(Calendar.DATE, now.get(Calendar.DATE) + day);
+    return now.getTime();
+  }
+
+  /**
+   * 得到几个月前的时间
+   *
+   * @param d
+   * @param month
+   * @return
+   */
+  public static Date getDateBeforeMonth(Date d, int month) {
+    d = getDateFirstDayOfMonth(d.getYear() + 1900, d.getMonth() + 1, null);
+    Calendar now = Calendar.getInstance();
+    now.setTime(d);
+    now.set(Calendar.MONTH, now.get(Calendar.MONTH) - month);
+    return now.getTime();
+  }
+
+  /**
+   * 得到几个月后的时间
+   *
+   * @param d
+   * @param month
+   * @return
+   */
+  public static Date getDateAfterMonth(Date d, int month) {
+    d = getDateFirstDayOfMonth(d.getYear() + 1900, d.getMonth()+1, null);
+    Calendar now = Calendar.getInstance();
+    now.setTime(d);
+    now.set(Calendar.MONTH, now.get(Calendar.MONTH) + month);
+    return now.getTime();
+  }
+
+  public static String getFirstDayOfMonth(int year, int month, SimpleDateFormat sd) {
+    if (sd == null)
+      sd = new SimpleDateFormat("yyyy-MM-dd");
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.YEAR, year);
+    calendar.set(Calendar.MONTH, month);
+    calendar.set(Calendar.HOUR_OF_DAY, 0);
+    calendar.set(Calendar.MINUTE, 0);
+    calendar.set(Calendar.SECOND, 0);
+    calendar.set(Calendar.DAY_OF_MONTH, 1);
+    calendar.add(Calendar.DAY_OF_MONTH, -1);
+    calendar.set(Calendar.DAY_OF_MONTH, 1);
+    Date date = calendar.getTime();
+    return sd.format(date);
+  }
+
+  public static Date getDateFirstDayOfMonth(int year, int month, SimpleDateFormat sd) {
+    if (sd == null)
+      sd = new SimpleDateFormat("yyyy-MM-dd");
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.YEAR, year);
+    calendar.set(Calendar.MONTH, month);
+    calendar.set(Calendar.HOUR_OF_DAY, 0);
+    calendar.set(Calendar.MINUTE, 0);
+    calendar.set(Calendar.SECOND, 0);
+    calendar.set(Calendar.DAY_OF_MONTH, 1);
+    calendar.add(Calendar.DAY_OF_MONTH, -1);
+    calendar.set(Calendar.DAY_OF_MONTH, 1);
+    Date date = calendar.getTime();
+    return date;
+  }
+
+  public static Date getFirstDayOfMonth(int year, int month) {
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.YEAR, year);
+    calendar.set(Calendar.MONTH, month);
+    calendar.set(Calendar.HOUR_OF_DAY, 0);
+    calendar.set(Calendar.MINUTE, 0);
+    calendar.set(Calendar.SECOND, 0);
+    calendar.set(Calendar.DAY_OF_MONTH, 1);
+    calendar.add(Calendar.DAY_OF_MONTH, -1);
+    calendar.set(Calendar.DAY_OF_MONTH, 1);
+    return calendar.getTime();
+  }
+
+  public static int getDaysOfMonth(Date date) {
+    Calendar calendar = Calendar.getInstance();
+    calendar.setTime(date);
+    return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
+  }
+
+  public static boolean isThisYear(Date date) {
+    Calendar now = Calendar.getInstance();
+    Calendar tmp = Calendar.getInstance();
+    tmp.setTime(date);
+    return tmp.get(Calendar.YEAR) == now.get(Calendar.YEAR);
+  }
+
+  public static Date getDate(String dateStr, String pattern) {
+    try {
+      String time = dateStr.replaceAll("/", "-");
+      SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+      return sdf.parse(time);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    return null;
+  }
+
+  /**
+   * 获取过去7天的日期
+   *
+   * @param past
+   * @param customDate
+   * @return
+   */
+  public static String getPastDateStr(int past, String customDate) {
+    Calendar calendar = Calendar.getInstance();
+    Date paramDate = DateUtil.getDateByStr(customDate);
+    calendar.setTime(paramDate);
+    calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - past);
+    Date day = calendar.getTime();
+    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+    String startTime = format.format(day);
+    return startTime;
+  }
+
+  /**
+   * 获取过去n天的日期max time
+   *
+   * @param past
+   * @param customDate
+   * @return
+   */
+  public static String getMaxPastDateStr(int past, String customDate) {
+    String endTime = getPastDateStr(past, customDate) + " 23:59:59";
+    return endTime;
+  }
+
+
+
+}

+ 73 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/FileMD5Util.java

@@ -0,0 +1,73 @@
+package com.mooctest.crowd.site.util.file;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * 计算文件MD5工具类
+ */
+public class FileMD5Util {
+
+  private final static Logger logger = LoggerFactory.getLogger(FileMD5Util.class);
+
+  public static String getFileMD5(File file) throws FileNotFoundException {
+
+    String value = null;
+    FileInputStream in = new FileInputStream(file);
+    MappedByteBuffer byteBuffer = null;
+    try {
+      byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
+      MessageDigest md5 = MessageDigest.getInstance("MD5");
+      md5.update(byteBuffer);
+      BigInteger bi = new BigInteger(1, md5.digest());
+      value = bi.toString(16);
+      if (value.length() < 32) {
+        value = "0" + value;
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    } finally {
+      FileUtil.close(in, byteBuffer);
+    }
+    return value;
+  }
+
+  public static String getFileMD5(MultipartFile file) {
+
+    try {
+      byte[] uploadBytes = file.getBytes();
+      MessageDigest md5 = MessageDigest.getInstance("MD5");
+      byte[] digest = md5.digest(uploadBytes);
+      String hashString = new BigInteger(1, digest).toString(16);
+      return hashString;
+    } catch (IOException e) {
+      logger.error("get file md5 error!!!", e);
+    } catch (NoSuchAlgorithmException e) {
+      logger.error("get file md5 error!!!", e);
+    }
+    return null;
+  }
+
+
+
+  public static void main(String[] args) throws Exception {
+
+    long start = System.currentTimeMillis();
+    String filePath = "F:\\desktop\\uploads\\461ad6106d8253b94bd00546a4a1a8e4\\pycharm-professional-2019.1.3.exe";
+    File file = new File(filePath);
+    String md5 = FileMD5Util.getFileMD5(file);
+    long end = System.currentTimeMillis();
+    System.out.println("cost:" + (end - start) + "ms, md5:" + md5);
+  }
+}

+ 53 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/FilePathUtil.java

@@ -0,0 +1,53 @@
+package com.mooctest.crowd.site.util.file;
+
+import com.mooctest.crowd.site.constants.FileConstant;
+import com.mooctest.crowd.site.data.dto.FileUploadRequestDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+
+@Component
+@Slf4j
+public class FilePathUtil implements ApplicationRunner {
+
+  @Value("${upload.root.dir}")
+  private String uploadRootDir;
+
+  @Value("${upload.window.root}")
+  private String uploadWindowRoot;
+
+  @Override
+  public void run(ApplicationArguments args) {
+    createUploadRootDir();
+  }
+
+  private void createUploadRootDir(){
+    String path = getBasePath();
+    File file = new File(path);
+    if(!file.mkdirs()){
+      file.mkdirs();
+    }
+  }
+
+  public String getPath(){
+    return uploadRootDir;
+  }
+
+  public String getBasePath(){
+    String path = uploadRootDir;
+    if(SystemUtil.isWinOs()){
+      path = uploadWindowRoot + uploadRootDir;
+    }
+
+    return path;
+  }
+
+  public String getPath(FileUploadRequestDTO param){
+    String path = this.getBasePath() + FileConstant.FILE_SEPARATORCHAR + param.getPath() + FileConstant.FILE_SEPARATORCHAR + param.getMd5();
+    return path;
+  }
+}

+ 709 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/FileUtil.java

@@ -0,0 +1,709 @@
+package com.mooctest.crowd.site.util.file;
+
+import cn.afterturn.easypoi.excel.ExcelImportUtil;
+import cn.afterturn.easypoi.excel.entity.ImportParams;
+import com.mooctest.crowd.domain.exception.FileCopyException;
+import com.mooctest.crowd.domain.exception.FileDownloadException;
+import com.mooctest.crowd.site.constants.FileConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.multipart.MultipartFile;
+import sun.misc.Cleaner;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.lang.reflect.Method;
+import java.nio.MappedByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * @author: Diors.Po
+ * @Email: 171256175@qq.com
+ * @date 2019-08-05 19:57
+ */
+@Slf4j
+public class FileUtil {
+
+    public static final String PATH_HEAD = "/vagrant/";
+    public static final String USER_HEAD = "/data/User";
+    public static final String DOT = ".";
+    public static final String SLASH_ONE = "/";
+    public static final String SLASH_TWO = "\\";
+    public static final String HOME = "";
+    private static String uploadWindowRoot;
+
+    public static final Map<String, String> FILE_TYPE_MAP = new HashMap<>(); //本系统中合法的文件类型
+
+    static {
+
+        //图片
+        FILE_TYPE_MAP.put("ffd8ff", "jpg"); //JPEG (jpg)
+        FILE_TYPE_MAP.put("89504e", "png"); //PNG (png)
+        FILE_TYPE_MAP.put("474946", "gif"); //GIF (gif)
+
+        FILE_TYPE_MAP.put("49492a", "tif"); //TIFF (tif)
+        FILE_TYPE_MAP.put("492049", "tif");
+        FILE_TYPE_MAP.put("4d4d00", "tif");
+
+        FILE_TYPE_MAP.put("424d22", "bmp"); //16色位图(bmp)
+        FILE_TYPE_MAP.put("424d82", "bmp"); //24位位图(bmp)
+        FILE_TYPE_MAP.put("424d8e", "bmp"); //256色位图(bmp)
+
+        //压缩文件
+        FILE_TYPE_MAP.put("504b03", "zip");
+        FILE_TYPE_MAP.put("504b05", "zip");
+        FILE_TYPE_MAP.put("504b07", "zip");
+        FILE_TYPE_MAP.put("526172", "rar");
+
+        //文档
+        FILE_TYPE_MAP.put("255044", "pdf"); //Adobe Acrobat (pdf)
+        FILE_TYPE_MAP.put("7b5c72", "doc"); //doc文件
+        FILE_TYPE_MAP.put("504b03", "docx");//docx文件,xlsx等一致
+        FILE_TYPE_MAP.put("d0cf11", "doc/dot"); //MS Excel 注意:word、msi 和 excel的文件头一样
+        FILE_TYPE_MAP.put("0d444f", "doc/dot");
+        FILE_TYPE_MAP.put("cf11e0", "doc/dot");
+        FILE_TYPE_MAP.put("dba52d", "doc/dot");
+        FILE_TYPE_MAP.put("d0cf11", "wps/wks");//WPS文字wps、表格et、演示dps都是一样的
+        FILE_TYPE_MAP.put("0e574b", "wps/wks");
+        FILE_TYPE_MAP.put("ff0002", "wps/wks");
+
+        //exe可执行文件
+        FILE_TYPE_MAP.put("4d5a90", "exe");
+    }
+
+    public static String save(String dirPath, MultipartFile inputFile, String fileName) throws IOException {
+        byte[] bs = new byte[1024];
+        int len;
+
+        File saveDir = new File(dirPath);
+        if (!saveDir.exists()) {
+            saveDir.mkdirs();
+        }
+        if(fileName.equals("")){
+            fileName = inputFile.getOriginalFilename();
+        }
+        String saveFilePath = saveDir.getPath() + "/"  + fileName;
+        OutputStream os = new FileOutputStream( saveFilePath );
+        InputStream is = inputFile.getInputStream();
+        while ((len = is.read(bs)) != -1) {
+            os.write(bs, 0, len);
+        }
+
+        os.close();
+        is.close();
+
+        return saveFilePath.replaceAll("\\\\", "/" );
+    }
+
+    public static boolean checkFile(InputStream inputStream){
+        try {
+            byte[] headBytes = new byte[3];
+            inputStream.read(headBytes, 0, headBytes.length);
+            String headStr = bytesToHexString(headBytes).toLowerCase();
+            log.info("The Head of File: "+headStr);
+//            log.info("Whether the map contain the " + headStr +": " + FILE_TYPE_MAP.containsKey(headStr));
+            return FILE_TYPE_MAP.containsKey(headStr);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+    public static boolean checkExcel(InputStream inputStream){
+        String headStr = getHeadStr(inputStream);
+        log.info("The Head of File: "+headStr);
+        String name = FILE_TYPE_MAP.get(headStr);
+        if (name == null || (!name.contains("doc")&&(!name.contains("wps"))))
+            return false;
+        return true;
+    }
+
+    public static String getHeadStr(InputStream inputStream){
+        try {
+            byte[] headBytes = new byte[3];
+            inputStream.read(headBytes, 0, headBytes.length);
+            return bytesToHexString(headBytes).toLowerCase();
+        } catch (IOException e) {
+            throw new RuntimeException("读取文件头出错!", e);
+        }
+    }
+
+    private static String bytesToHexString(byte[] src) {
+        StringBuilder builder = new StringBuilder();
+        if (src == null || src.length <= 0) {
+            return null;
+        }
+        String hv;
+        for (int i = 0; i < src.length; i++) {
+            // 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式,并转换为大写
+            hv = Integer.toHexString(src[i] & 0xFF).toUpperCase();
+            if (hv.length() < 2) {
+                builder.append(0);
+            }
+            builder.append(hv);
+        }
+        log.info("HexString: " + builder.toString());
+        return builder.toString();
+    }
+
+    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass){
+        if (file == null){
+            return null;
+        }
+        ImportParams params = new ImportParams();
+        params.setTitleRows(titleRows);
+        params.setHeadRows(headerRows);
+        List<T> list = null;
+        try {
+            list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);
+        }catch (NoSuchElementException e){
+            log.info("excel文件不能为空");
+            throw new RuntimeException("excel文件不能为空");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return list;
+    }
+
+    public static void moveFiles(String oldPath, String newPath) throws IOException {
+
+        String[] filePaths = new File(oldPath).list();
+
+        if (filePaths != null && filePaths.length > 0) {
+            if (!new File(newPath).exists()) {
+                new File(newPath).mkdirs();
+            }
+
+            for (int i = 0; i < filePaths.length; i++) {
+                if (new File(oldPath + File.separator + filePaths[i]).isDirectory()) {
+                    moveFiles(oldPath + File.separator + filePaths[i],
+                            newPath + File.separator + filePaths[i]);
+                } else if (new File(oldPath + File.separator + filePaths[i]).isFile()) {
+                    copyFile(oldPath + File.separator + filePaths[i],
+                            newPath + File.separator + filePaths[i]);
+                    new File(oldPath + File.separator + filePaths[i])
+                            .renameTo(new File(newPath + File.separator + filePaths[i]));
+                }
+            }
+        }
+    }
+
+    public static void copyFile(String oldPath, String newPath) {
+
+        try {
+            File oldFile = new File(oldPath);
+            File file = new File(newPath);
+            FileInputStream in = new FileInputStream(oldFile);
+            FileOutputStream out = new FileOutputStream(file);
+            byte[] buffer = new byte[2097152];
+
+            while ((in.read(buffer)) != -1) {
+                out.write(buffer);
+            }
+        } catch (IOException e) {
+            throw new FileCopyException();
+        }
+    }
+
+    /**
+     * 获取含扩展名的文件名(不包含path路径)
+     */
+    public static String getFileName(String fileName) {
+
+        String name = "";
+        if (StringUtils.lastIndexOf(fileName, SLASH_ONE) >= StringUtils
+                .lastIndexOf(fileName, SLASH_TWO)) {
+            name = StringUtils
+                    .substring(fileName, StringUtils.lastIndexOf(fileName, SLASH_ONE) + 1,
+                            fileName.length());
+
+        } else {
+            name = StringUtils
+                    .substring(fileName, StringUtils.lastIndexOf(fileName, SLASH_TWO) + 1,
+                            fileName.length());
+        }
+        return StringUtils.trimToEmpty(name);
+    }
+
+    /**
+     * 获取没有扩展名的文件名
+     */
+    public static String getWithoutExtension(String fileName) {
+
+        String ext = StringUtils.substring(fileName, 0,
+                StringUtils.lastIndexOf(fileName, DOT) == -1 ? fileName.length() : StringUtils.lastIndexOf(fileName, DOT));
+        return StringUtils.trimToEmpty(ext);
+    }
+
+    /**
+     * 获取扩展名
+     */
+    public static String getExtension(String fileName) {
+
+        if (StringUtils.INDEX_NOT_FOUND == StringUtils.indexOf(fileName, DOT)) {
+            return StringUtils.EMPTY;
+        }
+        String ext = StringUtils.substring(fileName,
+                StringUtils.lastIndexOf(fileName, DOT) + 1);
+        return StringUtils.trimToEmpty(ext);
+    }
+
+    /**
+     * 判断是否同为扩展名
+     */
+    public static boolean isExtension(String fileName, String ext) {
+
+        return StringUtils.equalsIgnoreCase(getExtension(fileName), ext);
+    }
+
+    /**
+     * 判断是否存在扩展名
+     */
+    public static boolean hasExtension(String fileName) {
+
+        return !isExtension(fileName, StringUtils.EMPTY);
+    }
+
+    /**
+     * 得到正确的扩展名
+     */
+    public static String trimExtension(String ext) {
+
+        return getExtension(DOT + ext);
+    }
+
+    /**
+     * 向path中填充扩展名(如果没有或不同的话)
+     */
+    public static String fillExtension(String fileName, String ext) {
+
+        fileName = replacePath(fileName + DOT);
+        ext = trimExtension(ext);
+        if (!hasExtension(fileName)) {
+            return fileName + getExtension(ext);
+        }
+        if (!isExtension(fileName, ext)) {
+            return getWithoutExtension(fileName) + getExtension(ext);
+        }
+        return fileName;
+    }
+
+    /**
+     * 判断是否是文件PATH
+     */
+    public static boolean isFile(String fileName) {
+
+        return hasExtension(fileName);
+    }
+
+    /**
+     * 判断是否是文件夹PATH
+     */
+    public static boolean isFolder(String fileName) {
+
+        return !hasExtension(fileName);
+    }
+
+    public static String replacePath(String path) {
+
+        return StringUtils.replace(StringUtils.trimToEmpty(path), SLASH_ONE,
+                SLASH_TWO);
+    }
+
+    /**
+     * 链接PATH前处理
+     */
+    public static String trimLeftPath(String path) {
+
+        if (isFile(path)) {
+            return path;
+        }
+        path = replacePath(path);
+        String top = StringUtils.left(path, 1);
+        if (StringUtils.equalsIgnoreCase(SLASH_TWO, top)) {
+            return StringUtils.substring(path, 1);
+        }
+        return path;
+    }
+
+    /**
+     * 链接PATH后处理
+     */
+    public static String trimRightPath(String path) {
+
+        if (isFile(path)) {
+            return path;
+        }
+        path = replacePath(path);
+        String bottom = StringUtils.right(path, 1);
+        if (StringUtils.equalsIgnoreCase(SLASH_TWO, bottom)) {
+            return StringUtils.substring(path, 0, path.length() - 2);
+        }
+        return path + SLASH_TWO;
+    }
+
+    /**
+     * 链接PATH前后处理,得到准确的链接PATH
+     */
+    public static String trimPath(String path) {
+
+        path = StringUtils.replace(StringUtils.trimToEmpty(path), SLASH_ONE,
+                SLASH_TWO);
+        path = trimLeftPath(path);
+        path = trimRightPath(path);
+        return path;
+    }
+
+    /**
+     * 通过数组完整链接PATH
+     */
+    public static String bulidFullPath(String... paths) {
+
+        StringBuffer sb = new StringBuffer();
+        for (String path : paths) {
+            sb.append(trimPath(path));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 去除首尾斜杠 path
+     */
+    public static String withoutHeadAndTailDiagonal(String path) {
+
+        int start = 0;
+        int end = 0;
+        boolean existHeadDiagonal = path.startsWith(FileConstant.FILE_SEPARATORCHAR);
+        boolean existTailDiagonal = path.endsWith(FileConstant.FILE_SEPARATORCHAR);
+        if (existHeadDiagonal && existTailDiagonal) {
+            start = StringUtils.indexOf(path, FileConstant.FILE_SEPARATORCHAR, 0) + 1;
+            end = StringUtils.lastIndexOf(path, FileConstant.FILE_SEPARATORCHAR);
+            return StringUtils.substring(path, start, end);
+        } else if (existHeadDiagonal && !existTailDiagonal) {
+            start = StringUtils.indexOf(path, FileConstant.FILE_SEPARATORCHAR, 0) + 1;
+            return StringUtils.substring(path, start);
+        } else if (!existHeadDiagonal && existTailDiagonal) {
+            end = StringUtils.lastIndexOf(path, FileConstant.FILE_SEPARATORCHAR);
+            return StringUtils.substring(path, 0, end);
+        }
+        return path;
+    }
+
+    public static void delFile(String filepath) {
+
+        File f = new File(filepath);//定义文件路径
+        if (f.exists() && f.isDirectory()) {//判断是文件还是目录
+            if (f.listFiles().length != 0) {
+                //若有则把文件放进数组,并判断是否有下级目录
+                File delFile[] = f.listFiles();
+                int i = f.listFiles().length;
+                for (int j = 0; j < i; j++) {
+                    if (delFile[j].isDirectory()) {
+                        delFile(delFile[j].getAbsolutePath());//递归调用del方法并取得子目录路径
+                    }
+                    delFile[j].delete();//删除文件
+                }
+            }
+            f.delete();
+        }
+    }
+
+    public static void delFileList(List<String> filePaths) {
+
+        for (String filePath : filePaths) {
+            delFile(filePath);
+        }
+    }
+
+    public static List<String> splitPath(String filePath) {
+
+        List<String> pathList = new ArrayList<>();
+        if (filePath.contains(FileConstant.FILE_SEPARATORCHAR)) {
+            String[] arrPath = StringUtils.split(filePath, FileConstant.FILE_SEPARATORCHAR);
+            StringBuilder sbPath = new StringBuilder();
+            for (int i = 0; i < arrPath.length - 1; i++) {
+                sbPath.append(FileConstant.FILE_SEPARATORCHAR).append(arrPath[i]);
+                pathList.add(sbPath.toString());
+            }
+
+        }
+
+        return pathList;
+    }
+
+    public static void main(String[] args) throws Exception {
+
+    }
+
+    public static String getParentPath(String filePath) {
+
+        if (StringUtils.lastIndexOf(filePath, SLASH_ONE) == 0) {
+            return SLASH_ONE;
+        } else {
+            String path = StringUtils.substring(filePath, 0,
+                    StringUtils.lastIndexOf(filePath, SLASH_ONE));
+            return path;
+        }
+    }
+
+    /**
+     * 获取路径下的所有文件/文件夹
+     *
+     * @param directoryPath 需要遍历的文件夹路径
+     * @param isAddDirectory 是否将子文件夹的路径也添加到list集合中
+     */
+    public static List<String> getAllFile(String directoryPath, boolean isAddDirectory) {
+
+        List<String> list = new ArrayList<String>();
+        File baseFile = new File(directoryPath);
+        if (baseFile.isFile() || !baseFile.exists()) {
+            return list;
+        }
+        File[] files = baseFile.listFiles();
+        for (File file : files) {
+            if (file.isDirectory()) {
+                if (isAddDirectory) {
+                    list.add(file.getAbsolutePath());
+                }
+                list.addAll(getAllFile(file.getAbsolutePath(), isAddDirectory));
+            } else {
+                list.add(file.getAbsolutePath());
+            }
+        }
+        return list;
+    }
+
+
+
+    public static void downloadFile(String name, String path, HttpServletRequest request,
+                                    HttpServletResponse response) throws FileNotFoundException {
+        File downloadFile = new File(path);
+        String fileName = name;
+        if (StringUtils.isBlank(fileName)) {
+            fileName = downloadFile.getName();
+        }
+        String headerValue = String.format("attachment; filename=\"%s\"", fileName);
+        response.addHeader(HttpHeaders.CONTENT_DISPOSITION, headerValue);
+        response.addHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
+        //获取文件大小
+        long downloadSize = downloadFile.length();
+        long fromPos = 0, toPos = 0;
+        if (request.getHeader("Range") == null) {
+            response.addHeader(HttpHeaders.CONTENT_LENGTH, downloadSize + "");
+        } else {
+            log.info("range:{}", response.getHeader("Range"));
+            //如果为持续下载
+            response.setStatus(HttpStatus.PARTIAL_CONTENT.value());
+            String range = request.getHeader("Range");
+            String bytes = range.replaceAll("bytes=", "");
+            String[] ary = bytes.split("-");
+            fromPos = Long.parseLong(ary[0]);
+            log.info("fronPos:{}", fromPos);
+            if (ary.length == 2) {
+                toPos = Long.parseLong(ary[1]);
+            }
+            int size;
+            if (toPos > fromPos) {
+                size = (int) (toPos - fromPos);
+            } else {
+                size = (int) (downloadSize - fromPos);
+            }
+            response.addHeader(HttpHeaders.CONTENT_LENGTH, size + "");
+            downloadSize = size;
+        }
+
+        try (RandomAccessFile in = new RandomAccessFile(downloadFile, "rw");
+             OutputStream out = response.getOutputStream()) {
+            if (fromPos > 0) {
+                in.seek(fromPos);
+            }
+            int bufLen = (int) (downloadSize < 2048 ? downloadSize : 2048);
+            byte[] buffer = new byte[bufLen];
+            int num;
+            //当前写入客户端大小
+            int count = 0;
+            while ((num = in.read(buffer)) != -1) {
+                out.write(buffer, 0, num);
+                count += num;
+                if (downloadSize - count < bufLen) {
+                    bufLen = (int) (downloadSize - count);
+                    if (bufLen == 0) {
+                        break;
+                    }
+                    buffer = new byte[bufLen];
+                }
+            }
+            response.flushBuffer();
+        } catch (IOException e) {
+            log.error("download error:" + e.getMessage(), e);
+            throw new FileDownloadException();
+        }
+    }
+
+
+    /**
+     * 不确定是否能准确,待测试 判断文本文件的字符集,文件开头三个字节表明编码格式。 <a href="http://blog.163.com/wf_shunqiziran/blog/static/176307209201258102217810/">参考的博客地址</a>
+     */
+    public static String charset(String path) {
+
+        String charset = "GBK";
+        byte[] first3Bytes = new byte[3];
+        try {
+            boolean checked = false;
+            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
+            bis.mark(0); // 读者注: bis.mark(0);修改为 bis.mark(100);我用过这段代码,需要修改上面标出的地方。
+            // Wagsn注:不过暂时使用正常,遂不改之
+            int read = bis.read(first3Bytes, 0, 3);
+            if (read == -1) {
+                bis.close();
+                return charset; // 文件编码为 ANSI
+            } else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
+                charset = "UTF-16LE"; // 文件编码为 Unicode
+                checked = true;
+            } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
+                charset = "UTF-16BE"; // 文件编码为 Unicode big endian
+                checked = true;
+            } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
+                    && first3Bytes[2] == (byte) 0xBF) {
+                charset = "UTF-8"; // 文件编码为 UTF-8
+                checked = true;
+            }
+            bis.reset();
+            if (!checked) {
+                while ((read = bis.read()) != -1) {
+                    if (read >= 0xF0) {
+                        break;
+                    }
+                    if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK
+                    {
+                        break;
+                    }
+                    if (0xC0 <= read && read <= 0xDF) {
+                        read = bis.read();
+                        if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
+                        // (0x80 - 0xBF),也可能在GB编码内
+                        {
+                            continue;
+                        } else {
+                            break;
+                        }
+                    } else if (0xE0 <= read && read <= 0xEF) { // 也有可能出错,但是几率较小
+                        read = bis.read();
+                        if (0x80 <= read && read <= 0xBF) {
+                            read = bis.read();
+                            if (0x80 <= read && read <= 0xBF) {
+                                charset = "UTF-8";
+                                break;
+                            } else {
+                                break;
+                            }
+                        } else {
+                            break;
+                        }
+                    }
+                }
+            }
+            bis.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return charset;
+    }
+
+    public static boolean isBase64(String str) {
+
+        String base64Pattern = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$";
+        return Pattern.matches(base64Pattern, str);
+    }
+
+    @Value("${upload.window.root}")
+    public void setUploadWindowRoot(String windowRoot) {
+        uploadWindowRoot = windowRoot;
+    }
+
+    public static void close(final Closeable closeable){
+        if(closeable != null){
+            try {
+                closeable.close();
+            } catch (IOException e) {
+                log.error("close fail:"+e.getMessage(),e);
+            } finally {
+            }
+        }
+    }
+
+    /**
+     * 在MappedByteBuffer释放后再对它进行读操作的话就会引发jvm crash,在并发情况下很容易发生 正在释放时另一个线程正开始读取,于是crash就发生了。所以为了系统稳定性释放前一般需要检 查是否还有线程在读或写
+     */
+    public static void freedMappedByteBuffer(final MappedByteBuffer mappedByteBuffer) {
+
+        try {
+            if (mappedByteBuffer == null) {
+                return;
+            }
+
+            mappedByteBuffer.force();
+            AccessController.doPrivileged(new PrivilegedAction<Object>() {
+                @Override
+                public Object run() {
+
+                    try {
+                        Method getCleanerMethod = mappedByteBuffer.getClass().getMethod("cleaner", new Class[0]);
+                        getCleanerMethod.setAccessible(true);
+                        Cleaner cleaner = (Cleaner) getCleanerMethod.invoke(mappedByteBuffer,
+                                new Object[0]);
+                        cleaner.clean();
+                    } catch (Exception e) {
+                        log.error("clean MappedByteBuffer error!!!", e);
+                    }
+                    log.info("clean MappedByteBuffer completed!!!");
+                    return null;
+                }
+            });
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void clean(final Object buffer) {
+
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+
+                try {
+                    Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
+                    getCleanerMethod.setAccessible(true);
+                    Cleaner cleaner = (Cleaner) getCleanerMethod.invoke(buffer, new Object[0]);
+                    cleaner.clean();
+                } catch (Exception e) {
+                    log.error("clean fail :" + e.getMessage(), e);
+                }
+                return null;
+            }
+        });
+
+    }
+
+    public static void close(FileInputStream in, MappedByteBuffer byteBuffer) {
+
+        if (null != in) {
+            try {
+                in.getChannel().close();
+                in.close();
+            } catch (IOException e) {
+                log.error("close error:"+e.getMessage(), e);
+            }
+        }
+        if (null != byteBuffer) {
+            freedMappedByteBuffer(byteBuffer);
+        }
+    }
+}

+ 670 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/RedisUtil.java

@@ -0,0 +1,670 @@
+package com.mooctest.crowd.site.util.file;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.Cursor;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ScanOptions;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+@Component
+public final class RedisUtil {
+
+    private static Logger logger = LoggerFactory.getLogger(RedisUtil.class);
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplateMap;
+//    private static RedisTemplate redisTemplateMap;
+
+    // =============================common============================
+    /**
+     * 指定缓存失效时间
+     * @param key 键
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0) {
+                redisTemplateMap.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("expire fail:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 根据key 获取过期时间
+     * @param key 键 不能为null
+     * @return 时间(秒) 返回0代表为永久有效
+     */
+    public long getExpire(String key) {
+        return redisTemplateMap.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断key是否存在
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public boolean hasKey(String key) {
+        try {
+            return redisTemplateMap.hasKey(key);
+        } catch (Exception e) {
+            logger.error("hasKey fail:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     * @param key 可以传一个值 或多个
+     */
+    @SuppressWarnings("unchecked")
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplateMap.delete(key[0]);
+            } else {
+                redisTemplateMap.delete(CollectionUtils.arrayToList(key));
+            }
+        }
+    }
+
+    // ============================String=============================
+    /**
+     * 普通缓存获取
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplateMap.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     * @param key 键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public boolean set(String key, Object value) {
+        try {
+            redisTemplateMap.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            logger.error("set fail:"+e.getMessage(),e);
+            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) {
+                redisTemplateMap.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("set fail:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     * @param key 键
+     * @param value 值
+     * @param time 时间 time要大于0 如果time小于等于0 将设置无限期
+     * @param  timeUnit 时间类型
+     * @return true成功 false 失败
+     */
+    public boolean set(String key, Object value, long time,TimeUnit timeUnit) {
+        try {
+            if (time > 0) {
+                redisTemplateMap.opsForValue().set(key, value, time, timeUnit);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("set fail:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+
+    /**
+     * 普通缓存异步放入并设置时间
+     * @param key 键
+     * @param value 值
+     * @param time 时间 time要大于0 如果time小于等于0 将设置无限期
+     * @param  timeUnit 时间类型
+     * @return true成功 false 失败
+     */
+
+    public boolean setAsync(String key, Object value, long time,TimeUnit timeUnit) {
+        SimpleAsyncExec simpleAsyncExec = SimpleAsyncExec.getInstance();
+        Boolean flag =  simpleAsyncExec.exec(new Callable() {
+            @Override
+            public Boolean call() throws Exception {
+                try {
+                    if (time > 0) {
+                        redisTemplateMap.opsForValue().set(key, value, time, timeUnit);
+                    } else {
+                        set(key, value);
+                    }
+                    return true;
+
+                } catch (Exception e) {
+                    logger.error("set setAsync:"+e.getMessage(),e);
+                    return false;
+                }
+            }
+        });
+
+        return flag;
+
+    }
+
+    /**
+     * 递增
+     * @param key 键
+     * @param delta 要增加几(大于0)
+     * @return
+     */
+    public long incr(String key, long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplateMap.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 redisTemplateMap.opsForValue().increment(key, -delta);
+    }
+
+    // ================================Map=================================
+    /**
+     * HashGet
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplateMap.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取hashKey对应的所有键值
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplateMap.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplateMap.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            logger.error("set hmset:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     * @param key 键
+     * @param map 对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public boolean hmset(String key, Map<String, Object> map, long time) {
+        try {
+            redisTemplateMap.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("set hmset:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplateMap.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            logger.error("set hmset:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+
+
+    /**
+     * 向一张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 {
+            redisTemplateMap.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("set hmset:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     * @param key 键
+     * @param item 项
+     * @param value 值
+     * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public boolean hset(String key, String item, Object value, long time,TimeUnit timeUnit) {
+        try {
+            redisTemplateMap.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("set hmset:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     * @param key 键 不能为null
+     * @param item 项 可以使多个 不能为null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplateMap.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     * @param key 键 不能为null
+     * @param item 项 不能为null
+     * @return true 存在 false不存在
+     */
+    public boolean hHasKey(String key, String item) {
+        return redisTemplateMap.opsForHash().hasKey(key, item);
+    }
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     * @param key 键
+     * @param item 项
+     * @param by 要增加几(大于0)
+     * @return
+     */
+    public double hincr(String key, String item, double by) {
+        return redisTemplateMap.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     * @param key 键
+     * @param item 项
+     * @param by 要减少记(小于0)
+     * @return
+     */
+    public double hdecr(String key, String item, double by) {
+        return redisTemplateMap.opsForHash().increment(key, item, -by);
+    }
+
+    // ============================set=============================
+    /**
+     * 根据key获取Set中的所有值
+     * @param key 键
+     * @return
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplateMap.opsForSet().members(key);
+        } catch (Exception e) {
+            logger.error("set sGet:"+e.getMessage(),e);
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     * @param key 键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplateMap.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            logger.error("set sHasKey:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSet(String key, Object... values) {
+        try {
+            return redisTemplateMap.opsForSet().add(key, values);
+        } catch (Exception e) {
+            logger.error("set sSet:"+e.getMessage(),e);
+            return 0;
+        }
+    }
+
+    /**
+     * 将set数据放入缓存
+     * @param key 键
+     * @param time 时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public long sSetAndTime(String key, long time, Object... values) {
+        try {
+            Long count = redisTemplateMap.opsForSet().add(key, values);
+            if (time > 0)
+            expire(key, time);
+            return count;
+        } catch (Exception e) {
+            logger.error("set sSetAndTime:"+e.getMessage(),e);
+            return 0;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     * @param key 键
+     * @return
+     */
+    public long sGetSetSize(String key) {
+        try {
+            return redisTemplateMap.opsForSet().size(key);
+        } catch (Exception e) {
+            logger.error("set sGetSetSize:"+e.getMessage(),e);
+            return 0;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     * @param key 键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public long setRemove(String key, Object... values) {
+        try {
+            Long count = redisTemplateMap.opsForSet().remove(key, values);
+            return count;
+        } catch (Exception e) {
+            logger.error("set setRemove:"+e.getMessage(),e);
+            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 redisTemplateMap.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            logger.error("set lGet:"+e.getMessage(),e);
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     * @param key 键
+     * @return
+     */
+    public long lGetListSize(String key) {
+        try {
+            return redisTemplateMap.opsForList().size(key);
+        } catch (Exception e) {
+            logger.error("set lGetListSize:"+e.getMessage(),e);
+            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 redisTemplateMap.opsForList().index(key, index);
+        } catch (Exception e) {
+            logger.error("set lGetIndex:"+e.getMessage(),e);
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, Object value) {
+        try {
+            redisTemplateMap.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            logger.error("set lSet:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            redisTemplateMap.opsForList().rightPush(key, value);
+            if (time > 0)
+            expire(key, time);
+            return true;
+        } catch (Exception e) {
+            logger.error("set lSet:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     * @param key 键
+     * @param value 值
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplateMap.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            logger.error("set lSet:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key 键
+     * @param value 值
+     * @param time 时间(秒)
+     * @return
+     */
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            redisTemplateMap.opsForList().rightPushAll(key, value);
+            if (time > 0)
+            expire(key, time);
+            return true;
+        } catch (Exception e) {
+            logger.error("set lSet:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     * @param key 键
+     * @param index 索引
+     * @param value 值
+     * @return
+     */
+    public boolean lUpdateIndex(String key, long index, Object value) {
+        try {
+            redisTemplateMap.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            logger.error("set lUpdateIndex:"+e.getMessage(),e);
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     * @param key 键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public long lRemove(String key, long count, Object value) {
+        try {
+            Long remove = redisTemplateMap.opsForList().remove(key, count, value);
+            return remove;
+        } catch (Exception e) {
+            logger.error("set lRemove:"+e.getMessage(),e);
+            return 0;
+        }
+    }
+
+    // ===============================模糊匹配key=================================
+
+    /**
+     * scan 实现
+     * @param pattern   表达式
+     * @param consumer  对迭代到的key进行操作
+     */
+    public void scan(String pattern, Consumer<byte[]> consumer) {
+        redisTemplateMap.execute((RedisConnection connection) -> {
+            try (Cursor<byte[]> cursor = connection.scan(
+                ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
+                cursor.forEachRemaining(consumer);
+                return null;
+            } catch (IOException e) {
+                logger.error("scan:"+e.getMessage(),e);
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    /**
+     * 获取符合条件的key
+     * @param pattern   表达式
+     * @return
+     */
+    public List<String> keys(String pattern) {
+        List<String> keys = new ArrayList<>();
+        this.scan(pattern, item -> {
+            //符合条件的key
+            String key = new String(item, StandardCharsets.UTF_8);
+            keys.add(key);
+        });
+        return keys;
+    }
+
+    /**
+     * 模糊删除
+     * @param pattern 表达式,型如:user*
+     */
+    public void delFuzzyKeys(String pattern){
+       List<String> keys = this.keys(pattern);
+       if(!CollectionUtils.isEmpty(keys)){
+           redisTemplateMap.delete(keys);
+       }
+    }
+
+
+}

+ 39 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/SimpleAsyncExec.java

@@ -0,0 +1,39 @@
+package com.mooctest.crowd.site.util.file;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.*;
+
+public class SimpleAsyncExec {
+
+	private final static SimpleAsyncExec inst = new SimpleAsyncExec();
+	private ExecutorService executor = Executors.newFixedThreadPool(5);
+	private static Logger logger = LoggerFactory.getLogger(SimpleAsyncExec.class);
+
+	public static SimpleAsyncExec getInstance() {
+		return inst;
+	}
+
+	public <T> T exec(Callable callable) {
+		T t = null;
+		//logger.info("SimpleAsyncExec exec start。。。");
+		Future<T> future = executor.submit(callable);
+		try {
+			 t = future.get();
+		} catch (InterruptedException e) {
+			logger.error(e.getMessage(),e);
+		} catch (ExecutionException e) {
+			logger.error(e.getMessage(),e);
+		}
+
+		return t;
+	}
+
+
+	public void exec(Runnable run) {
+		//logger.info("SimpleAsyncExec exec start。。。");
+		executor.submit(run);
+	}
+
+}

+ 91 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/SpringContextHolder.java

@@ -0,0 +1,91 @@
+package com.mooctest.crowd.site.util.file;
+
+import org.apache.commons.lang3.Validate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext
+ *
+ * @author linyb1
+ */
+@Component
+public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
+
+  private static ApplicationContext applicationContext = null;
+
+  private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
+
+  /**
+   * 取得存储在静态变量中的ApplicationContext.
+   */
+  public static ApplicationContext getApplicationContext() {
+
+    assertContextInjected();
+    return applicationContext;
+  }
+
+  /**
+   * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+   */
+  @SuppressWarnings("unchecked")
+  public static <T> T getBean(String name) {
+
+    assertContextInjected();
+    return (T) applicationContext.getBean(name);
+  }
+
+  /**
+   * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+   */
+  public static <T> T getBean(Class<T> requiredType) {
+
+    assertContextInjected();
+    return applicationContext.getBean(requiredType);
+  }
+
+  /**
+   * 清除SpringContextHolder中的ApplicationContext为Null.
+   */
+  public static void clearHolder() {
+
+    logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
+    applicationContext = null;
+  }
+
+  /**
+   * 实现ApplicationContextAware接口, 注入Context到静态变量中.
+   */
+  public void setApplicationContext(ApplicationContext applicationContext) {
+    // logger.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);
+
+    if (SpringContextHolder.applicationContext != null) {
+      logger.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:"
+          + SpringContextHolder.applicationContext);
+    }
+
+    SpringContextHolder.applicationContext = applicationContext; // NOSONAR
+  }
+
+  /**
+   * 实现DisposableBean接口, 在Context关闭时清理静态变量.
+   */
+  public void destroy() throws Exception {
+
+    SpringContextHolder.clearHolder();
+  }
+
+  /**
+   * 检查ApplicationContext不为空.
+   */
+  private static void assertContextInjected() {
+
+    Validate.validState(applicationContext != null,
+        "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
+  }
+
+}

+ 27 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/SystemUtil.java

@@ -0,0 +1,27 @@
+package com.mooctest.crowd.site.util.file;
+
+public class SystemUtil {
+
+  /**
+   * 判断是否为window系统
+   * @return
+   */
+  public static boolean isWinOs(){
+    String os = System.getProperty("os.name");
+    if(os.toLowerCase().startsWith("win")){
+       return true;
+    }
+
+    return false;
+
+  }
+
+  /**
+   * 获取用户当前工作目录
+   * @return
+   */
+  public static String getUserCurrentDir(){
+     return System.getProperty("user.dir");
+  }
+
+}

+ 67 - 0
site/src/main/java/com/mooctest/crowd/site/util/file/YmlUtil.java

@@ -0,0 +1,67 @@
+package com.mooctest.crowd.site.util.file;
+
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class YmlUtil {
+
+    /**
+     * key:文件名索引
+     * value:配置文件内容
+     */
+    private static Map<String, LinkedHashMap> ymls = new HashMap<>();
+
+    /**
+     * string:当前线程需要查询的文件名
+     */
+    private static ThreadLocal<String> nowFileName = new ThreadLocal<>();
+
+    static {
+        loadYml("application.yml");
+    }
+
+    /**
+     * 加载配置文件
+     * @param fileName
+     */
+    public static void loadYml(String fileName) {
+        nowFileName.set(fileName);
+        if (!ymls.containsKey(fileName)) {
+            ymls.put(fileName, new Yaml().loadAs(YmlUtil.class.getResourceAsStream("/" + fileName), LinkedHashMap.class));
+        }
+    }
+
+    public static Object getValue(String key) {
+        // 首先将key进行拆分
+        String[] keys = key.split("[.]");
+
+        // 将配置文件进行复制
+        Map ymlInfo = (Map) ymls.get(nowFileName.get()).clone();
+        for (int i = 0; i < keys.length; i++) {
+            Object value = ymlInfo.get(keys[i]);
+            if (i < keys.length - 1) {
+                ymlInfo = (Map) value;
+            } else if (value == null) {
+                throw new RuntimeException("key is no found");
+            } else {
+                return value;
+            }
+        }
+
+        return null;
+    }
+
+    public static Object getValue(String fileName, String key) {
+        // 首先加载配置文件
+        loadYml(fileName);
+        return getValue(key);
+    }
+
+
+    public static void main(String[] args)  {
+        System.out.println(getValue("hello.desc"));
+    }
+}

+ 83 - 41
site/src/main/resources/application.yml

@@ -28,6 +28,7 @@ oss:
   accessKeySecret: yroxrpm46DzTyzHrLBZzS3MRNIicP6
   endPoint: oss-cn-hangzhou.aliyuncs.com
   bucketName: mooctest-crowd-service
+  idleConnectionTime: 1000
 
 cache:
   expire:
@@ -41,6 +42,14 @@ green:
   agency:
     id: 99231
 
+square:
+  index:
+    count: 6
+  task:
+    count: 6
+  project:
+    count: 6
+
 private:
   cloud:
     # 导入私有云的项目code以此开头
@@ -51,6 +60,60 @@ private:
     publish:
       project:
         id: 20471
+
+upload:
+  chunkSize: 5 #单位为M
+  chunkMaxPartCount: 10000 #文件上传大小上限为:chunkSize * chunkMaxPartCount
+  thread:
+    maxSize: 25
+  queue:
+    maxSize: 100
+  root:
+    dir: /Users/guochao/Desktop/project/data/cofortest/
+  window: #window系统指定盘符
+    root: D:/
+
+
+---
+spring:
+  profiles: dev-online
+  datasource:
+    url: jdbc:mysql://101.37.175.111:3306/crowd-test-service-online?useSSL=false&useUnicode=yes&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
+    username: mooctest
+    password: secr3t!
+  redis:
+    host: 59.42.10.53
+    port: 6379
+    password: '#2019@callforttest@!'
+    jedis:
+      pool:
+        max-active: 8
+        max-idle: 8
+        max-wait: -1
+        min-idle: 0
+    database: 6
+user:
+  service:
+    baseUrl: http://59.42.10.53:8081
+
+#file:
+#  save:
+#    path: /var/www/
+
+file:
+  save:
+    path: /Users/guochao/Desktop/project/data/cofortest/
+
+#feature:
+#  client:
+#    oss: true
+feature:
+  client:
+    oss: false
+
+
+website:
+  domain: mooctest.net
 ---
 spring:
   profiles: private-cloud
@@ -92,6 +155,16 @@ user:
   service:
     baseUrl: http://crowd_user:8081
 #    baseUrl: http://59.42.10.53:8081
+private:
+  cloud:
+    # 导入私有云的项目code以此开头
+    master: MASTER
+    task:
+      type:
+        code: MzZC
+    publish:
+      project:
+        id: 4
 
 ---
 spring:
@@ -183,6 +256,16 @@ user:
 website:
   domain: 127.0.0.1
 
+private:
+  cloud:
+    # 导入私有云的项目code以此开头
+    master: MASTER
+    task:
+      type:
+        code: MzZC
+    publish:
+      project:
+        id: 4
 ---
 spring:
   profiles: dev-localhost
@@ -282,47 +365,6 @@ website:
 
 ---
 spring:
-  profiles: dev-online
-  datasource:
-    url: jdbc:mysql://101.37.175.111:3306/crowd-test-service-online?useSSL=false&useUnicode=yes&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
-    username: mooctest
-    password: secr3t!
-  redis:
-    host: 59.42.10.53
-    port: 6379
-    password: '#2019@callforttest@!'
-    jedis:
-      pool:
-        max-active: 8
-        max-idle: 8
-        max-wait: -1
-        min-idle: 0
-    database: 6
-user:
-  service:
-    baseUrl: http://59.42.10.53:8081
-
-#file:
-#  save:
-#    path: /var/www/
-
-file:
-  save:
-    path: /Users/guochao/Desktop/project/data/cofortest/
-
-#feature:
-#  client:
-#    oss: true
-feature:
-  client:
-    oss: false
-
-
-website:
-  domain: mooctest.net
-
----
-spring:
   profiles: pre-online
   datasource:
     url: jdbc:mysql://10.18.18.50:3306/crowd-test-service?useSSL=false&useUnicode=yes&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai