Forráskód Böngészése

Merge branch 'withConferTest-noPassword' into dev

# Conflicts:
#	pom.xml
#	src/main/resources/bootstrap.yml
insomniaLee 4 éve
szülő
commit
888bd018d6

+ 5 - 3
Dockerfile

@@ -1,10 +1,12 @@
 FROM openjdk:8-jdk-alpine
-ADD ./deploy.sh /project/
-RUN chmod 777 /project/deploy.sh
+#ADD ./deploy.sh /project/
+#RUN chmod 777 /project/deploy.sh
 #RUN wget -c -P /project/ http://third-part-tool.oss-cn-shanghai.aliyuncs.com/sgns.wiki.word.zip#RUN unzip -o /project/sgns.wiki.word.zip -d /project/
 #ADD ./sgns.wiki.word /project/sgns.wiki.word
 ADD ./target/crowd_review-1.0-SNAPSHOT.jar /project/crowd_review.jar
 EXPOSE 9004
 #RUN unzip -o /project/sgns.wiki.word.zip -d /project/
 #ENTRYPOINT ["/project/deploy.sh"]
-ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Xms800m","-Xmx1g","-jar","-Dspring.profiles.active=test","/project/crowd_review.jar"]
+# ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Xms800m","-Xmx1g","-jar","-Dspring.profiles.active=test","/project/crowd_review.jar"]
+
+

+ 19 - 0
pom.xml

@@ -174,6 +174,25 @@
             <artifactId>spring-cloud-starter-zookeeper-config</artifactId>
         </dependency>
 
+        <!-- Aliyun OSS -->
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.10.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-sts</artifactId>
+            <version>2.1.6</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>2.1.7</version>
+        </dependency>
+        <!-- OSS END -->
+
+
     </dependencies>
 
     <dependencyManagement>

+ 19 - 0
src/main/java/com/mooctest/config/MongoOneConfig.java

@@ -2,6 +2,8 @@ package com.mooctest.config;
 
 import com.mongodb.MongoClient;
 import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
@@ -16,6 +18,9 @@ import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
 import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
 import org.springframework.core.env.Environment;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @Configuration
 @EnableMongoRepositories(
         basePackages = "com.mooctest.dao",
@@ -27,13 +32,27 @@ public class MongoOneConfig extends AbstractMongoConfig {
     private String database;
     @Value("${mongodb1.port}")
     private int port;
+//    @Value("${mongodb1.username}")
+//    private String username;
+//    @Value("${mongodb1.password}")
+//    private String password;
     //Setter methods go here..
     /*
      * 创建MongoDBFactory的方法
      * 两个MongoDB连接共用
      */
+
+
     public MongoDbFactory mongoDbFactory() throws Exception {
+//        List<ServerAddress> seeds = new ArrayList<>();
+//        ServerAddress serverAddress = new ServerAddress(host, port);
+//        seeds.add(serverAddress);
+//        List<MongoCredential> mongoCredentialList = new ArrayList<>();
+//        mongoCredentialList.add(MongoCredential.createCredential(username, database, password.toCharArray()));
+//        return new SimpleMongoDbFactory(new MongoClient(seeds, mongoCredentialList), database);
         return new SimpleMongoDbFactory(new MongoClient(host, port), database);
+
+
     }
 
 

+ 15 - 0
src/main/java/com/mooctest/config/MongoTwoConfig.java

@@ -2,6 +2,8 @@ package com.mooctest.config;
 
 import com.mongodb.MongoClient;
 import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
@@ -16,6 +18,9 @@ import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
 import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
 
+import java.util.ArrayList;
+import java.util.List;
+
 
 @Configuration
 @EnableMongoRepositories(
@@ -29,12 +34,22 @@ public class MongoTwoConfig{
     private String database;
     @Value("${mongodb2.port}")
     private int port;
+//    @Value("${mongodb2.username}")
+//    private String username;
+//    @Value("${mongodb2.password}")
+//    private String password;
     //Setter methods go here..
     /*
      * 创建MongoDBFactory的方法
      * 两个MongoDB连接共用
      */
     public MongoDbFactory mongoDbFactory2() throws Exception {
+//        List<ServerAddress> seeds = new ArrayList<>();
+//        ServerAddress serverAddress = new ServerAddress(host, port);
+//        seeds.add(serverAddress);
+//        List<MongoCredential> mongoCredentialList = new ArrayList<>();
+//        mongoCredentialList.add(MongoCredential.createCredential(username, database, password.toCharArray()));
+//        return new SimpleMongoDbFactory(new MongoClient(seeds, mongoCredentialList), database);
         return new SimpleMongoDbFactory(new MongoClient(host, port), database);
     }
 

+ 45 - 0
src/main/java/com/mooctest/controller/TaskController.java

@@ -3,6 +3,7 @@ package com.mooctest.controller;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.mooctest.data.BugDTO;
+import com.mooctest.data.SimpleResponse;
 import com.mooctest.data.TaskDTO;
 import com.mooctest.model.BugData;
 import com.mooctest.model.MasterReport;
@@ -14,8 +15,10 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.sql.Timestamp;
 import java.util.ArrayList;
@@ -50,6 +53,11 @@ public class TaskController {
     @Value("${report.host}")
     String reportHost;
 
+    @Value("${useOss}")
+    boolean useOss;
+    @Value("${urlPath}")
+    String urlPrefix;
+
     @GetMapping("/crowdTask")
     public String home(Model model) {
         // 获得所有taskDTO,包括本地和慕测端
@@ -165,4 +173,41 @@ public class TaskController {
         JSONObject temp = (JSONObject) input;
         temp.put("create_time",new Timestamp(Long.parseLong(temp.get("create_time").toString()))); // 修改创建时间的格式  以便前段展示
     }
+
+    @GetMapping("/addCrowdTask")
+    public String addCrowdTask(Model model){
+        model.addAttribute("templateUrl",useOss?"http://mooctest-site.oss-cn-shanghai.aliyuncs.com/excel-template.xlsx":urlPrefix+"excel-template.xlsx");
+        return "addCrowdTask";
+    }
+
+    @GetMapping("/importTask")
+    public String importTask(Model model){
+        return "importTask";
+    }
+
+    @PostMapping("/addCrowdTask")
+    @ResponseBody
+    public SimpleResponse addCrowdTask2(@RequestParam("name")String name, @RequestParam("description")String description,
+                                        @RequestParam("time")String time, @RequestParam("type")String type,
+                                        @RequestParam("os")String os, @RequestParam("threePage") MultipartFile threePage,
+                                        Model model){
+        if(true)return  new SimpleResponse(200,"success");
+        return taskService.addCrowdTask(name, description, time, type, os, threePage);
+    }
+
+    @PostMapping("/importTask")
+    @ResponseBody
+    public String importTask2(@RequestParam("number")String number,@RequestParam("number")String originId,
+                              @RequestParam("zipFile") MultipartFile zipFile,@RequestParam("jsonFile") MultipartFile jsonFile){
+        if(true)return  "success";
+
+        return taskService.importTask(number, originId, zipFile, jsonFile)?"success":"导出失败";
+    }
+
+    @PostMapping("/exportTask")
+    @ResponseBody
+    public String exportTask(@RequestParam("caseId") String caseId) {
+
+        return taskService.exportTask(caseId) ? "success" : "导出失败";
+    }
 }

+ 15 - 0
src/main/java/com/mooctest/dao2/CrowdTaskDao.java

@@ -0,0 +1,15 @@
+package com.mooctest.dao2;
+
+import com.mooctest.model.CrowdTask;
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+import java.util.List;
+
+
+public interface CrowdTaskDao extends MongoRepository<CrowdTask, String> {
+
+    CrowdTask findFirstByOrderByIdDesc();
+
+    @Override
+    List<CrowdTask> findAll();
+}

+ 15 - 0
src/main/java/com/mooctest/data/SimpleResponse.java

@@ -0,0 +1,15 @@
+package com.mooctest.data;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@Builder
+@NoArgsConstructor
+public class SimpleResponse {
+    private int status;
+    private String message;
+}

+ 26 - 0
src/main/java/com/mooctest/model/CrowdTask.java

@@ -0,0 +1,26 @@
+package com.mooctest.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Document(collection = "exam")
+public class CrowdTask {
+    @Id
+    private String id;
+
+    private String json;
+
+    private String app_name;
+
+    private String paper_type;
+
+    private String test_type;
+
+    private String description;
+}

+ 10 - 0
src/main/java/com/mooctest/service/FileService.java

@@ -0,0 +1,10 @@
+package com.mooctest.service;
+
+import org.springframework.web.multipart.MultipartFile;
+
+public interface FileService {
+
+    //上传一个file 返回一个String
+    public String uploadFile(MultipartFile file);
+    public String uploadJson(String content,long taskId);
+}

+ 146 - 8
src/main/java/com/mooctest/service/TaskService.java

@@ -3,31 +3,35 @@ package com.mooctest.service;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.mongodb.MongoClient;
-import com.mongodb.client.FindIterable;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoCursor;
-import com.mongodb.client.MongoDatabase;
 import com.mooctest.dao.MasterReportDao;
 import com.mooctest.dao.TaskDao;
+import com.mooctest.dao2.CrowdTaskDao;
+import com.mooctest.data.SimpleResponse;
 import com.mooctest.data.TaskDTO;
+import com.mooctest.model.CrowdTask;
 import com.mooctest.model.Task;
 import com.mooctest.util.EncodeUtil;
+import com.mooctest.util.TaskUtil;
 import com.mooctest.util.TimeUtil;
 import org.bson.Document;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.ResponseEntity;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.http.*;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.StringHttpMessageConverter;
 import org.springframework.stereotype.Service;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
@@ -54,11 +58,17 @@ public class TaskService {
     TaskDao taskDao;
 
     @Autowired
+    CrowdTaskDao crowdTaskDao;
+
+    @Autowired
     BugReportService bugReportService;
 
     @Autowired
     BugDataService bugDataService;
 
+    @Autowired
+    FileService fileService;
+
     @Value("${task.info.addr}")
     String taskInfoAddr;
 
@@ -251,6 +261,49 @@ public class TaskService {
         return res;
     }
 
+    public String addCrowdTaskPure(String name,String description,String time,String type,MultipartFile threePage){
+        if(threePage ==null || threePage.isEmpty()) return "请提交三级页面";
+        String threePageUrl = fileService.uploadFile(threePage);
+        try {
+            long flag = createCrowdTest(threePageUrl,threePage.getOriginalFilename(),"",getMaxCaseId()+1,type,description,name);
+            return flag!=-1 ?""+flag:"创建失败";
+        } catch (Exception e) {
+            e.printStackTrace();
+            return "创建众测失败";
+        }
+    }
+
+    public SimpleResponse addCrowdTask(String name, String description, String time, String type, String os, MultipartFile threePage){
+        if(threePage ==null || threePage.isEmpty()) return new SimpleResponse(400,"请提交三级页面");
+        String threePageUrl = fileService.uploadFile(threePage);
+        try {
+            long flag = createCrowdTest(threePageUrl,threePage.getOriginalFilename(),generatePaperType(type,os),getMaxCaseId()+1,type,description,name);
+            return flag!=-1 ?new SimpleResponse(200,"创建成功,id为"+flag):new SimpleResponse(400,"创建失败");
+        } catch (Exception e) {
+            e.printStackTrace();
+            return new SimpleResponse(400,"创建众测失败");
+        }
+    }
+
+    public long createCrowdTest(String file, String fileName, String paperType, Long caseId, String testType, String description, String appName) throws Exception {
+        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
+        params.add("file", file);
+        params.add("file_name", fileName);
+        params.add("paper_type", paperType);
+        params.add("case_id", caseId + "");
+        params.add("test_type", testType);
+        params.add("description", description);
+        params.add("app_name", appName);
+        RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
+        ResponseEntity<String> responseEntity = restTemplate.postForEntity
+                ("http://" + reportHost  + "/Bug/api/extra/uploadExamUrl", params, String.class);
+        if (responseEntity.getStatusCode().equals(HttpStatus.OK)){
+            fileService.uploadJson(responseEntity.getBody(),caseId);
+            return caseId;
+        }else return -1;
+    }
+
     public String getEncodeTaskReportUrl(long examId,long caseId){
         String url = HTTP + reportHost + "/report/detail/" + examId +"/" +caseId +"/userId";
         String encodedUrl = EncodeUtil.strConvertBase(url);
@@ -277,4 +330,89 @@ public class TaskService {
         taskDTO.setNumOfUndeal(undealBugs);
         return taskDTO;
     }
+
+    private long getMaxCaseId(){
+        List<CrowdTask> list = crowdTaskDao.findAll();
+        long max =0;
+        long temp =0;
+        for(CrowdTask task : list){
+            temp=Long.parseLong(task.getId());
+            max=temp>max?temp:max;
+        }
+//        CrowdTask task = crowdTaskDao.findFirstByOrderByIdDesc();
+        return max;
+    }
+
+    public boolean exportTask(String caseId){
+        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
+        params.add("caseId", caseId);
+        RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
+        ResponseEntity<String> responseEntity = restTemplate.postForEntity
+                ("http://" + reportHost  + "/Bug/api/data/outputByCaseId", params, String.class);
+        return responseEntity.getStatusCode().equals(HttpStatus.OK);
+    }
+
+    public boolean importTask(String number,String originId,
+                                  MultipartFile zipFile, MultipartFile jsonFile)  {
+
+        HttpHeaders headers = new HttpHeaders();
+        MediaType type = MediaType.parseMediaType("multipart/form-data");
+        // 设置请求的格式类型
+        headers.setContentType(type);
+        ByteArrayResource zipFileResource = null;
+        ByteArrayResource jsonFileResource  = null;
+        try {
+            zipFileResource= new ByteArrayResource(zipFile.getBytes()) {
+                @Override
+                public String getFilename() {
+                    return zipFile.getOriginalFilename();
+                }
+            };
+            jsonFileResource= new ByteArrayResource(jsonFile.getBytes()) {
+                @Override
+                public String getFilename() {
+                    return jsonFile.getOriginalFilename();
+                }
+            };
+        }catch (IOException e){
+            return false;
+        }
+        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
+        params.add("sourceZipFile", zipFileResource);
+        params.add("sourceJsonFile", jsonFileResource);
+        params.add("originalCaseId", originId);
+        params.add("cpSerialNum", number);
+        HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(params, headers);
+        RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
+        ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://" + reportHost  + "/Bug/api/data/inputFromFile", files, String.class);
+        return responseEntity.getStatusCode().equals(HttpStatus.OK);
+    }
+
+
+    private static String generatePaperType(String testType ,  String os){
+        String defaultValue = "\"windows\",\"linux\",\"macos\"";
+        StringBuffer value  = new StringBuffer();
+        if(os==null||os.length()==0){
+            if(testType.equals(TaskUtil.TaskType.MOBILE.getTaskType())){
+                value.append("\"Android\",\"ios\"");
+            }else{
+                value.append("\"windows\",\"linux\",\"macos\"");
+            }
+
+        }else{
+            String [] data =  os.split(";");
+            for(int i  =0;i<data.length;i++){
+                if(i!=data.length-1){
+                    value.append("\""+data[i]+"\",");
+                }else{
+                    value.append("\""+data[i]+"\"");
+                }
+            }
+        }
+        return  "{\"title\":\"测试报告名称\",\"subTitles\":[{\"name\":\"设备名称\", \"type\":\"text\"},{\"name\":\"设备品牌\",\"type\":\"text\"},{\"name\":\"操作系统\",\"type\":\"enum\",\"value\":["
+                +value.toString()
+                + "]}],\"caseList\":true,\"bugList\": true,\"testScript\":false,\"suppleReport\":false,\"testLog\":false}";
+    }
 }

+ 64 - 0
src/main/java/com/mooctest/service/impl/NginxFileService.java

@@ -0,0 +1,64 @@
+package com.mooctest.service.impl;
+
+import com.mooctest.service.FileService;
+import org.apache.commons.io.IOUtils;
+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.*;
+
+@Service
+@ConditionalOnExpression("${useOss}==false")
+public class NginxFileService implements FileService {
+
+    @Value("${filePath}")
+    private String filePath;
+    @Value("${urlPath}")
+    private String urlPath;
+
+    @Override
+    public String uploadFile(MultipartFile file) {
+        String fileName = System.currentTimeMillis()+ file.getOriginalFilename();
+        File saveFile = new File(filePath + fileName);
+        //判断文件父目录是否存在
+//        System.out.println(saveFile.getParentFile());
+        if (!saveFile.getParentFile().exists()) {
+            saveFile.getParentFile().mkdirs();
+        }
+        try {
+            file.transferTo(saveFile);
+            return urlPath+fileName;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static void main(String[] args) {
+        NginxFileService n=new NginxFileService();
+        System.out.println(n.uploadJson("adfasfasdf",324));
+    }
+
+    @Override
+    public String uploadJson(String content,long taskId) {
+        String fileName =  taskId+".json";
+        File saveFile = new File(filePath + fileName);
+        //判断文件父目录是否存在
+//        System.out.println(saveFile.getParentFile());
+        if (!saveFile.getParentFile().exists()) {
+            saveFile.getParentFile().mkdirs();
+        }
+        try {
+            Writer write = new OutputStreamWriter(new FileOutputStream(saveFile), "UTF-8");
+            write.write(content);
+            write.flush();
+            write.close();
+            return urlPath+fileName;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

+ 42 - 0
src/main/java/com/mooctest/service/impl/OssFileService.java

@@ -0,0 +1,42 @@
+package com.mooctest.service.impl;
+
+import com.mooctest.service.FileService;
+import com.mooctest.util.OSSClientUtil;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+
+@Service
+@ConditionalOnExpression("${useOss}==true")
+public class OssFileService implements FileService {
+    @Override
+    public String uploadFile(MultipartFile file) {
+        return OSSClientUtil.uploadSingleFile(file);
+    }
+
+    public static void main(String[] args) {
+        OssFileService o = new OssFileService();
+        System.out.println(o.uploadJson("234",234));
+    }
+
+    @Override
+    public String uploadJson(String content, long taskId) {
+        String filePath = System.getProperty("user.dir")+"/";
+        String fileName =  taskId+".json";
+        File saveFile = new File(filePath + fileName);
+        try {
+            Writer write = new OutputStreamWriter(new FileOutputStream(saveFile), "UTF-8");
+            write.write(content);
+            write.flush();
+            write.close();
+            return OSSClientUtil.uploadSingleFile(saveFile);
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+}

+ 381 - 0
src/main/java/com/mooctest/util/OSSClientUtil.java

@@ -0,0 +1,381 @@
+package com.mooctest.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.aliyun.oss.OSSClient;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * @class:AliyunOSSClientUtil
+ * @descript:java使用阿里云OSS存储对象上传图片
+ *
+ */
+public class OSSClientUtil {
+    //log日志
+    private static Logger logger = LoggerFactory.getLogger(OSSClientUtil.class);
+
+
+    //阿里云API的内或外网域名
+    private static String ENDPOINT = "oss-cn-shanghai.aliyuncs.com";
+    //阿里云API的密钥Access Key ID
+    private static String ACCESS_KEY_ID="LTAI4FdrT3HsfdR5edBVN7ws";
+    //阿里云API的密钥Access Key Secret
+    private static String ACCESS_KEY_SECRET="yroxrpm46DzTyzHrLBZzS3MRNIicP6";
+    //阿里云API的bucket名称
+    private static String BACKET_NAME="mooctest-site";
+    //阿里云API的文件夹名称
+    private static String FOLDER="xinchuangJson";
+
+
+
+
+    /**
+     * 获取阿里云OSS客户端对象
+     * @return ossClient
+     */
+    public static  OSSClient getOSSClient(){
+        return new OSSClient(ENDPOINT,ACCESS_KEY_ID, ACCESS_KEY_SECRET);
+    }
+
+    /**
+     * 创建存储空间
+     * @param ossClient      OSS连接
+     * @param bucketName 存储空间
+     * @return
+     */
+    private  static String createBucketName(OSSClient ossClient,String bucketName){
+        //存储空间
+        final String bucketNames=bucketName;
+        if(!ossClient.doesBucketExist(bucketName)){
+            //创建存储空间
+            Bucket bucket=ossClient.createBucket(bucketName);
+            logger.info("创建存储空间成功");
+            return bucket.getName();
+        }
+        return bucketNames;
+    }
+
+    /**
+     * 创建存储空间
+     * @param ossClient      OSS连接
+     * @param bucketName 存储空间
+     * @return
+     */
+    private  static void putBucketACL(OSSClient ossClient,String bucketName){
+        //存储空间
+        final String bucketNames=bucketName;
+        System.out.println(ossClient.doesBucketExist(bucketName));
+        if(ossClient.doesBucketExist(bucketName)){
+            //修改存储空间的权限
+            ossClient.setBucketAcl(bucketNames, CannedAccessControlList.PublicRead);
+            logger.info("创建存储空间成功");
+        }
+    }
+
+    /**
+     * 删除存储空间buckName
+     * @param ossClient  oss对象
+     * @param bucketName  存储空间
+     */
+    private static  void deleteBucket(OSSClient ossClient, String bucketName){
+        ossClient.deleteBucket(bucketName);
+        logger.info("删除" + bucketName + "Bucket成功");
+    }
+
+    /**
+     * 创建模拟文件夹
+     * @param ossClient oss连接
+     * @param bucketName 存储空间
+     * @param folder   模拟文件夹名如"qj_nanjing/"
+     * @return  文件夹名
+     */
+    private  static String createFolder(OSSClient ossClient,String bucketName,String folder){
+        //文件夹名
+        final String keySuffixWithSlash =folder;
+        //判断文件夹是否存在,不存在则创建
+        if(!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)){
+            //创建文件夹
+            ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0]));
+            logger.info("创建文件夹成功");
+            //得到文件夹名
+            OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash);
+            String fileDir=object.getKey();
+            return fileDir;
+        }
+        return keySuffixWithSlash;
+    }
+
+    /**
+     * 根据key删除OSS服务器上的文件
+     * @param ossClient  oss连接
+     * @param bucketName  存储空间
+     * @param folder  模拟文件夹名 如"qj_nanjing/"
+     * @param key Bucket下的文件的路径名+文件名 如:"upload/cake.jpg"
+     */
+    private static void deleteFile(OSSClient ossClient, String bucketName, String folder, String key){
+        ossClient.deleteObject(bucketName, folder + key);
+        logger.info("删除" + bucketName + "下的文件" + folder + key + "成功");
+    }
+    /**
+     * 根据key删除OSS服务器上的文件
+     * @param key Bucket下的文件的路径名+文件名 如:"upload/cake.jpg"
+     */
+    private static void deleteFile(String key){
+        OSSClient ossClient = getOSSClient();
+        ossClient.deleteObject(BACKET_NAME,key);
+        logger.info("删除" + BACKET_NAME + "下的文件" + key + "成功");
+    }
+
+    private static void uploadFile2OSS(){
+        // Endpoint以杭州为例,其它Region请按实际情况填写。
+        String endpoint = ENDPOINT;
+        // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
+        String accessKeyId = ACCESS_KEY_ID;
+        String accessKeySecret = ACCESS_KEY_SECRET;
+
+        // 创建OSSClient实例。
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+
+        // 创建PutObjectRequest对象。
+        PutObjectRequest putObjectRequest = new PutObjectRequest(BACKET_NAME,
+                System.currentTimeMillis()+"",
+                new File("/Users/insomnialee/Pictures/五等分/13.png"));
+
+        // 如果需要上传时设置存储类型与访问权限,请参考以下示例代码。
+        // ObjectMetadata metadata = new ObjectMetadata();
+        // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
+        // metadata.setObjectAcl(CannedAccessControlList.Private);
+        // putObjectRequest.setMetadata(metadata);
+
+        // 上传文件。
+        PutObjectResult res = ossClient.putObject(putObjectRequest);
+
+
+        // 关闭OSSClient。
+        ossClient.shutdown();
+    }
+
+    /**
+     * 上传图片至OSS
+     * @param file 上传文件(文件全路径如:D:\\image\\cake.jpg)
+     * @return String 返回访问路径
+     * */
+    private static  String uploadObject2OSS(File file) {
+        OSSClient ossClient = getOSSClient();
+        String resultStr = null;
+        String fileName = null;
+        try {
+            //以输入流的形式上传文件
+            InputStream is = new FileInputStream(file);
+            //文件名  如果出现重复,则重新生成名字,再上传
+            fileName = file.getName();
+//            if(ossClient.doesObjectExist(BACKET_NAME, FOLDER + fileName)){
+//                fileName = getfileName(file.getName());
+//            }
+            //文件大小
+            Long fileSize = file.length();
+            //创建上传Object的Metadata
+            ObjectMetadata metadata = new ObjectMetadata();
+            //上传的文件的长度
+            metadata.setContentLength(is.available());
+            //指定该Object被下载时的网页的缓存行为
+//            metadata.setCacheControl("no-cache");
+            //指定该Object下设置Header
+//            metadata.setHeader("Pragma", "no-cache");
+            //指定该Object被下载时的内容编码格式
+//            metadata.setContentEncoding("utf-8");
+            //文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,
+            //如果没有扩展名则填默认值application/octet-stream
+//            metadata.setContentType(getContentType(fileName));
+            //指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称)
+            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
+            //上传文件   (上传文件流的形式)
+            PutObjectResult putResult = ossClient.putObject(BACKET_NAME, FOLDER +"/"+ fileName, is, metadata);
+            resultStr = putResult.getETag();
+            logger.info("上传阿里云OSS服务器成功." +resultStr);
+            //解析结果
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
+        }
+        return "http://"+BACKET_NAME+"."+ENDPOINT+"/"+FOLDER +"/"+ fileName;
+    }
+    /**
+     * 上传图片至OSS
+     * @param in 上传文件流
+     * @return String 返回访问路径,图片存储KEY
+     * */
+    private static  String uploadObject2OSS(InputStream in,String name,Long size) {
+        OSSClient ossClient = getOSSClient();
+        String resultStr = null;
+        String fileName = null;
+        try {
+            //文件名  如果出现重复,则重新生成名字,再上传
+            fileName = getfileName(name);
+            if(ossClient.doesObjectExist(BACKET_NAME, FOLDER + fileName)){
+                fileName = getfileName(name);
+            }
+            //文件大小
+            Long fileSize = size;
+            //创建上传Object的Metadata
+            ObjectMetadata metadata = new ObjectMetadata();
+            //上传的文件的长度
+            metadata.setContentLength(in.available());
+            //指定该Object被下载时的网页的缓存行为
+            metadata.setCacheControl("no-cache");
+            //指定该Object下设置Header
+            metadata.setHeader("Pragma", "no-cache");
+            //指定该Object被下载时的内容编码格式
+            metadata.setContentEncoding("utf-8");
+            //文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,
+            //如果没有扩展名则填默认值application/octet-stream
+            metadata.setContentType(getContentType(fileName));
+            //指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称)
+            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
+            //上传文件   (上传文件流的形式)
+            PutObjectResult putResult = ossClient.putObject(BACKET_NAME, FOLDER + fileName, in, metadata);
+            resultStr = putResult.getETag();
+            logger.info("上传阿里云OSS服务器成功." +resultStr);
+            //解析结果
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
+        }
+        return BACKET_NAME+"."+ENDPOINT+"/"+FOLDER + fileName;
+    }
+
+    /**
+     * 通过文件名判断并获取OSS服务文件上传时文件的contentType
+     * @param fileName 文件名
+     * @return 文件的contentType
+     */
+    private static  String getContentType(String fileName){
+        //文件的后缀名
+        String fileExtension = fileName.substring(fileName.lastIndexOf("."));
+        if(".bmp".equalsIgnoreCase(fileExtension)) {
+            return "image/bmp";
+        }
+        if(".gif".equalsIgnoreCase(fileExtension)) {
+            return "image/gif";
+        }
+        if(".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension)  || ".png".equalsIgnoreCase(fileExtension) ) {
+            return "image/jpeg";
+        }
+        if(".html".equalsIgnoreCase(fileExtension)) {
+            return "text/html";
+        }
+        if(".txt".equalsIgnoreCase(fileExtension)) {
+            return "text/plain";
+        }
+        if(".vsd".equalsIgnoreCase(fileExtension)) {
+            return "application/vnd.visio";
+        }
+        if(".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
+            return "application/vnd.ms-powerpoint";
+        }
+        if(".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
+            return "application/msword";
+        }
+        if(".xml".equalsIgnoreCase(fileExtension)) {
+            return "text/xml";
+        }
+        //默认返回类型
+        return "image/jpeg";
+    }
+
+    /**
+     * 修改文件名
+     * @param fileName 文件名
+     * @return 文件的新名称
+     */
+    private static  String getfileName(String fileName){
+        String fileType = fileName.substring(fileName.lastIndexOf("."), fileName.length());
+        String name = System.currentTimeMillis()+fileType;
+        System.out.println(fileName+"----"+name);
+        return name;
+
+    }
+
+    /**
+     * 上传图片至OSS
+     * @param file 上传文件(文件全路径如:D:\\image\\cake.jpg)
+     * @return String 返回访问路径
+     * */
+    private  static  String uploadMultipartFile2OSS(MultipartFile file) {
+        OSSClient ossClient = getOSSClient();
+        String resultStr = null;
+        String fileName = null;
+        try {
+            //以输入流的形式上传文件
+            InputStream is = file.getInputStream();
+            //文件名  如果出现重复,则重新生成名字,再上传
+            fileName = getfileName(file.getOriginalFilename());
+//            if(ossClient.doesObjectExist(BACKET_NAME, FOLDER + fileName)){
+//                fileName = getfileName(file.getName());
+//            }
+            //文件大小
+            Long fileSize = file.getSize();
+            //创建上传Object的Metadata
+            ObjectMetadata metadata = new ObjectMetadata();
+            //上传的文件的长度
+            metadata.setContentLength(is.available());
+            //指定该Object被下载时的网页的缓存行为
+//            metadata.setCacheControl("no-cache");
+            //指定该Object下设置Header
+//            metadata.setHeader("Pragma", "no-cache");
+            //指定该Object被下载时的内容编码格式
+//            metadata.setContentEncoding("utf-8");
+            //文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,
+            //如果没有扩展名则填默认值application/octet-stream
+//            metadata.setContentType(getContentType(fileName));
+            //指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称)
+            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
+            //上传文件   (上传文件流的形式)
+            PutObjectResult putResult = ossClient.putObject(BACKET_NAME, FOLDER + "/"+ fileName, is, metadata);
+            resultStr = putResult.getETag();
+            logger.info("上传阿里云OSS服务器成功." +resultStr);
+            //解析结果
+        } catch (Exception e) {
+            e.printStackTrace();
+            logger.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
+        }
+        return "http://"+BACKET_NAME+"."+ENDPOINT+"/"+FOLDER + "/" + fileName;
+    }
+
+    public static  String uploadSingleFile(MultipartFile file){
+        OSSClient ossClient=OSSClientUtil.getOSSClient();
+        return uploadMultipartFile2OSS(file);
+    }
+
+    public static  String uploadSingleFile(File file){
+//        OSSClient ossClient=OSSClientUtil.getOSSClient();
+        return uploadObject2OSS(file);
+    }
+
+    //测试
+//    public static void main(String[] args) throws Exception {
+//        uploadFile2OSS();
+//        //初始化OSSClient
+//        OSSClient ossClient=OSSClientUtil.getOSSClient();
+//        //上传文件
+//        String files="/Users/insomnialee/Pictures/五等分/13.png";
+//        String[] file=files.split(",");
+//        for(String filename:file){
+//            //System.out.println("filename:"+filename);
+//            File filess=new File(filename);
+//            String md5key = OSSClientUtil.uploadObject2OSS(filess);
+//            System.out.println("文件地址:" + md5key);
+//        }
+//    }
+
+
+}

+ 25 - 0
src/main/java/com/mooctest/util/TaskUtil.java

@@ -0,0 +1,25 @@
+package com.mooctest.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class TaskUtil {
+
+
+    public enum TaskType {
+
+        MOBILE("移动应用"), // 未审核
+        WEB("Web应用"); // 已审核
+
+        private String taskType;
+
+        private TaskType(String taskType) {
+            this.taskType = taskType;
+        }
+
+        public String getTaskType() {
+            return taskType;
+        }
+    }
+
+}

+ 42 - 14
src/main/resources/application.yml

@@ -1,4 +1,4 @@
-spring.profiles.active: test
+spring.profiles.active: private-cloud
 spring:
   mvc:
     favicon:
@@ -46,18 +46,6 @@ spring:
   thymeleaf:
     cache: false
 
-
-mongodb1.database: ${MONGODB_REVIEW_DB}
-mongodb1.host: ${MONGODB_REVIEW_HOST}
-mongodb1.port: ${MONGODB_REVIEW_PORT}
-
-mongodb2.database: ${MONGODB_REPORT_DB}
-mongodb2.host: ${MONGODB_REPORT_HOST}
-mongodb2.port: ${MONGODB_REPORT_PORT}
-
-report.export.addr: ${EXPORT_ADDR}
-task.info.addr: ${TASK_ADDR}
-
 server:
   port: 9004
 #    port: 8090
@@ -65,4 +53,44 @@ logging:
   level:
     com.mooctest: debug
   pattern:
-    console: "%d{yyyy-MM-dd HH:mm:ss} [%p] [%t] [%c] %m%n"
+    console: "%d{yyyy-MM-dd HH:mm:ss} [%p] [%t] [%c] %m%n"
+---
+spring:
+  profiles: private-cloud
+  thymeleaf:
+    cache: false
+
+task:
+  info:
+    addr: http://47.98.174.59:8100/Bug/api/extra/getExamList
+  distribute:
+    url: test
+server:
+  port: 9004
+report.host: 47.98.174.59:8100
+audit:
+  distribute:
+    addr: test
+mooctest:
+  host: http://101.37.175.111:8191
+report.export.addr: http://47.99.140.117:9002/generateReport
+report.export.excel.addr: http://47.98.174.59:8095/generateReport
+mongodb1.database: crowd_review_enterprise
+mongodb1.host: 47.98.174.59
+mongodb1.port: 27017
+mongodb2.database: co-enterprise
+mongodb2.host: 47.98.174.59
+mongodb2.port: 27017
+useOss: false
+filePath: /xinchuang/data/
+urlPath: http://47.98.174.59:8100/resourcesXinchuang/
+logging:
+  level:
+    com:
+      mooctest: debug
+  pattern:
+    console: "%d{yyyy-MM-dd HH:mm:ss} [%p] [%t] [%c] %m%n"
+
+
+
+---

+ 0 - 1
src/main/resources/bootstrap.yml

@@ -1,5 +1,4 @@
 spring.profiles.active: dev
-
 spring:
   application:
     name: mooctest-crowd-review

+ 261 - 0
src/main/resources/templates/addCrowdTask.html

@@ -0,0 +1,261 @@
+<!DOCTYPE html>
+<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"
+      xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
+      layout:decorator="main">
+<head>
+    <link rel="stylesheet" href="/static/css/applications.css" type="text/css"/>
+    <!-- dataTables -->
+    <link rel="stylesheet" type="text/css"
+          href="/static/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css"/>
+    <!-- Font Awesome -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/font-awesome/css/font-awesome.min.css"/>
+    <!-- Ionicons -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/Ionicons/css/ionicons.min.css"/>
+    <!-- daterange picker -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/bootstrap-daterangepicker/daterangepicker.css"/>
+    <!-- bootstrap datepicker -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css"/>
+    <!-- iCheck for checkboxes and radio inputs -->
+    <link rel="stylesheet" href="/static/AdminLTE/plugins/iCheck/all.css"/>
+    <!-- Bootstrap time Picker -->
+    <link rel="stylesheet" href="/static/AdminLTE/plugins/timepicker/bootstrap-timepicker.min.css"/>
+    <!-- Select2 -->
+    <link rel="stylesheet" type="text/css" href="/static/AdminLTE/bower_components/select2/dist/css/select2.min.css"/>
+    <!-- Theme style -->
+    <link rel="stylesheet" href="/static/AdminLTE/dist/css/AdminLTE.min.css"/>
+    <!-- AdminLTE Skins. Choose a skin from the css/skins
+         folder instead of downloading all of them to reduce the load. -->
+    <link rel="stylesheet" href="/static/AdminLTE/dist/css/skins/_all-skins.min.css"/>
+</head>
+<body>
+<th:block layout:fragment="sidebar">
+    <li th:replace="/home::mainSidebar">
+</th:block>
+
+
+
+
+<th:block layout:fragment="maincontent">
+    <section class="content-header">
+        <h1>
+            新建众测任务
+            <small>众包测试</small>
+        </h1>
+        <ol class="breadcrumb">
+            <li><a href="/home"><i class="fa fa-dashboard"></i> 主页</a></li>
+            <li><a href="/addCrowdTask">新建众测任务</a></li>
+        </ol>
+    </section>
+
+    <!-- Main content -->
+    <section class="content" id="maincontent">
+
+        <div class="box box-primary" >
+            <div class="box-header with-border">
+                <h3 class="box-title">基本信息</h3>
+                <button class="btn btn-success pull-right" onclick="addTask()">
+                    生成
+                </button>
+            </div>
+            <!-- /.box-header -->
+            <div class="box-body">
+                <form class="form-horizontal">
+                    <!-- text input -->
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label">名称</label>
+                        <div class="col-sm-4">
+                            <input  onchange="checkNull(event)" class="form-control" id="name" placeholder="请输入任务名称"/>
+                        </div>
+                    </div>
+                    <!-- textarea -->
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label">描述</label>
+                        <div class="col-sm-6">
+                            <textarea onchange="checkNull(event)" id="description" class="form-control" rows="3" placeholder="请输入描述信息"></textarea>
+                        </div>
+                    </div>
+                    <!-- text input -->
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label">操作系统枚举项<br><small>请用逗号分割</small></label>
+                        <div class="col-sm-4">
+                            <input  onchange="checkNull(event)" class="form-control" id="os" placeholder="请输入os枚举项"/>
+                        </div>
+                    </div>
+                    <!-- Date and time range -->
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label" >任务时间</label>
+                        <div class=" col-sm-5">
+                            <!--                                <div class="input-group-addon">-->
+                            <!--                                    <i class="fa fa-clock-o"></i>-->
+                            <!--                                </div>-->
+                            <input type="text" class="form-control pull-right" id="reservationtime"/>
+                        </div>
+                        <!-- /.input group -->
+                    </div>
+
+                    <!-- select -->
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label" >任务类型</label>
+                        <div class=" col-sm-4">
+                            <select class="form-control" id="testType" >
+                                <option >移动应用</option>
+                                <option >Web应用</option>
+                            </select>
+                        </div>
+                    </div>
+
+                    <!-- text input -->
+<!--                    <div class="form-group" id="webUrl" style="display: none">-->
+<!--                        <label class="col-sm-2 control-label">待测网站链接</label>-->
+<!--                        <div class="col-sm-6">-->
+<!--                            <input  onchange="checkNull(event)" class="form-control" id="websiteUrl" placeholder="请输入网站链接"/>-->
+<!--                        </div>-->
+<!--                    </div>-->
+
+<!--                    <div class="form-group" id="apkFile">-->
+<!--                        <label for="testApplication"  class="col-sm-2 control-label">待测应用</label>-->
+<!--                        <div class="col-sm-10">-->
+<!--                            <input  type="file" id="testApplication"/>-->
+<!--                            &lt;!&ndash;                               <p class="help-block">Example block-level help text here.</p>&ndash;&gt;-->
+<!--                        </div>-->
+<!--                    </div>-->
+
+<!--                    <div class="form-group">-->
+<!--                        <label for="testRequirement" class="col-sm-2 control-label">测试需求</label>-->
+<!--                        <div class="col-sm-10">-->
+<!--                            <input  type="file" id="testRequirement"/>-->
+<!--                            &lt;!&ndash;                               <p class="help-block">Example block-level help text here.</p>&ndash;&gt;-->
+<!--                        </div>-->
+<!--                    </div>-->
+
+                    <div class="form-group">
+                        <label for="threePage" class="col-sm-2 control-label">三级页面<br><a th:href="${templateUrl}">示例</a></label>
+                        <div class="col-sm-10">
+                            <input  type="file" id="threePage"/>
+                            <!--                               <p class="help-block">Example block-level help text here.</p>-->
+                        </div>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </section>
+
+</th:block>
+</body>
+</html>
+<!-- jQuery 3 -->
+<!--<script src="/static/AdminLTE/bower_components/jquery/dist/jquery.min.js"></script>-->
+<!-- Bootstrap 3.3.7 -->
+<script src="/static/AdminLTE/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
+<script src="/static/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
+<script src="/static/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
+<script src="/static/AdminLTE/bower_components/select2/dist/js/select2.min.js"></script>
+<!-- InputMask -->
+<script src="/static/AdminLTE/plugins/input-mask/jquery.inputmask.js"></script>
+<script src="/static/AdminLTE/plugins/input-mask/jquery.inputmask.date.extensions.js"></script>
+<script src="/static/AdminLTE/plugins/input-mask/jquery.inputmask.extensions.js"></script>
+<!-- date-range-picker -->
+<script src="/static/AdminLTE/bower_components/moment/min/moment.min.js"></script>
+<script src="/static/AdminLTE/bower_components/bootstrap-daterangepicker/daterangepicker.js"></script>
+<!-- bootstrap datepicker -->
+<script src="/static/AdminLTE/bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js"></script>
+<!-- bootstrap time picker -->
+<script src="/static/AdminLTE/plugins/timepicker/bootstrap-timepicker.min.js"></script>
+<!-- SlimScroll -->
+<script src="/static/AdminLTE/bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script>
+<!-- iCheck 1.0.1 -->
+<script src="/static/AdminLTE/plugins/iCheck/icheck.min.js"></script>
+<!-- FastClick -->
+<script src="/static/AdminLTE/bower_components/fastclick/lib/fastclick.js"></script>
+
+<script src="/static/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
+<script src="/static/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
+<script src="/static/AdminLTE/bower_components/select2/dist/js/select2.min.js"></script>
+<script src="/static/AdminLTE/bower_components/bootstrap-notify/dist/bootstrap-notify.min.js"></script>
+<!--<script src="/static/js/app_info.js"></script>-->
+<script type="text/javascript">
+    $(document).ready(function(){
+        $('#crowdSidebar').addClass('active');
+        $('#crowdSidebarMenu-addCrowdTask').addClass('active');
+        $('#reservationtime').daterangepicker({ timePicker: true, timePickerIncrement: 30, locale: { format: 'MM/DD/YYYY hh:mm A' }})
+    });
+
+    // changeType = function () {
+    //     var type = $('#testType').val();
+    //     if(type == 'Web应用'){
+    //         $('#apkFile').css('display','none');
+    //         $('#webUrl').css('display','block');
+    //     }else{
+    //         //移动应用
+    //         $('#apkFile').css('display','block');
+    //         $('#webUrl').css('display','none');
+    //     }
+    // }
+    
+    addTask = function(){
+        //type  = 0  表示添加
+        // type = 1 表示修改
+        // 遍历页面上的数据,生成json数据。
+        var data = new FormData();
+        data.append('name',$('#name').val());
+        data.append('description',$('#description').val());
+        data.append('time',$('#reservationtime').val());
+        data.append('type',$('#testType').val());
+        data.append('os',$('#os').val());
+        // data.append('webUrl',$('#webUrl').val());
+        // if($('#testType').val() == '移动应用') data.append("apk",document.getElementById('testApplication').files[0])
+        // data.append("requirement",document.getElementById('testRequirement').files[0])
+        data.append("threePage",document.getElementById('threePage').files[0])
+        $.ajax({
+            url: '/addCrowdTask',
+            data:data,
+            type: 'POST',
+            processData: false,
+            contentType: false,
+            success: function (result) {
+                console.log(result);
+                console.log(result.status != '200')
+                if(result.status != '200'){
+                    $.notify({
+                        message: result.message
+                    },{
+                        // settings
+                        delay: 100,
+                        timer: 1000,
+                        type: 'error'
+                    });
+                }else {
+                    $.notify({
+                        message: result.message
+                    },{
+                        delay: 100,
+                        timer: 3000,
+                        type: 'success'
+                    });
+                }
+            }
+        });
+    }
+
+
+    checkNull = function (event) {
+        var num = event.target.value;
+        console.log(num)
+        if(num!=null&&num.length!=0){
+            console.log(event.target.parentElement.parentElement)
+            event.target.parentElement.parentElement.setAttribute("class","form-group has-success")
+            if(event.target.nextSibling!=null){
+                event.target.nextSibling.remove();
+            }
+        }else{
+            var newNode = document.createElement("span");
+            newNode.setAttribute("class","help-block")
+            newNode.innerHTML =" 请填写 ";
+            if(event.target.nextSibling==null){
+                event.target.parentNode.insertBefore(newNode,event.target.nextSibling)
+            }
+            event.target.parentElement.parentElement.setAttribute("class","form-group has-error")
+        }
+    }
+
+</script>

+ 2 - 2
src/main/resources/templates/base.html

@@ -16,7 +16,7 @@
     <!-- AdminLTE Skins. We have chosen the skin-blue for this starter
       page. However, you can choose any other skin. Make sure you
       apply the skin class to the body tag so the changes take effect. -->
-    <link rel="stylesheet" href="/static/AdminLTE/dist/css/skins/skin-blue.min.css" />
+    <link rel="stylesheet" href="/static/AdminLTE/dist/css/skins/skin-red.min.css" />
 
     <title>报告审核</title>
     <style>
@@ -42,7 +42,7 @@
     </style>
 
 </head>
-<body class="skin-blue sidebar-mini" style="height: auto; min-height: 100%;">
+<body class="skin-red sidebar-mini" style="height: auto; min-height: 100%;">
 <div class="wrapper" style="height: auto; min-height: 100%;">
     <header class="main-header">
         <!-- Logo -->

+ 10 - 0
src/main/resources/templates/home.html

@@ -44,11 +44,21 @@
                                 <i class="fa fa-align-justify"></i>任务列表
                             </a>
                         </li>
+                        <li id="crowdSidebarMenu-addCrowdTask">
+                            <a href="/addCrowdTask">
+                                <i class="fa fa-plus"></i>新建众测任务
+                            </a>
+                        </li>
                         <li id="crowdSidebarMenu-addExcel">
                             <a href="/addExcel">
                                 <i class="fa fa-file-excel-o"></i>导入第三方项目
                             </a>
                         </li>
+                        <li id="crowdSidebarMenu-importTask">
+                            <a href="/importTask">
+                                <i class="fa  fa-object-ungroup"></i>导入任务
+                            </a>
+                        </li>
                     </ul>
                 </li>
 

+ 202 - 0
src/main/resources/templates/importTask.html

@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"
+      xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
+      layout:decorator="main">
+<head>
+    <link rel="stylesheet" href="/static/css/applications.css" type="text/css"/>
+    <!-- dataTables -->
+    <link rel="stylesheet" type="text/css"
+          href="/static/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css"/>
+    <!-- Font Awesome -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/font-awesome/css/font-awesome.min.css"/>
+    <!-- Ionicons -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/Ionicons/css/ionicons.min.css"/>
+    <!-- daterange picker -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/bootstrap-daterangepicker/daterangepicker.css"/>
+    <!-- bootstrap datepicker -->
+    <link rel="stylesheet" href="/static/AdminLTE/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css"/>
+    <!-- iCheck for checkboxes and radio inputs -->
+    <link rel="stylesheet" href="/static/AdminLTE/plugins/iCheck/all.css"/>
+    <!-- Bootstrap time Picker -->
+    <link rel="stylesheet" href="/static/AdminLTE/plugins/timepicker/bootstrap-timepicker.min.css"/>
+    <!-- Select2 -->
+    <link rel="stylesheet" type="text/css" href="/static/AdminLTE/bower_components/select2/dist/css/select2.min.css"/>
+    <!-- Theme style -->
+    <link rel="stylesheet" href="/static/AdminLTE/dist/css/AdminLTE.min.css"/>
+    <!-- AdminLTE Skins. Choose a skin from the css/skins
+         folder instead of downloading all of them to reduce the load. -->
+    <link rel="stylesheet" href="/static/AdminLTE/dist/css/skins/_all-skins.min.css"/>
+</head>
+<body>
+<th:block layout:fragment="sidebar">
+    <li th:replace="/home::mainSidebar">
+</th:block>
+
+
+
+
+<th:block layout:fragment="maincontent">
+    <section class="content-header">
+        <h1>
+            导入众测报告
+            <small>众包测试</small>
+        </h1>
+        <ol class="breadcrumb">
+            <li><a href="/home"><i class="fa fa-dashboard"></i> 主页</a></li>
+            <li><a href="/importTask">导入众测任务</a></li>
+        </ol>
+    </section>
+
+    <!-- Main content -->
+    <section class="content" id="maincontent">
+
+        <div class="box box-primary" >
+            <div class="box-header with-border">
+                <h3 class="box-title">基本信息</h3>
+                <button class="btn btn-success pull-right" onclick="importTask()">
+                    导入
+                </button>
+            </div>
+            <!-- /.box-header -->
+            <div class="box-body">
+                <form class="form-horizontal">
+                    <!-- text input -->
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label">单位序列号</label>
+                        <div class="col-sm-4">
+                            <input  onchange="checkNull(event)" class="form-control" id="number" placeholder="请输入单位名称"/>
+                        </div>
+                    </div>
+                    <!-- text input -->
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label">原始任务id</label>
+                        <div class="col-sm-4">
+                            <input  onchange="checkNull(event)" class="form-control" id="originId" placeholder="请输入原始任务id"/>
+                        </div>
+                    </div>
+
+
+                    <div class="form-group">
+                        <label for="zipFile" class="col-sm-2 control-label">导入zip文件</label>
+                        <div class="col-sm-10">
+                            <input  type="file" id="zipFile"/>
+                            <!--                               <p class="help-block">Example block-level help text here.</p>-->
+                        </div>
+                    </div>
+                    <div class="form-group">
+                        <label for="jsonFile" class="col-sm-2 control-label">导入json文件</label>
+                        <div class="col-sm-10">
+                            <input  type="file" id="jsonFile"/>
+                            <!--                               <p class="help-block">Example block-level help text here.</p>-->
+                        </div>
+                    </div>
+
+
+                </form>
+            </div>
+        </div>
+    </section>
+
+</th:block>
+</body>
+</html>
+<!-- jQuery 3 -->
+<!--<script src="/static/AdminLTE/bower_components/jquery/dist/jquery.min.js"></script>-->
+<!-- Bootstrap 3.3.7 -->
+<script src="/static/AdminLTE/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
+<script src="/static/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
+<script src="/static/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
+<script src="/static/AdminLTE/bower_components/select2/dist/js/select2.min.js"></script>
+<!-- InputMask -->
+<script src="/static/AdminLTE/plugins/input-mask/jquery.inputmask.js"></script>
+<script src="/static/AdminLTE/plugins/input-mask/jquery.inputmask.date.extensions.js"></script>
+<script src="/static/AdminLTE/plugins/input-mask/jquery.inputmask.extensions.js"></script>
+<!-- date-range-picker -->
+<script src="/static/AdminLTE/bower_components/moment/min/moment.min.js"></script>
+<script src="/static/AdminLTE/bower_components/bootstrap-daterangepicker/daterangepicker.js"></script>
+<!-- bootstrap datepicker -->
+<script src="/static/AdminLTE/bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js"></script>
+<!-- bootstrap time picker -->
+<script src="/static/AdminLTE/plugins/timepicker/bootstrap-timepicker.min.js"></script>
+<!-- SlimScroll -->
+<script src="/static/AdminLTE/bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script>
+<!-- iCheck 1.0.1 -->
+<script src="/static/AdminLTE/plugins/iCheck/icheck.min.js"></script>
+<!-- FastClick -->
+<script src="/static/AdminLTE/bower_components/fastclick/lib/fastclick.js"></script>
+
+<script src="/static/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
+<script src="/static/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
+<script src="/static/AdminLTE/bower_components/select2/dist/js/select2.min.js"></script>
+<script src="/static/AdminLTE/bower_components/bootstrap-notify/dist/bootstrap-notify.min.js"></script>
+<!--<script src="/static/js/app_info.js"></script>-->
+<script type="text/javascript">
+    $(document).ready(function(){
+        $('#crowdSidebar').addClass('active');
+        $('#crowdSidebarMenu-importTask').addClass('active');
+    });
+
+
+
+    importTask = function(){
+        //type  = 0  表示添加
+        // type = 1 表示修改
+        // 遍历页面上的数据,生成json数据。
+        var data = new FormData();
+        data.append('number',$('#number').val());
+        data.append('originId',$('#originId').val());
+        data.append("zipFile",document.getElementById('zipFile').files[0])
+        data.append("jsonFile",document.getElementById('jsonFile').files[0])
+        $.ajax({
+            url: '/importTask',
+            data:data,
+            type: 'POST',
+            processData: false,
+            contentType: false,
+            success: function (result) {
+
+                if(result != 'success'){
+                    $.notify({
+                        message: result
+                    },{
+                        // settings
+                        delay: 100,
+                        timer: 1000,
+                        type: 'error'
+                    });
+                }else {
+                    $.notify({
+                        message: '导入成功'
+                    },{
+                        // settings
+                        delay: 100,
+                        timer: 3000,
+                        type: 'success'
+                    });
+                }
+            }
+        });
+    }
+
+
+    checkNull = function (event) {
+        var num = event.target.value;
+        console.log(num)
+        if(num!=null&&num.length!=0){
+            console.log(event.target.parentElement.parentElement)
+            event.target.parentElement.parentElement.setAttribute("class","form-group has-success")
+            if(event.target.nextSibling!=null){
+                event.target.nextSibling.remove();
+            }
+        }else{
+            var newNode = document.createElement("span");
+            newNode.setAttribute("class","help-block")
+            newNode.innerHTML =" 请填写 ";
+            if(event.target.nextSibling==null){
+                event.target.parentNode.insertBefore(newNode,event.target.nextSibling)
+            }
+            event.target.parentElement.parentElement.setAttribute("class","form-group has-error")
+        }
+    }
+
+</script>

+ 50 - 1
src/main/resources/templates/task_detail.html

@@ -62,7 +62,7 @@
                             <i class="fa fa-sitemap " style="color: rgba(214,182,27,0.7)"></i>
                             <span>聚合视图</span>
                         </a>
-                        <ul class="treeview-menu"  id="crowdSidebar-reportReview-aggrReportMenu">
+                        <ul class="treeview-menu"  id="crowdSidebar-reportReview-aggrReportMenu" th:if="${task!=null}">
                             <li id="crowdSidebar-reportReview-aggrReportMenu-all">
                                 <a th:href="'/agg_report_list?examId='+${examId}+'&amp;caseId='+${caseId}">
                                     <i class="fa fa-circle-o"></i>
@@ -104,11 +104,21 @@
                     </li>
                 </ul>
             </li>
+            <li id="crowdSidebarMenu-addCrowdTask">
+                <a href="/addCrowdTask">
+                    <i class="fa fa-plus"></i>新建众测任务
+                </a>
+            </li>
             <li id="crowdSidebarMenu-addExcel">
                 <a href="/addExcel">
                     <i class="fa fa-file-excel-o"></i>导入第三方项目
                 </a>
             </li>
+            <li id="crowdSidebarMenu-importTask">
+                <a href="/importTask">
+                    <i class="fa  fa-object-ungroup"></i>导入任务
+                </a>
+            </li>
         </ul>
     </li>
 
@@ -253,6 +263,10 @@
                 <i class="fa fa-coffee" style="margin-right: 3px;"></i>
                 <span>全部交付</span>
             </button>
+            <button  id="export-btn" onclick="exportTask()" class="btn btn-sm btn-assign pull-right">
+                <i class="fa fa-coffee" style="margin-right: 3px;"></i>
+                <span>导出任务</span>
+            </button>
         </div>
         <table id="report-list" class="table table-striped table-bordered" cellpadding="0" width="100%">
             <thead>
@@ -363,6 +377,7 @@
     /*<![CDATA[*/
     var reportHost = [[${reportHost}]]
     var encodedReportDetailUrl = [[${encodedUrl}]]
+    var caseId = [[${caseId}]]
     /*]]>*/
 
 
@@ -401,6 +416,40 @@
         });
     });
 
+    exportTask  = function(){
+        var data = new FormData();
+        data.append('caseId',caseId);
+        $.ajax({
+            url: '/exportTask',
+            data:data,
+            type: 'POST',
+            processData: false,
+            contentType: false,
+            success: function (result) {
+                if(result != 'success'){
+                    $.notify({
+                        message: result
+                    },{
+                        // settings
+                        delay: 100,
+                        timer: 1000,
+                        type: 'error'
+                    });
+                }else {
+
+                    $.notify({
+                        message: '导出成功'
+                    },{
+                        // settings
+                        delay: 100,
+                        timer: 3000,
+                        type: 'success'
+                    });
+                }
+            }
+        });
+    }
+
 
     copyUrl = function(){
         var clipboard = new Clipboard('#taskUrl-btn', {