Преглед на файлове

Merge branch 'test' into prod

# Conflicts:
#	src/main/java/edu/nju/controller/AnalyzeController.java
insomniaLee преди 4 години
родител
ревизия
d25afa065f

+ 1 - 0
.gitignore

@@ -28,5 +28,6 @@ HELP.md
 ### VS Code ###
 .vscode/
 
+.DS_Store
 *.DS_Store
 */.DS_Store

+ 120 - 0
src/main/java/edu/nju/algorithm/progress/BugForProgress.java

@@ -0,0 +1,120 @@
+package edu.nju.algorithm.progress;
+import java.util.Date;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-06 15:56
+ * @Email xjwhhh233@outlook.com
+ */
+
+public class BugForProgress {
+    String id;
+    String testCaseId;
+    String testCaseName;
+    String userId;
+
+    String title;
+    String description;
+    Date bugCreateTime;
+
+    String severity;
+    String bugPage;
+
+    public BugForProgress(){};
+
+    public BugForProgress(String id, String testCaseId, String testCaseName, String title, String description) {
+        this.id = id;
+        this.testCaseId = testCaseId;
+        this.testCaseName = testCaseName;
+
+        this.title = title;
+        this.description = description;
+    }
+
+    public BugForProgress(String id, String testCaseId, String testCaseName, String userId, String title, String description, Date bugCreateTime, String severity, String bugPage) {
+        this.id = id;
+        this.testCaseId = testCaseId;
+        this.userId = userId;
+        this.testCaseName = testCaseName;
+        this.title = title;
+        this.description = description;
+        this.bugCreateTime = bugCreateTime;
+        this.severity = severity;
+        this.bugPage = bugPage;
+    }
+
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getTestCaseId() {
+        return testCaseId;
+    }
+
+    public void setTestCaseId(String testCaseId) {
+        this.testCaseId = testCaseId;
+    }
+
+    public String getTestCaseName() {
+        return testCaseName;
+    }
+
+    public void setTestCaseName(String testCaseName) {
+        this.testCaseName = testCaseName;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Date getBugCreateTime() {
+        return bugCreateTime;
+    }
+
+    public void setBugCreateTime(Date bugCreateTime) {
+        this.bugCreateTime = bugCreateTime;
+    }
+
+    public String getSeverity() {
+        return severity;
+    }
+
+    public void setSeverity(String severity) {
+        this.severity = severity;
+    }
+
+    public String getBugPage() {
+        return bugPage;
+    }
+
+    public void setBugPage(String bugPage) {
+        this.bugPage = bugPage;
+    }
+
+}
+

+ 34 - 0
src/main/java/edu/nju/algorithm/progress/ConstantsForProgress.java

@@ -0,0 +1,34 @@
+package edu.nju.algorithm.progress;
+
+/**
+ * @author xujiawei
+ */
+public interface ConstantsForProgress {
+//	final static Integer FIELD_INDEX_TEST_CASE_ID = 0;
+//	final static Integer FIELD_INDEX_USER_ID = 1;
+//	final static Integer FIELD_INDEX_TEST_CASE_NAME	 = 2;
+//	final static Integer FIELD_INDEX_BUG_DETAIL = 9;
+//	final static Integer FIELD_INDEX_REPRO_STEPS = 10;
+//	final static Integer FIELD_INDEX_SUBMIT_TIME  = 15;
+//	final static Integer FIELD_BUG_TAG = 14;
+//	final static Integer FIELD_DUP_TAG  = 17;    //17 corresponds to R column, 18 corresponds to S column
+	
+	// YK add
+	final static String FIELD_BUG_ID = "bug_id";
+	final static String FIELD_TEST_CASE_ID = "test_case_id";
+	final static String FIELD_USER_ID = "worker_id";
+	final static String FIELD_TEST_CASE_NAME	 = "test_case_name";
+	final static String FIELD_BUG_DETAIL = "title";
+	final static String FIELD_REPRO_STEPS = "description";
+	final static String FIELD_SUBMIT_TIME  = "bug_create_time";
+	final static String FIELD_BUG_TAG = "severity";
+	final static String FIELD_DUP_TAG  = "bug_page";    //17 corresponds to R column, 18 corresponds to S column
+	final static double SIM_THRESHOLD = 0.01;
+	
+	int captureSize = 6;
+	int equalTimeThres = 2;
+	
+	final static String projectFolder = "data/demoProjects";     
+
+	Integer sampleStep = 10;
+}

+ 80 - 0
src/main/java/edu/nju/algorithm/progress/M0CRCAlgorithm.java

@@ -0,0 +1,80 @@
+package edu.nju.algorithm.progress;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.TreeMap;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-06 15:54
+ * @Email xjwhhh233@outlook.com
+ */
+public class M0CRCAlgorithm {
+    public Integer[] obtainRecaptureResults(TreeMap<Integer, ArrayList<BugForProgress>> captureProcess) {
+        HashSet<String> distinctBugs = new HashSet<String>();
+        for (Integer cap : captureProcess.keySet()) {
+            ArrayList<BugForProgress> reportList = captureProcess.get(cap);
+
+            for (int i = 0; i < reportList.size(); i++) {
+                String dupTag = reportList.get(i).getBugPage();
+                distinctBugs.add(dupTag);
+            }
+        }
+
+        int captureSize = captureProcess.size();
+        int sep1 = captureSize / 2;
+        int sep2 = sep1;
+        int sep3 = sep1;
+        if (sep1 > 3) {
+            sep2 = sep1 - 1;
+            sep3 = sep1 + 1;
+        }
+        if (sep1 > 5) {
+            sep2 = sep1 - 2;
+            sep3 = sep1 + 2;
+        }
+
+        int estBugs1 = this.estimateTotalBugNum(captureProcess, sep1);
+        int estBugs2 = this.estimateTotalBugNum(captureProcess, sep2);
+        int estBugs3 = this.estimateTotalBugNum(captureProcess, sep3);
+
+        int estBugs = (estBugs1 + estBugs2 + estBugs3) / 3;
+        return new Integer[]{estBugs, estBugs, estBugs, distinctBugs.size()};
+    }
+
+    public Integer estimateTotalBugNum(TreeMap<Integer, ArrayList<BugForProgress>> captureProcess, int sep) {
+        HashSet<String> priorNoDupBugs = new HashSet<String>();
+        HashSet<String> laterNoDupBugs = new HashSet<String>();
+        int count = 0;
+        for (Integer cap : captureProcess.keySet()) {
+            count++;
+            ArrayList<BugForProgress> reportList = captureProcess.get(cap);
+            for (int i = 0; i < reportList.size(); i++) {
+                BugForProgress report = reportList.get(i);
+                if (count <= sep) {
+                    priorNoDupBugs.add(report.getBugPage());
+                } else {
+                    laterNoDupBugs.add(report.getBugPage());
+                }
+            }
+        }
+
+        int overlapBugs = 0;
+        for (String dupTag : priorNoDupBugs) {
+            if (laterNoDupBugs.contains(dupTag)) {
+                overlapBugs++;
+            }
+        }
+
+        int priorBugs = priorNoDupBugs.size();
+        int laterBugs = laterNoDupBugs.size();
+
+        int estimateBugs = priorBugs * laterBugs;
+        if (overlapBugs > 0) {
+            estimateBugs = estimateBugs / overlapBugs;
+        }
+
+        return estimateBugs;
+    }
+
+}

+ 97 - 0
src/main/java/edu/nju/algorithm/progress/TaskClosePrediction.java

@@ -0,0 +1,97 @@
+package edu.nju.algorithm.progress;
+
+import edu.nju.entities.Bug;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.TreeMap;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-06 16:02
+ * @Email xjwhhh233@outlook.com
+ */
+public class TaskClosePrediction {
+    public Double determineTaskProgressStatus(List<BugForProgress> bugForProgressList) {
+        Random rand = new Random();
+
+        ArrayList<Integer[]> captureResults = new ArrayList<Integer[]>();
+        TreeMap<Integer, ArrayList<BugForProgress>> captureProcess = new TreeMap<Integer, ArrayList<BugForProgress>>();
+
+        int captureSize = ConstantsForProgress.captureSize;
+        int equalTimeThres = ConstantsForProgress.equalTimeThres;
+
+        if (bugForProgressList.size() / captureSize < 5) {
+            return bugForProgressList.size() * 2.0;
+        }
+
+        M0CRCAlgorithm M0Algorithm = new M0CRCAlgorithm();
+        int captureTime = -1;
+        for (int i = 0; i < bugForProgressList.size(); ) {
+            captureTime++;
+            int beginReport = captureTime * captureSize;
+            int endReport = beginReport + captureSize;
+
+            if (endReport >= bugForProgressList.size()) {
+                break;
+            }
+
+            ArrayList<BugForProgress> curCapture = new ArrayList<BugForProgress>();
+            for (int j = beginReport; j < endReport && j < bugForProgressList.size(); j++) {
+                curCapture.add(bugForProgressList.get(j));
+            }
+            captureProcess.put(captureTime, curCapture);
+
+            Integer[] results = M0Algorithm.obtainRecaptureResults(captureProcess);    //estBugs, estBugs, estBugs, alreadySubBugs
+            captureResults.add(results);
+        }
+
+        Boolean isTerminate = this.whetherCanTerminate(captureResults, equalTimeThres);
+        Integer[] lastResults = captureResults.get(captureResults.size() - 1);
+        Double bugRatio = 1.0 * lastResults[3] / lastResults[0];
+        //System.out.println ( "bug ratio : " + bugRatio + " whether can terminate: " + isTerminate );
+
+        bugRatio = bugRatio * 100;
+        if (!isTerminate) {
+            if (bugRatio > 98) {
+                bugRatio = rand.nextInt(4) + 94.0;
+            }
+        }
+
+        return bugRatio;
+    }
+
+    private Boolean whetherCanTerminate(ArrayList<Integer[]> captureResults, Integer equalTimeThres) {
+        if (captureResults.size() < equalTimeThres) {
+            return false;
+        }
+
+        int count = 0;
+        for (int i = captureResults.size() - 1; i > 0; i--) {
+            Integer[] curResults = captureResults.get(i);
+            Integer[] priorResults = captureResults.get(i - 1);
+
+            if (this.isEqual(priorResults, curResults)) {
+                count++;
+            } else {
+                break;
+            }
+
+            if (count >= equalTimeThres) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private Boolean isEqual(Integer[] priorResults, Integer[] curResults) {
+        if (!curResults[0].equals(curResults[3])) {
+            return false;
+        }
+        if (curResults[3] == 0) {
+            return false;
+        }
+        return true;
+    }
+}

+ 22 - 1
src/main/java/edu/nju/controller/AnalyzeController.java

@@ -11,12 +11,16 @@ import java.util.UUID;
 
 import javax.servlet.http.HttpServletResponse;
 
+import edu.nju.dao.TaskDao;
+import edu.nju.entities.Task;
 import edu.nju.entities.ShortToken;
 import edu.nju.model.*;
 import edu.nju.util.AESUtil;
 import edu.nju.util.BlockChainAspect;
 import org.json.JSONArray;
 import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.util.DigestUtils;
@@ -38,7 +42,12 @@ public class AnalyzeController {
 
 //	@Autowired
 //	BlockChainAspect blockChainAspect;
-	
+
+	@Autowired
+	TaskDao taskDao;
+
+	Logger log= LoggerFactory.getLogger(AnalyzeController.class);
+
 	//根据用例获取所有有效bug
 	@RequestMapping(value = "/valid")
 	@ResponseBody
@@ -615,8 +624,20 @@ public class AnalyzeController {
 
 
 
+	@RequestMapping(value = "/progress", method = RequestMethod.GET)
+	@ResponseBody
+	public Double crowdTestProgress(@RequestParam("caseId") String caseId, @RequestParam("taskId") String taskId){
+		Task task=taskDao.findById(taskId);
+		if(task.getEnd_time()>System.currentTimeMillis()){
+			return aservice.crowdTestProgressFromDB(caseId,taskId);
+		}else{
+			return aservice.crowdTestProgress(caseId,taskId);
+		}
+	}
+
 
 	private String [] url2decode(String str){
+		log.info("#AnalyseController url2decode(): "+str);
 		String [] res = new String[2];
 		String []  temp = str.split("&");
 		if(temp[0].startsWith("taskId=")){

+ 52 - 0
src/main/java/edu/nju/controller/DataController.java

@@ -0,0 +1,52 @@
+package edu.nju.controller;
+import edu.nju.entities.BugDetail;
+import edu.nju.service.DataService;
+import edu.nju.service.FileService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2020-12-25 10:40
+ * @Email xjwhhh233@outlook.com
+ */
+@Controller
+@RequestMapping(value = "/data")
+@CrossOrigin(origins = "*", maxAge = 3600, allowCredentials = "true")
+public class DataController {
+
+    @Autowired
+    DataService dataService;
+
+    @Autowired
+    FileService fileService;
+
+    /**根据caseId获取bug报告
+     *
+     * @param caseId
+     */
+    @RequestMapping(value = "/outputByCaseId")
+    @ResponseBody
+    public List<BugDetail> getBugDetailByCaseId(String caseId) {
+        return fileService.exportBugInfo(caseId);
+    }
+
+
+    /**
+     * bug数据导入
+     * @param zipFile
+     * @param jsonFile
+     * @param originalCaseId 与目前系统哪个case对应
+     * @param cpSerialNum 来自哪个cp
+     * @return
+     */
+    @RequestMapping(value = "/inputFromFile")
+    @ResponseBody
+    public List<BugDetail> saveBugDetailFromFile(@RequestParam("zipFile") MultipartFile zipFile, @RequestParam("jsonFile") MultipartFile jsonFile, String originalCaseId, String cpSerialNum) {
+        return fileService.importBugInfo(zipFile,jsonFile,originalCaseId,cpSerialNum);
+    }
+}

+ 31 - 0
src/main/java/edu/nju/dao/BugDetailDao.java

@@ -0,0 +1,31 @@
+package edu.nju.dao;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.stereotype.Repository;
+
+import edu.nju.entities.BugDetail;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2020-12-28 15:24
+ * @Email xjwhhh233@outlook.com
+ */
+@Repository
+public class BugDetailDao {
+    @Autowired
+    private MongoOperations mongoOperations;
+
+    //save存在则更新,不存在则插入
+    public String save(BugDetail bugDetail) {
+        mongoOperations.save(bugDetail);
+        return bugDetail.getId();
+    }
+}

+ 2 - 1
src/main/java/edu/nju/dao/CTBDao.java

@@ -49,7 +49,8 @@ public class CTBDao {
 //		System.out.println("3"+id);
 		return mongoOperations.find(query, CaseToBug.class).get(0);
 	}
-	
+
+	//根据testCaseId获取bugIdList
 	public List<String> findById(String id) {
 //		System.out.println("3"+id);
 		Query query = new Query();

+ 34 - 0
src/main/java/edu/nju/dao/CaseToBugDao.java

@@ -0,0 +1,34 @@
+package edu.nju.dao;
+
+import edu.nju.entities.CaseToBug;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2020-12-25 11:01
+ * @Email xjwhhh233@outlook.com
+ */
+@Repository
+public class CaseToBugDao {
+    @Autowired
+    private MongoOperations mongoOperations;
+
+
+    public CaseToBug findById(String testCaseId) {
+        Query query = new Query();
+        query.addCriteria(Criteria.where("_id").is(testCaseId));
+        List<CaseToBug> caseToBugList = mongoOperations.find(query,CaseToBug.class);
+        if(caseToBugList.size()==0){
+            return null;
+        }else{
+            return caseToBugList.get(0);
+        }
+    }
+}

+ 36 - 0
src/main/java/edu/nju/dao/CrowdTestDao.java

@@ -0,0 +1,36 @@
+package edu.nju.dao;
+
+import edu.nju.entities.CaseToBug;
+import edu.nju.entities.CrowdTest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Repository;
+import java.util.List;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-06 16:43
+ * @Email xjwhhh233@outlook.com
+ */
+@Repository
+public class CrowdTestDao {
+    @Autowired
+    private MongoOperations mongoOperations;
+
+    public void save(CrowdTest crowdTest) {
+        mongoOperations.save(crowdTest);
+    }
+
+    public CrowdTest findByCaseTakeId(String caseTakeId){
+        Query query = new Query();
+        query.addCriteria(Criteria.where("case_take_id").is(caseTakeId));
+        List<CrowdTest> crowdTestList= mongoOperations.find(query, CrowdTest.class);
+        if(crowdTestList.size()==0){
+            return null;
+        }else{
+            return crowdTestList.get(0);
+        }
+    }
+}

+ 6 - 0
src/main/java/edu/nju/dao/ReportDao.java

@@ -65,4 +65,10 @@ public class ReportDao {
 		query.addCriteria(Criteria.where("worker_id").is(worker_id));
 		return mongoOperations.find(query, Report.class);
 	}
+
+	public List<Report> findByCaseId(String caseId){
+		Query query = new Query();
+		query.addCriteria(Criteria.where("case_id").is(caseId));
+		return mongoOperations.find(query, Report.class);
+	}
 }

+ 21 - 1
src/main/java/edu/nju/dao/TaskDao.java

@@ -1,6 +1,8 @@
 package edu.nju.dao;
 
 import edu.nju.entities.Task;
+import edu.nju.util.HTTP;
+import org.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.data.mongodb.core.query.Criteria;
@@ -24,8 +26,26 @@ public class TaskDao {
         Query query = new Query();
         query.addCriteria(Criteria.where("_id").is(id));
         List<Task> tasks = mongoOperations.find(query, Task.class);
-        if(tasks == null || tasks.size() == 0) { return null; }
+        if(tasks.size() == 0) {
+            return getAndSaveTaskInfo(id);
+        }
         else { return tasks.get(0); }
     }
 
+    private Task getAndSaveTaskInfo(String id){
+        String result = HTTP.sendGet("http://www.mooctest.net/api/exam/" + id + "/info", "");
+        if (!"".equals(result)) {
+            JSONObject json = new JSONObject(result);
+            long beginTime = json.getLong("beginTime");
+            long endTime = json.getLong("endTime");
+            String name=json.getString("name");
+            double totalMins = (endTime - beginTime) / 1000 / 60.0;
+            Task newTask = new Task(id, name,beginTime, endTime, totalMins, totalMins);
+            save(newTask);
+            return newTask;
+        }else{
+            return null;
+        }
+    }
+
 }

+ 450 - 0
src/main/java/edu/nju/entities/BugDetail.java

@@ -0,0 +1,450 @@
+package edu.nju.entities;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2020-12-24 15:53
+ * @Email xjwhhh233@outlook.com
+ */
+@Document
+public class BugDetail implements java.io.Serializable {
+
+    //bug属性
+    @Id
+    private String id;
+    private String bugCategory;
+    private String severity;
+    private String recurrent;
+    private String bugCreateTime;
+    private String bugPage;
+    private String title;
+    private String bugDescription;
+    private String imgUrl;
+    private int score;
+    private String parent;
+    private List<String> children;
+    private String root;
+    private int goodNum;
+    private Set<String> goodWorkerId;
+    private int badNum;
+    private Set<String> badWorkerId;
+
+    //测试用例属性
+    private String testCaseId;
+    private String testCaseName;
+    private String testCaseFront;
+    private String testCaseBehind;
+    private String testCaseDescription;
+    private String testCaseCreateTime;
+
+    //report属性
+    private String reportId;
+    private String reportName;
+    private String reportCreateTime;
+    private String scriptLocation;
+    private String reportLocation;
+    private String logLocation;
+    private String deviceModel;
+    private String deviceBrand;
+    private String deviceOs;
+
+    //工人属性
+    private String workerId;
+
+    //众测任务属性
+    private String caseAppName;
+    private String casePaperType;
+    private String caseTestType;
+    private String caseDescription;
+    private String caseRequireDoc;
+
+
+    //原系统中的case_take_id
+    private String caseTakeId;
+    //与目前系统中的哪个case对应
+    private String originalCaseId;
+
+    //cp系统序列号
+    private String cpSerialNum;
+
+    public BugDetail(){};
+
+    @PersistenceConstructor
+    public BugDetail(String id, String bugCategory, String severity, String recurrent, String bugCreateTime, String bugPage, String title, String bugDescription, String imgUrl, int score, String parent, List<String> children, String root, int goodNum, Set<String> goodWorkerId, int badNum, Set<String> badWorkerId, String testCaseId, String testCaseName, String testCaseFront, String testCaseBehind, String testCaseDescription, String testCaseCreateTime, String reportId, String reportName, String reportCreateTime, String scriptLocation, String reportLocation, String logLocation, String deviceModel, String deviceBrand, String deviceOs, String workerId, String caseAppName, String casePaperType, String caseTestType, String caseDescription, String caseRequireDoc, String caseTakeId, String originalCaseId, String cpSerialNum) {
+        this.id = id;
+        this.bugCategory = bugCategory;
+        this.severity = severity;
+        this.recurrent = recurrent;
+        this.bugCreateTime = bugCreateTime;
+        this.bugPage = bugPage;
+        this.title = title;
+        this.bugDescription = bugDescription;
+        this.imgUrl = imgUrl;
+        this.score = score;
+        this.parent = parent;
+        this.children = children;
+        this.root = root;
+        this.goodNum = goodNum;
+        this.goodWorkerId = goodWorkerId;
+        this.badNum = badNum;
+        this.badWorkerId = badWorkerId;
+        this.testCaseId = testCaseId;
+        this.testCaseName = testCaseName;
+        this.testCaseFront = testCaseFront;
+        this.testCaseBehind = testCaseBehind;
+        this.testCaseDescription = testCaseDescription;
+        this.testCaseCreateTime = testCaseCreateTime;
+        this.reportId = reportId;
+        this.reportName = reportName;
+        this.reportCreateTime = reportCreateTime;
+        this.scriptLocation = scriptLocation;
+        this.reportLocation = reportLocation;
+        this.logLocation = logLocation;
+        this.deviceModel = deviceModel;
+        this.deviceBrand = deviceBrand;
+        this.deviceOs = deviceOs;
+        this.workerId = workerId;
+        this.caseAppName = caseAppName;
+        this.casePaperType = casePaperType;
+        this.caseTestType = caseTestType;
+        this.caseDescription = caseDescription;
+        this.caseRequireDoc = caseRequireDoc;
+        this.caseTakeId = caseTakeId;
+        this.originalCaseId = originalCaseId;
+        this.cpSerialNum = cpSerialNum;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getBugCategory() {
+        return bugCategory;
+    }
+
+    public void setBugCategory(String bugCategory) {
+        this.bugCategory = bugCategory;
+    }
+
+    public String getSeverity() {
+        return severity;
+    }
+
+    public void setSeverity(String severity) {
+        this.severity = severity;
+    }
+
+    public String getRecurrent() {
+        return recurrent;
+    }
+
+    public void setRecurrent(String recurrent) {
+        this.recurrent = recurrent;
+    }
+
+    public String getBugCreateTime() {
+        return bugCreateTime;
+    }
+
+    public void setBugCreateTime(String bugCreateTime) {
+        this.bugCreateTime = bugCreateTime;
+    }
+
+    public String getBugPage() {
+        return bugPage;
+    }
+
+    public void setBugPage(String bugPage) {
+        this.bugPage = bugPage;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getBugDescription() {
+        return bugDescription;
+    }
+
+    public void setBugDescription(String bugDescription) {
+        this.bugDescription = bugDescription;
+    }
+
+    public String getImgUrl() {
+        return imgUrl;
+    }
+
+    public void setImgUrl(String imgUrl) {
+        this.imgUrl = imgUrl;
+    }
+
+    public int getScore() {
+        return score;
+    }
+
+    public void setScore(int score) {
+        this.score = score;
+    }
+
+    public String getParent() {
+        return parent;
+    }
+
+    public void setParent(String parent) {
+        this.parent = parent;
+    }
+
+    public List<String> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<String> children) {
+        this.children = children;
+    }
+
+    public String getRoot() {
+        return root;
+    }
+
+    public void setRoot(String root) {
+        this.root = root;
+    }
+
+    public Set<String> getGoodWorkerId() {
+        return goodWorkerId;
+    }
+
+    public int getGoodNum() {
+        return goodNum;
+    }
+
+    public void setGoodNum(int goodNum) {
+        this.goodNum = goodNum;
+    }
+
+    public int getBadNum() {
+        return badNum;
+    }
+
+    public void setBadNum(int badNum) {
+        this.badNum = badNum;
+    }
+
+    public void setGoodWorkerId(Set<String> goodWorkerId) {
+        this.goodWorkerId = goodWorkerId;
+    }
+
+    public Set<String> getBadWorkerId() {
+        return badWorkerId;
+    }
+
+    public void setBadWorkerId(Set<String> badWorkerId) {
+        this.badWorkerId = badWorkerId;
+    }
+
+    public String getTestCaseId() {
+        return testCaseId;
+    }
+
+    public void setTestCaseId(String testCaseId) {
+        this.testCaseId = testCaseId;
+    }
+
+    public String getTestCaseName() {
+        return testCaseName;
+    }
+
+    public void setTestCaseName(String testCaseName) {
+        this.testCaseName = testCaseName;
+    }
+
+    public String getTestCaseFront() {
+        return testCaseFront;
+    }
+
+    public void setTestCaseFront(String testCaseFront) {
+        this.testCaseFront = testCaseFront;
+    }
+
+    public String getTestCaseBehind() {
+        return testCaseBehind;
+    }
+
+    public void setTestCaseBehind(String testCaseBehind) {
+        this.testCaseBehind = testCaseBehind;
+    }
+
+    public String getTestCaseDescription() {
+        return testCaseDescription;
+    }
+
+    public void setTestCaseDescription(String testCaseDescription) {
+        this.testCaseDescription = testCaseDescription;
+    }
+
+    public String getTestCaseCreateTime() {
+        return testCaseCreateTime;
+    }
+
+    public void setTestCaseCreateTime(String testCaseCreateTime) {
+        this.testCaseCreateTime = testCaseCreateTime;
+    }
+
+    public String getReportId() {
+        return reportId;
+    }
+
+    public void setReportId(String reportId) {
+        this.reportId = reportId;
+    }
+
+    public String getReportName() {
+        return reportName;
+    }
+
+    public void setReportName(String reportName) {
+        this.reportName = reportName;
+    }
+
+    public String getReportCreateTime() {
+        return reportCreateTime;
+    }
+
+    public void setReportCreateTime(String reportCreateTime) {
+        this.reportCreateTime = reportCreateTime;
+    }
+
+    public String getScriptLocation() {
+        return scriptLocation;
+    }
+
+    public void setScriptLocation(String scriptLocation) {
+        this.scriptLocation = scriptLocation;
+    }
+
+    public String getReportLocation() {
+        return reportLocation;
+    }
+
+    public void setReportLocation(String reportLocation) {
+        this.reportLocation = reportLocation;
+    }
+
+    public String getLogLocation() {
+        return logLocation;
+    }
+
+    public void setLogLocation(String logLocation) {
+        this.logLocation = logLocation;
+    }
+
+    public String getDeviceModel() {
+        return deviceModel;
+    }
+
+    public void setDeviceModel(String deviceModel) {
+        this.deviceModel = deviceModel;
+    }
+
+    public String getDeviceBrand() {
+        return deviceBrand;
+    }
+
+    public void setDeviceBrand(String deviceBrand) {
+        this.deviceBrand = deviceBrand;
+    }
+
+    public String getDeviceOs() {
+        return deviceOs;
+    }
+
+    public void setDeviceOs(String deviceOs) {
+        this.deviceOs = deviceOs;
+    }
+
+    public String getWorkerId() {
+        return workerId;
+    }
+
+    public void setWorkerId(String workerId) {
+        this.workerId = workerId;
+    }
+
+    public String getCaseAppName() {
+        return caseAppName;
+    }
+
+    public void setCaseAppName(String caseAppName) {
+        this.caseAppName = caseAppName;
+    }
+
+    public String getCasePaperType() {
+        return casePaperType;
+    }
+
+    public void setCasePaperType(String casePaperType) {
+        this.casePaperType = casePaperType;
+    }
+
+    public String getCaseTestType() {
+        return caseTestType;
+    }
+
+    public void setCaseTestType(String caseTestType) {
+        this.caseTestType = caseTestType;
+    }
+
+    public String getCaseDescription() {
+        return caseDescription;
+    }
+
+    public void setCaseDescription(String caseDescription) {
+        this.caseDescription = caseDescription;
+    }
+
+    public String getCaseRequireDoc() {
+        return caseRequireDoc;
+    }
+
+    public void setCaseRequireDoc(String caseRequireDoc) {
+        this.caseRequireDoc = caseRequireDoc;
+    }
+
+    public String getCaseTakeId() {
+        return caseTakeId;
+    }
+
+    public void setCaseTakeId(String caseTakeId) {
+        this.caseTakeId = caseTakeId;
+    }
+
+    public String getOriginalCaseId() {
+        return originalCaseId;
+    }
+
+    public void setOriginalCaseId(String originalCaseId) {
+        this.originalCaseId = originalCaseId;
+    }
+
+    public String getCpSerialNum() {
+        return cpSerialNum;
+    }
+
+    public void setCpSerialNum(String cpSerialNum) {
+        this.cpSerialNum = cpSerialNum;
+    }
+}

+ 78 - 0
src/main/java/edu/nju/entities/CrowdTest.java

@@ -0,0 +1,78 @@
+package edu.nju.entities;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-06 15:43
+ * @Email xjwhhh233@outlook.com
+ */
+@Document
+public class CrowdTest {
+
+    private static final long serialVersionUID = 7484726588592610736L;
+
+    @Id
+    private String id;
+
+    private String caseId;
+
+    private String taskId;
+
+    private String caseTakeId;
+
+    private Double progress;
+
+    public CrowdTest(){};
+
+    @PersistenceConstructor
+    public CrowdTest(String id, String caseId, String taskId, String caseTakeId, Double progress) {
+        this.id = id;
+        this.caseId = caseId;
+        this.taskId = taskId;
+        this.caseTakeId = caseTakeId;
+        this.progress = progress;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getCaseId() {
+        return caseId;
+    }
+
+    public void setCaseId(String caseId) {
+        this.caseId = caseId;
+    }
+
+    public String getTaskId() {
+        return taskId;
+    }
+
+    public void setTaskId(String taskId) {
+        this.taskId = taskId;
+    }
+
+    public String getCaseTakeId() {
+        return caseTakeId;
+    }
+
+    public void setCaseTakeId(String caseTakeId) {
+        this.caseTakeId = caseTakeId;
+    }
+
+    public Double getProgress() {
+        return progress;
+    }
+
+    public void setProgress(Double progress) {
+        this.progress = progress;
+    }
+}

+ 94 - 4
src/main/java/edu/nju/service/AnalyzeService.java

@@ -1,18 +1,20 @@
 package edu.nju.service;
 
 import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
 
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.Lists;
+import edu.nju.algorithm.progress.BugForProgress;
+import edu.nju.algorithm.progress.TaskClosePrediction;
 import edu.nju.dao.*;
 import edu.nju.entities.*;
 import edu.nju.model.*;
 import edu.nju.util.DataMaskingUtil;
 import edu.nju.util.HTTP;
 import edu.nju.util.TimeUtil;
+import edu.nju.util.TransUtil;
 import org.apache.commons.lang3.EnumUtils;
 import org.json.JSONArray;
 import org.json.JSONObject;
@@ -86,11 +88,14 @@ public class AnalyzeService {
 	ExtraService extraService;
 
 	@Autowired
-
 	ShortTokenDao shortTokenDao;
 
+	@Autowired
 	ExamDao examDao;
 
+	@Autowired
+	CrowdTestDao crowdTestDao;
+
 	@Value("${server.host}")
 	private String serverHost;
 	@Value("${report.port}")
@@ -1093,6 +1098,91 @@ public class AnalyzeService {
 		return shortTokenDao.save(shortToken);
 	}
 
+	public Double crowdTestProgressFromDB(String caseId,String taskId){
+		String case_take_id=caseId+"-"+taskId;
+		CrowdTest crowdTest=crowdTestDao.findByCaseTakeId(case_take_id);
+		if(crowdTest==null){
+			return (double) 0;
+		}else{
+			return crowdTest.getProgress();
+		}
+	}
+
+	public Double crowdTestProgress(String caseId,String taskId) {
+		SimpleDateFormat formatLine = new SimpleDateFormat ("yyyy/MM/dd HH:mm");
+		SimpleDateFormat formatCon = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
+		List<BugForProgress> bugForProgressList=new ArrayList<>();
+		String caseTakeId=caseId+"-"+taskId;
+		List<Report> reportList = reportDao.findByCaseTakeId(caseTakeId);
+		for (Report report : reportList) {
+			String reportId = report.getId();
+			List<TestCase> testCaseList = testCaseDao.findByReport(reportId);
+			for (TestCase testCase : testCaseList) {
+				String testCaseId = testCase.getId();
+				List<String> bugIdList = ctbdao.findById(testCaseId);
+				for (String bugId : bugIdList) {
+					Bug bug = bdao.findByid(bugId);
+					if (bug != null) {
+						BugForProgress bugForProgress = new BugForProgress();
+						bugForProgress.setId(bugId);
+						bugForProgress.setTestCaseId(testCaseId);
+						bugForProgress.setTestCaseName(testCase.getName());
+						bugForProgress.setUserId(report.getWorker_id());
+						//bug基本属性
+						bugForProgress.setSeverity(TransUtil.severityTransFromInt(bug.getSeverity()));
+						bugForProgress.setBugPage(bug.getBug_page());
+						bugForProgress.setTitle(bug.getTitle());
+						bugForProgress.setDescription(bug.getDescription());
+						String time = TransUtil.formatTimeMillis(bug.getCreate_time_millis());
+						time = transferString(time);
+						Date submitTime  = null;
+						try {
+							if (time.contains("-")) {
+								submitTime = formatCon.parse(time);
+							} else {
+								submitTime = formatLine.parse(time);
+							}
+						}catch (ParseException e){
+							e.printStackTrace();
+						}
+						bugForProgress.setBugCreateTime(submitTime);
+						bugForProgressList.add(bugForProgress);
+					}
+				}
+			}
+		}
+		bugForProgressList.sort(new Comparator<BugForProgress>() {
+			@Override
+			public int compare(BugForProgress o1, BugForProgress o2) {
+				Date d1 = o1.getBugCreateTime();
+				Date d2 = o2.getBugCreateTime();
+				if (d1.before(d2)) {
+					return -1;
+				} else {
+					return 1;
+				}
+			}
+		});
+		TaskClosePrediction closePrediction = new TaskClosePrediction();
+		Double completeRatio = closePrediction.determineTaskProgressStatus(bugForProgressList);
+		completeRatio = completeRatio.intValue() * 1.0;
+		CrowdTest crowdTest=new CrowdTest();
+		crowdTest.setCaseId(caseId);
+		crowdTest.setTaskId(taskId);
+		crowdTest.setCaseTakeId(caseTakeId);
+		crowdTestDao.save(crowdTest);
+		return completeRatio;
+	}
+
+
+	private String transferString ( String str ){
+		String result = str;
+		result = result.replaceAll( "\r\n", " " );
+		result = result.replaceAll( "\r", " " );
+		result = result.replaceAll( "\n", " " );
+		return result;
+	}
+
 
 
 

+ 381 - 0
src/main/java/edu/nju/service/DataService.java

@@ -0,0 +1,381 @@
+package edu.nju.service;
+
+import com.alibaba.fastjson.JSON;
+import com.aliyun.oss.OSS;
+import edu.nju.dao.*;
+import edu.nju.entities.*;
+import edu.nju.util.OssAliyun;
+import edu.nju.util.TransUtil;
+import org.json.JSONArray;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2020-12-25 10:45
+ * @Email xjwhhh233@outlook.com
+ */
+@Service
+public class DataService {
+    private static final int BUFFER_SIZE = 2048;
+    @Autowired
+    ExamDao examDao;
+
+    @Autowired
+    BugDao bugDao;
+
+    @Autowired
+    ReportDao reportDao;
+
+    @Autowired
+    TestCaseDao testCaseDao;
+
+    @Autowired
+    CaseToBugDao caseToBugDao;
+
+    @Autowired
+    BugScoreDao bugScoreDao;
+
+    @Autowired
+    BugMirrorDao bugMirrorDao;
+
+    @Autowired
+    BugHistoryDao bugHistoryDao;
+
+    @Autowired
+    BugDetailDao bugDetailDao;
+
+    @Value("${cpSerialNum}")
+    private String cpSerialNum;
+
+    @Value("${oss.bucketName}")
+    private String bucketName;
+
+    @Value("${oss.imageUrlPrefix}")
+    private String ossImageUrlPrefix;
+
+    /**
+     * 根据caseId获取对应bug信息
+     *
+     * @param caseId
+     * @return
+     */
+    public List<BugDetail> getBugDetailByCaseId(String caseId) {
+        List<BugDetail> bugDetailList = new ArrayList<>();
+        Exam crowdCase = examDao.findById(caseId);
+        if (crowdCase != null) {
+            List<Report> reportList = reportDao.findByCaseId(caseId);
+            for (Report report : reportList) {
+                String reportId = report.getId();
+                List<TestCase> testCaseList = testCaseDao.findByReport(reportId);
+                for (TestCase testCase : testCaseList) {
+                    String testCaseId = testCase.getId();
+                    CaseToBug caseToBug = caseToBugDao.findById(testCaseId);
+                    if (caseToBug != null) {
+                        List<String> bugIdList = caseToBug.getBug_id();
+                        for (String bugId : bugIdList) {
+                            BugDetail bugDetail = new BugDetail();
+                            bugDetail.setId(bugId);
+                            //bug基本属性
+                            Bug bug = bugDao.findByid(bugId);
+                            if (bug != null) {
+                                bugDetail.setBugCategory(bug.getBug_category());
+                                bugDetail.setSeverity(TransUtil.severityTransFromInt(bug.getSeverity()));
+                                bugDetail.setRecurrent(TransUtil.recurrentTransFromInt(bug.getRecurrent()));
+                                bugDetail.setBugCreateTime(TransUtil.formatTimeMillis(bug.getCreate_time_millis()));
+                                bugDetail.setBugPage(bug.getBug_page());
+                                bugDetail.setTitle(bug.getTitle());
+                                bugDetail.setBugDescription(bug.getDescription());
+                                bugDetail.setImgUrl(bug.getImg_url());
+                            }
+                            //bugScore属性
+                            BugScore bugScore = bugScoreDao.findById(bugId);
+                            if (bugScore != null) {
+                                bugDetail.setScore(bugScore.getGrade());
+                            }
+                            //bugMirror属性
+                            BugMirror bugMirror = bugMirrorDao.findById(bugId);
+                            if (bugMirror != null) {
+                                Set<String> goodWorkerIdSet = new HashSet<>();
+                                Set<String> badWorkerIdSet = new HashSet<>();
+                                Set<String> goodReportIdSet = bugMirror.getGood();
+                                Set<String> badReportIdSet = bugMirror.getBad();
+                                int goodNum = 0;
+                                int badNum = 0;
+                                for (String goodReportId : goodReportIdSet) {
+                                    Report goodReport = reportDao.findById(goodReportId);
+                                    if (goodReport != null) {
+                                        goodNum++;
+                                        goodWorkerIdSet.add(goodReport.getWorker_id());
+                                    }
+                                }
+                                for (String badReportId : badReportIdSet) {
+                                    Report badReport = reportDao.findById(badReportId);
+                                    if (badReport != null) {
+                                        badNum++;
+                                        badWorkerIdSet.add(badReport.getWorker_id());
+                                    }
+                                }
+                                bugDetail.setGoodNum(goodNum);
+                                bugDetail.setBadNum(badNum);
+                                bugDetail.setGoodWorkerId(goodWorkerIdSet);
+                                bugDetail.setBadWorkerId(badWorkerIdSet);
+                            }
+                            //bugHistory属性
+                            BugHistory bugHistory = bugHistoryDao.findByid(bugId);
+                            if (bugHistory != null) {
+                                bugDetail.setParent(bugHistory.getParent());
+                                bugDetail.setChildren(bugHistory.getChildren());
+                                bugDetail.setRoot(bugHistory.getRoot());
+                            }
+                            //testCase属性
+                            bugDetail.setTestCaseId(testCase.getId());
+                            bugDetail.setTestCaseName(testCase.getName());
+                            bugDetail.setTestCaseFront(testCase.getFront());
+                            bugDetail.setTestCaseBehind(testCase.getBehind());
+                            bugDetail.setTestCaseDescription(testCase.getDescription());
+                            bugDetail.setTestCaseCreateTime(TransUtil.formatTimeMillis(testCase.getCreate_time_millis()));
+                            //report属性
+                            bugDetail.setReportId(report.getId());
+                            bugDetail.setReportName(report.getName());
+                            bugDetail.setScriptLocation(report.getScript_location());
+                            bugDetail.setReportLocation(report.getReport_location());
+                            bugDetail.setLogLocation(report.getLog_location());
+                            bugDetail.setDeviceModel(report.getDevice_model());
+                            bugDetail.setDeviceBrand(report.getDevice_brand());
+                            bugDetail.setDeviceOs(report.getDevice_os());
+                            //worker属性
+                            bugDetail.setWorkerId(report.getWorker_id());
+                            //众测任务属性
+                            bugDetail.setCaseAppName(crowdCase.getName());
+                            bugDetail.setCasePaperType(crowdCase.getPaper_type());
+                            bugDetail.setCaseTestType(crowdCase.getTest_type());
+                            bugDetail.setCaseDescription(crowdCase.getDescription());
+                            bugDetail.setCaseRequireDoc("");
+                            bugDetail.setCaseTakeId(report.getCase_take_id());
+                            //cp序列号
+                            bugDetail.setCpSerialNum(cpSerialNum);
+                            bugDetailList.add(bugDetail);
+                        }
+                    }
+                }
+            }
+        }
+        bugDetailToFile(bugDetailList, caseId);
+        return bugDetailList;
+    }
+
+    public List<BugDetail> saveBugDetailFromOss(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId, String cpSerialNum) {
+        try {
+            //读取文件流并保存在本地
+            String prefix="";
+            String zipFilePath=prefix+"/xinchuangdata/input/imageZip/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".zip";
+            File zipFile=new File(zipFilePath);
+            if(!zipFile.getParentFile().exists()) { zipFile.getParentFile().mkdirs(); }
+            if(!sourceZipFile.isEmpty()) { sourceZipFile.transferTo(zipFile); }
+            String jsonFilePath=prefix+"/xinchuangdata/input/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".json";
+            File jsonFile=new File(jsonFilePath);
+            if(!jsonFile.getParentFile().exists()) { jsonFile.getParentFile().mkdirs(); }
+            if(!sourceJsonFile.isEmpty()) { sourceJsonFile.transferTo(jsonFile); }
+            //读取本地文件
+            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(jsonFile));
+            ByteArrayOutputStream buf = new ByteArrayOutputStream();
+            int result = bufferedInputStream.read();
+            while (result != -1) {
+                buf.write((byte) result);
+                result = bufferedInputStream.read();
+            }
+            String json = buf.toString();
+            //转为bugDetail
+            List<BugDetail> bugDetailList = JSON.parseArray(json, BugDetail.class);
+            for (BugDetail bugDetail : bugDetailList) {
+                bugDetail.setOriginalCaseId(originalCaseId);
+                //修改图片文件路径为oss路径
+                String imageUrl = bugDetail.getImgUrl();
+                String[] imageUrlArray = imageUrl.split(",");
+                StringBuilder stringBuilder = new StringBuilder();
+                for (String imageUrlStr : imageUrlArray) {
+                    if(!"".equals(imageUrl)) {
+                        String[] filePath = imageUrlStr.split("/");
+                        String fileName = filePath[filePath.length - 1];
+                        String newImageUrl = ossImageUrlPrefix + originalCaseId + "/" + cpSerialNum + "/" + fileName;
+                        stringBuilder.append(newImageUrl).append(",");
+                    }
+                }
+                bugDetail.setImgUrl(stringBuilder.toString());
+                bugDetailDao.save(bugDetail);
+            }
+            //解压图片文件,上传至oss
+            String destPath=prefix+"/xinchuangdata/input/imageUnzip/"+originalCaseId+"/"+cpSerialNum;
+            File unzipFile=new File(destPath);
+            if(!unzipFile.getParentFile().exists()) { unzipFile.getParentFile().mkdirs(); }
+            unZip(zipFile,destPath,originalCaseId,cpSerialNum);
+            return bugDetailList;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return new ArrayList<>();
+    }
+
+    private void bugDetailToFile(List<BugDetail> bugDetailList, String caseId) {
+        String[] titles = {"bug_id", "bug_category", "severity", "recurrent", "bug_create_time", "bug_page", "title",
+                "description", "img_url",
+                "score", "parent", "children", "root", "good_num", "good_worker_id", "bad_num", "bad_worker_id",
+                "test_case_id", "test_case_name", "test_case_front", "test_case_behind", "test_case_description", "test_case_create_time",
+                "report_id", "report_name", "report_create_time", "script_location", "report_location", "log_location", "device_model", "device_brand", "device_os",
+                "worker_id",
+                "case_app_name", "case_paper_type", "case_test_type", "case_description", "case_require_doc",
+                "case_take_id", "originalCaseId", "cpSerialNum"};
+        File csvFile = exportCsv(titles, bugDetailList, caseId);
+        File jsonFile = exportJson(bugDetailList, caseId);
+//        uploadToOss(csvFile);
+//        uploadToOss(jsonFile);
+    }
+
+    private File exportJson(List<BugDetail> bugDetailList, String caseId) {
+        try {
+            File file = new File("data/output/" + caseId + ".json");
+            JSONArray jsonArray = new JSONArray(bugDetailList);
+            Writer write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
+            write.write(jsonArray.toString());
+            write.flush();
+            write.close();
+            return file;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    private <T> File exportCsv(String[] titles, List<T> list, String caseId) {
+        try {
+            File file = new File("data/output/" + caseId + ".csv");
+            //构建输出流,同时指定编码
+            OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
+            //csv文件是逗号分隔,除第一个外,每次写入一个单元格数据后需要输入逗号
+            for (String title : titles) {
+                ow.write(title);
+                ow.write(",");
+            }
+            //写完文件头后换行
+            ow.write("\r\n");
+            //写内容
+            for (Object obj : list) {
+                //利用反射获取所有字段
+                Field[] fields = obj.getClass().getDeclaredFields();
+                for (Field field : fields) {
+                    //设置字段可见性
+                    field.setAccessible(true);
+                    //防止某个field没有赋值
+                    if (field.get(obj) == null) {
+                        ow.write("");
+                    } else {
+                        //解决csv文件中对于逗号和双引号的转义问题
+                        ow.write("\"" + field.get(obj).toString().replaceAll("\"", "\"\"") + "\"");
+                    }
+                    ow.write(",");
+                }
+                //写完一行换行
+                ow.write("\r\n");
+            }
+            ow.flush();
+            ow.close();
+            return file;
+        } catch (IOException | IllegalAccessException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private void uploadToOss(String objectName,File file) {
+        if (file != null) {
+            OSS ossClient = OssAliyun.initShangHaiOss();
+            OssAliyun.uploadFile(ossClient, bucketName, objectName, file);
+        } else {
+            System.out.println("file is null");
+        }
+    }
+
+    /**
+     * zip解压
+     *
+     * @param srcFile     zip源文件
+     * @param destDirPath 解压后的目标文件夹
+     * @throws RuntimeException 解压失败会抛出运行时异常
+     */
+
+    private void unZip(File srcFile, String destDirPath,String originalCaseId,String fromCpSerialNum) throws RuntimeException {
+        long start = System.currentTimeMillis();
+        // 判断源文件是否存在
+        if (!srcFile.exists()) {
+            throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
+        }
+        // 开始解压
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(srcFile);
+            Enumeration<?> entries = zipFile.entries();
+
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = (ZipEntry) entries.nextElement();
+                System.out.println("解压" + entry.getName());
+                // 如果是文件夹,就创建个文件夹
+                if (entry.isDirectory()) {
+                    String dirPath = destDirPath + "/" + entry.getName();
+                    File dir = new File(dirPath);
+                    dir.mkdirs();
+                } else {
+                    // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
+                    File targetFile = new File(destDirPath + "/" + entry.getName());
+                    // 保证这个文件的父文件夹必须要存在
+                    if (!targetFile.getParentFile().exists()) {
+                        targetFile.getParentFile().mkdirs();
+                    }
+                    targetFile.createNewFile();
+                    // 将压缩文件内容写入到这个文件中
+                    InputStream is = zipFile.getInputStream(entry);
+                    FileOutputStream fos = new FileOutputStream(targetFile);
+                    int len;
+                    byte[] buf = new byte[BUFFER_SIZE];
+                    while ((len = is.read(buf)) != -1) {
+                        fos.write(buf, 0, len);
+                    }
+                    // 关流顺序,先打开的后关闭
+                    fos.close();
+                    is.close();
+                    //图片文件上传至oss
+                    String objectName = "xinchuang/image/"+originalCaseId+"/"+fromCpSerialNum+"/" + targetFile.getName();
+                    uploadToOss(objectName,targetFile);
+                }
+            }
+            long end = System.currentTimeMillis();
+            System.out.println("解压完成,耗时:" + (end - start) + " ms");
+        } catch (Exception e) {
+            throw new RuntimeException("unzip error from ZipUtils", e);
+        } finally {
+            if (zipFile != null) {
+                try {
+                    zipFile.close();
+
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+}
+
+

+ 17 - 0
src/main/java/edu/nju/service/FileService.java

@@ -0,0 +1,17 @@
+package edu.nju.service;
+
+import edu.nju.entities.BugDetail;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-04 15:24
+ * @Email xjwhhh233@outlook.com
+ */
+public interface FileService {
+    public void uploadImage();
+    public List<BugDetail> importBugInfo(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId, String cpSerialNum);
+    public List<BugDetail> exportBugInfo(String caseId);
+}

+ 471 - 0
src/main/java/edu/nju/service/NginxFileService.java

@@ -0,0 +1,471 @@
+package edu.nju.service;
+
+import edu.nju.entities.BugDetail;
+import edu.nju.entities.Exam;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+
+import java.io.File;
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+import com.aliyun.oss.OSS;
+import edu.nju.dao.*;
+import edu.nju.entities.*;
+import edu.nju.util.OssAliyun;
+import edu.nju.util.TransUtil;
+import org.json.JSONArray;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-04 15:27
+ * @Email xjwhhh233@outlook.com
+ */
+@Service
+@ConditionalOnExpression("${useOss}==false")
+public class NginxFileService implements FileService {
+
+    private static final int BUFFER_SIZE = 2048;
+    @Autowired
+    ExamDao examDao;
+
+    @Autowired
+    BugDao bugDao;
+
+    @Autowired
+    ReportDao reportDao;
+
+    @Autowired
+    TestCaseDao testCaseDao;
+
+    @Autowired
+    CaseToBugDao caseToBugDao;
+
+    @Autowired
+    BugScoreDao bugScoreDao;
+
+    @Autowired
+    BugMirrorDao bugMirrorDao;
+
+    @Autowired
+    BugHistoryDao bugHistoryDao;
+
+    @Autowired
+    BugDetailDao bugDetailDao;
+
+    @Value("${cpSerialNum}")
+    private String cpSerialNum;
+
+    @Value("${nginx.imageUrlPrefix}")
+    private String nginxImageUrlPrefix;
+
+    @Override
+    public void uploadImage() {
+
+    }
+
+    @Override
+    public List<BugDetail> importBugInfo(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId, String cpSerialNum) {
+       return saveBugDetail(sourceZipFile,sourceJsonFile,originalCaseId,cpSerialNum);
+    }
+
+    @Override
+    public List<BugDetail> exportBugInfo(String caseId) {
+        List<BugDetail> bugDetailList = getBugDetailListByCaseId(caseId);
+        bugDetailToFile(bugDetailList,caseId);
+        return bugDetailList;
+    }
+
+    private List<BugDetail> saveBugDetail(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId,String cpSerialNum){
+        try {
+            //读取文件流并保存在本地
+            String prefix="";
+            String zipFilePath=prefix+"/xinchuangdata/input/imageZip/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".zip";
+            File zipFile=new File(zipFilePath);
+            if(!zipFile.getParentFile().exists()) { zipFile.getParentFile().mkdirs(); }
+            if(!sourceZipFile.isEmpty()) { sourceZipFile.transferTo(zipFile); }
+            String jsonFilePath=prefix+"/xinchuangdata/input/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".json";
+            File jsonFile=new File(jsonFilePath);
+            if(!jsonFile.getParentFile().exists()) { jsonFile.getParentFile().mkdirs(); }
+            if(!sourceJsonFile.isEmpty()) { sourceJsonFile.transferTo(jsonFile); }
+            //读取本地文件
+            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(jsonFile));
+            ByteArrayOutputStream buf = new ByteArrayOutputStream();
+            int result = bufferedInputStream.read();
+            while (result != -1) {
+                buf.write((byte) result);
+                result = bufferedInputStream.read();
+            }
+            String json = buf.toString();
+            //转为bugDetail
+            List<BugDetail> bugDetailList = JSON.parseArray(json, BugDetail.class);
+            for (BugDetail bugDetail : bugDetailList) {
+                bugDetail.setOriginalCaseId(originalCaseId);
+                //修改图片文件路径为本地路径
+                String imageUrl = bugDetail.getImgUrl();
+                String[] imageUrlArray = imageUrl.split(",");
+                StringBuilder stringBuilder = new StringBuilder();
+                for (String imageUrlStr : imageUrlArray) {
+                    if(!"".equals(imageUrl)) {
+                        String[] filePath = imageUrlStr.split("/");
+                        String fileName = filePath[filePath.length - 1];
+                        String newImageUrl = nginxImageUrlPrefix + originalCaseId + "/" + cpSerialNum + "/" + fileName;
+                        stringBuilder.append(newImageUrl).append(",");
+                    }
+                }
+                bugDetail.setImgUrl(stringBuilder.toString());
+                bugDetailDao.save(bugDetail);
+            }
+            //解压图片文件,保存至本地
+            String destPath=prefix+"/xinchuangdata/input/imageUnzip/"+originalCaseId+"/"+cpSerialNum;
+            File unzipFile=new File(destPath);
+            if(!unzipFile.getParentFile().exists()) { unzipFile.getParentFile().mkdirs(); }
+            unZip(zipFile,destPath,originalCaseId,cpSerialNum);
+            return bugDetailList;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return new ArrayList<>();
+    }
+    private List<BugDetail> getBugDetailListByCaseId(String caseId) {
+        List<BugDetail> bugDetailList = new ArrayList<>();
+        Exam crowdCase = examDao.findById(caseId);
+        if (crowdCase != null) {
+            List<Report> reportList = reportDao.findByCaseId(caseId);
+            for (Report report : reportList) {
+                String reportId = report.getId();
+                List<TestCase> testCaseList = testCaseDao.findByReport(reportId);
+                for (TestCase testCase : testCaseList) {
+                    String testCaseId = testCase.getId();
+                    CaseToBug caseToBug = caseToBugDao.findById(testCaseId);
+                    if (caseToBug != null) {
+                        List<String> bugIdList = caseToBug.getBug_id();
+                        for (String bugId : bugIdList) {
+                            BugDetail bugDetail = new BugDetail();
+                            bugDetail.setId(bugId);
+                            //bug基本属性
+                            Bug bug = bugDao.findByid(bugId);
+                            if (bug != null) {
+                                bugDetail.setBugCategory(bug.getBug_category());
+                                bugDetail.setSeverity(TransUtil.severityTransFromInt(bug.getSeverity()));
+                                bugDetail.setRecurrent(TransUtil.recurrentTransFromInt(bug.getRecurrent()));
+                                bugDetail.setBugCreateTime(TransUtil.formatTimeMillis(bug.getCreate_time_millis()));
+                                bugDetail.setBugPage(bug.getBug_page());
+                                bugDetail.setTitle(bug.getTitle());
+                                bugDetail.setBugDescription(bug.getDescription());
+                                bugDetail.setImgUrl(bug.getImg_url());
+                            }
+                            //bugScore属性
+                            BugScore bugScore = bugScoreDao.findById(bugId);
+                            if (bugScore != null) {
+                                bugDetail.setScore(bugScore.getGrade());
+                            }
+                            //bugMirror属性
+                            BugMirror bugMirror = bugMirrorDao.findById(bugId);
+                            if (bugMirror != null) {
+                                Set<String> goodWorkerIdSet = new HashSet<>();
+                                Set<String> badWorkerIdSet = new HashSet<>();
+                                Set<String> goodReportIdSet = bugMirror.getGood();
+                                Set<String> badReportIdSet = bugMirror.getBad();
+                                int goodNum = 0;
+                                int badNum = 0;
+                                for (String goodReportId : goodReportIdSet) {
+                                    Report goodReport = reportDao.findById(goodReportId);
+                                    if (goodReport != null) {
+                                        goodNum++;
+                                        goodWorkerIdSet.add(goodReport.getWorker_id());
+                                    }
+                                }
+                                for (String badReportId : badReportIdSet) {
+                                    Report badReport = reportDao.findById(badReportId);
+                                    if (badReport != null) {
+                                        badNum++;
+                                        badWorkerIdSet.add(badReport.getWorker_id());
+                                    }
+                                }
+                                bugDetail.setGoodNum(goodNum);
+                                bugDetail.setBadNum(badNum);
+                                bugDetail.setGoodWorkerId(goodWorkerIdSet);
+                                bugDetail.setBadWorkerId(badWorkerIdSet);
+                            }
+                            //bugHistory属性
+                            BugHistory bugHistory = bugHistoryDao.findByid(bugId);
+                            if (bugHistory != null) {
+                                bugDetail.setParent(bugHistory.getParent());
+                                bugDetail.setChildren(bugHistory.getChildren());
+                                bugDetail.setRoot(bugHistory.getRoot());
+                            }
+                            //testCase属性
+                            bugDetail.setTestCaseId(testCase.getId());
+                            bugDetail.setTestCaseName(testCase.getName());
+                            bugDetail.setTestCaseFront(testCase.getFront());
+                            bugDetail.setTestCaseBehind(testCase.getBehind());
+                            bugDetail.setTestCaseDescription(testCase.getDescription());
+                            bugDetail.setTestCaseCreateTime(TransUtil.formatTimeMillis(testCase.getCreate_time_millis()));
+                            //report属性
+                            bugDetail.setReportId(report.getId());
+                            bugDetail.setReportName(report.getName());
+                            bugDetail.setScriptLocation(report.getScript_location());
+                            bugDetail.setReportLocation(report.getReport_location());
+                            bugDetail.setLogLocation(report.getLog_location());
+                            bugDetail.setDeviceModel(report.getDevice_model());
+                            bugDetail.setDeviceBrand(report.getDevice_brand());
+                            bugDetail.setDeviceOs(report.getDevice_os());
+                            //worker属性
+                            bugDetail.setWorkerId(report.getWorker_id());
+                            //众测任务属性
+                            bugDetail.setCaseAppName(crowdCase.getName());
+                            bugDetail.setCasePaperType(crowdCase.getPaper_type());
+                            bugDetail.setCaseTestType(crowdCase.getTest_type());
+                            bugDetail.setCaseDescription(crowdCase.getDescription());
+                            bugDetail.setCaseRequireDoc("");
+                            bugDetail.setCaseTakeId(report.getCase_take_id());
+                            //cp序列号
+                            bugDetail.setCpSerialNum(cpSerialNum);
+                            bugDetailList.add(bugDetail);
+                        }
+                    }
+                }
+            }
+        }
+        return bugDetailList;
+    }
+
+    private void bugDetailToFile(List<BugDetail> bugDetailList, String caseId) {
+        String[] titles = {"bug_id", "bug_category", "severity", "recurrent", "bug_create_time", "bug_page", "title",
+                "description", "img_url",
+                "score", "parent", "children", "root", "good_num", "good_worker_id", "bad_num", "bad_worker_id",
+                "test_case_id", "test_case_name", "test_case_front", "test_case_behind", "test_case_description", "test_case_create_time",
+                "report_id", "report_name", "report_create_time", "script_location", "report_location", "log_location", "device_model", "device_brand", "device_os",
+                "worker_id",
+                "case_app_name", "case_paper_type", "case_test_type", "case_description", "case_require_doc",
+                "case_take_id", "originalCaseId", "cpSerialNum"};
+        //导出文件
+        exportCsv(titles, bugDetailList, caseId);
+        exportJson(bugDetailList, caseId);
+        //导出图片zip包
+        String sourceFile = "/xinchuangdata/image/" + caseId;
+        try {
+            File dest = new File("/xinchuangdata/output/imageZip/" + caseId + ".zip");
+            if (!dest.getParentFile().exists()) {
+                dest.getParentFile().mkdirs();
+            }
+            FileOutputStream fileOutputStream = new FileOutputStream(dest);
+            toZip(sourceFile, fileOutputStream, false);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void exportJson(List<BugDetail> bugDetailList, String caseId) {
+        try {
+            File file = new File("/xinchuangdata/output/" + caseId + "/" + caseId + ".json");
+            if (!file.getParentFile().exists()) {
+                file.getParentFile().mkdirs();
+            }
+            JSONArray jsonArray = new JSONArray(bugDetailList);
+            Writer write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
+            write.write(jsonArray.toString());
+            write.flush();
+            write.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private <T> void exportCsv(String[] titles, List<T> list, String caseId) {
+        try {
+            File file = new File("/xinchuangdata/output/" + caseId + "/" + caseId + ".csv");
+            if (!file.getParentFile().exists()) {
+                file.getParentFile().mkdirs();
+            }
+            //构建输出流,同时指定编码
+            OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
+            //csv文件是逗号分隔,除第一个外,每次写入一个单元格数据后需要输入逗号
+            for (String title : titles) {
+                ow.write(title);
+                ow.write(",");
+            }
+            //写完文件头后换行
+            ow.write("\r\n");
+            //写内容
+            for (Object obj : list) {
+                //利用反射获取所有字段
+                Field[] fields = obj.getClass().getDeclaredFields();
+                for (Field field : fields) {
+                    //设置字段可见性
+                    field.setAccessible(true);
+                    //防止某个field没有赋值
+                    if (field.get(obj) == null) {
+                        ow.write("");
+                    } else {
+                        //解决csv文件中对于逗号和双引号的转义问题
+                        ow.write("\"" + field.get(obj).toString().replaceAll("\"", "\"\"") + "\"");
+                    }
+                    ow.write(",");
+                }
+                //写完一行换行
+                ow.write("\r\n");
+            }
+            ow.flush();
+            ow.close();
+        } catch (IOException | IllegalAccessException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void toZip(String srcDir, OutputStream out, boolean keepDirStructure)
+            throws RuntimeException {
+        long start = System.currentTimeMillis();
+        ZipOutputStream zos = null;
+        try {
+            zos = new ZipOutputStream(out);
+            File sourceFile = new File(srcDir);
+            compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
+            long end = System.currentTimeMillis();
+            System.out.println("压缩完成,耗时:" + (end - start) + " ms");
+        } catch (Exception e) {
+            throw new RuntimeException("zip error from ZipUtils", e);
+        } finally {
+            if (zos != null) {
+                try {
+                    zos.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * 递归压缩方法
+     *
+     * @param sourceFile       源文件
+     * @param zos              zip输出流
+     * @param name             压缩后的名称
+     * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
+     *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
+     * @throws Exception
+     */
+    private void compress(File sourceFile, ZipOutputStream zos, String name,
+                          boolean keepDirStructure) throws Exception {
+        byte[] buf = new byte[BUFFER_SIZE];
+        if (sourceFile.isFile()) {
+            // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
+            zos.putNextEntry(new ZipEntry(name));
+            // copy文件到zip输出流中
+            int len;
+            FileInputStream in = new FileInputStream(sourceFile);
+            while ((len = in.read(buf)) != -1) {
+                zos.write(buf, 0, len);
+            }
+            // Complete the entry
+            zos.closeEntry();
+            in.close();
+        } else {
+            File[] listFiles = sourceFile.listFiles();
+            if (listFiles == null || listFiles.length == 0) {
+                // 需要保留原来的文件结构时,需要对空文件夹进行处理
+                if (keepDirStructure) {
+                    // 空文件夹的处理
+                    zos.putNextEntry(new ZipEntry(name + "/"));
+                    // 没有文件,不需要文件的copy
+                    zos.closeEntry();
+                }
+
+            } else {
+                for (File file : listFiles) {
+                    // 判断是否需要保留原来的文件结构
+                    if (keepDirStructure) {
+                        // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
+                        // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
+                        compress(file, zos, name + "/" + file.getName(), true);
+                    } else {
+                        compress(file, zos, file.getName(), false);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * zip解压
+     *
+     * @param srcFile     zip源文件
+     * @param destDirPath 解压后的目标文件夹
+     * @throws RuntimeException 解压失败会抛出运行时异常
+     */
+
+    private void unZip(File srcFile, String destDirPath,String originalCaseId,String fromCpSerialNum) throws RuntimeException {
+        long start = System.currentTimeMillis();
+        // 判断源文件是否存在
+        if (!srcFile.exists()) {
+            throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
+        }
+        // 开始解压
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(srcFile);
+            Enumeration<?> entries = zipFile.entries();
+
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = (ZipEntry) entries.nextElement();
+                System.out.println("解压" + entry.getName());
+                // 如果是文件夹,就创建个文件夹
+                if (entry.isDirectory()) {
+                    String dirPath = destDirPath + "/" + entry.getName();
+                    File dir = new File(dirPath);
+                    dir.mkdirs();
+                } else {
+                    // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
+                    File targetFile = new File(destDirPath + "/" + entry.getName());
+                    // 保证这个文件的父文件夹必须要存在
+                    if (!targetFile.getParentFile().exists()) {
+                        targetFile.getParentFile().mkdirs();
+                    }
+                    targetFile.createNewFile();
+                    // 将压缩文件内容写入到这个文件中
+                    InputStream is = zipFile.getInputStream(entry);
+                    FileOutputStream fos = new FileOutputStream(targetFile);
+                    int len;
+                    byte[] buf = new byte[BUFFER_SIZE];
+                    while ((len = is.read(buf)) != -1) {
+                        fos.write(buf, 0, len);
+                    }
+                    // 关流顺序,先打开的后关闭
+                    fos.close();
+                    is.close();
+                }
+            }
+            long end = System.currentTimeMillis();
+            System.out.println("解压完成,耗时:" + (end - start) + " ms");
+        } catch (Exception e) {
+            throw new RuntimeException("unzip error from ZipUtils", e);
+        } finally {
+            if (zipFile != null) {
+                try {
+                    zipFile.close();
+
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+
+}

+ 479 - 0
src/main/java/edu/nju/service/OssFileService.java

@@ -0,0 +1,479 @@
+package edu.nju.service;
+
+import edu.nju.entities.BugDetail;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+import com.alibaba.fastjson.JSON;
+import com.aliyun.oss.OSS;
+import edu.nju.dao.*;
+import edu.nju.entities.*;
+import edu.nju.util.OssAliyun;
+import edu.nju.util.TransUtil;
+import org.json.JSONArray;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2021-01-04 15:53
+ * @Email xjwhhh233@outlook.com
+ */
+@Service
+@ConditionalOnExpression("${useOss}==true")
+public class OssFileService implements FileService {
+    private static final int BUFFER_SIZE = 2048;
+    @Autowired
+    ExamDao examDao;
+
+    @Autowired
+    BugDao bugDao;
+
+    @Autowired
+    ReportDao reportDao;
+
+    @Autowired
+    TestCaseDao testCaseDao;
+
+    @Autowired
+    CaseToBugDao caseToBugDao;
+
+    @Autowired
+    BugScoreDao bugScoreDao;
+
+    @Autowired
+    BugMirrorDao bugMirrorDao;
+
+    @Autowired
+    BugHistoryDao bugHistoryDao;
+
+    @Autowired
+    BugDetailDao bugDetailDao;
+
+    @Value("${cpSerialNum}")
+    private String cpSerialNum;
+
+    @Value("${oss.bucketName}")
+    private String bucketName;
+
+    @Value("${oss.imageUrlPrefix}")
+    private String ossImageUrlPrefix;
+
+    String localPrefix ="";
+
+    @Override
+    public void uploadImage() {
+
+    }
+
+    @Override
+    public List<BugDetail> importBugInfo(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId, String cpSerialNum) {
+        return saveBugDetail(sourceZipFile,sourceJsonFile,originalCaseId,cpSerialNum);
+    }
+
+    @Override
+    public List<BugDetail> exportBugInfo(String caseId) {
+        List<BugDetail> bugDetailList = getBugDetailListByCaseId(caseId);
+        bugDetailToFile(bugDetailList,caseId);
+        return bugDetailList;
+    }
+
+
+    /**
+     * 根据caseId获取对应bug信息
+     *
+     * @param caseId
+     * @return
+     */
+    public List<BugDetail> getBugDetailListByCaseId(String caseId) {
+        List<BugDetail> bugDetailList = new ArrayList<>();
+        Exam crowdCase = examDao.findById(caseId);
+        if (crowdCase != null) {
+            List<Report> reportList = reportDao.findByCaseId(caseId);
+            for (Report report : reportList) {
+                String reportId = report.getId();
+                List<TestCase> testCaseList = testCaseDao.findByReport(reportId);
+                for (TestCase testCase : testCaseList) {
+                    String testCaseId = testCase.getId();
+                    CaseToBug caseToBug = caseToBugDao.findById(testCaseId);
+                    if (caseToBug != null) {
+                        List<String> bugIdList = caseToBug.getBug_id();
+                        for (String bugId : bugIdList) {
+                            BugDetail bugDetail = new BugDetail();
+                            bugDetail.setId(bugId);
+                            //bug基本属性
+                            Bug bug = bugDao.findByid(bugId);
+                            if (bug != null) {
+                                bugDetail.setBugCategory(bug.getBug_category());
+                                bugDetail.setSeverity(TransUtil.severityTransFromInt(bug.getSeverity()));
+                                bugDetail.setRecurrent(TransUtil.recurrentTransFromInt(bug.getRecurrent()));
+                                bugDetail.setBugCreateTime(TransUtil.formatTimeMillis(bug.getCreate_time_millis()));
+                                bugDetail.setBugPage(bug.getBug_page());
+                                bugDetail.setTitle(bug.getTitle());
+                                bugDetail.setBugDescription(bug.getDescription());
+                                bugDetail.setImgUrl(bug.getImg_url());
+                            }
+                            //bugScore属性
+                            BugScore bugScore = bugScoreDao.findById(bugId);
+                            if (bugScore != null) {
+                                bugDetail.setScore(bugScore.getGrade());
+                            }
+                            //bugMirror属性
+                            BugMirror bugMirror = bugMirrorDao.findById(bugId);
+                            if (bugMirror != null) {
+                                Set<String> goodWorkerIdSet = new HashSet<>();
+                                Set<String> badWorkerIdSet = new HashSet<>();
+                                Set<String> goodReportIdSet = bugMirror.getGood();
+                                Set<String> badReportIdSet = bugMirror.getBad();
+                                int goodNum = 0;
+                                int badNum = 0;
+                                for (String goodReportId : goodReportIdSet) {
+                                    Report goodReport = reportDao.findById(goodReportId);
+                                    if (goodReport != null) {
+                                        goodNum++;
+                                        goodWorkerIdSet.add(goodReport.getWorker_id());
+                                    }
+                                }
+                                for (String badReportId : badReportIdSet) {
+                                    Report badReport = reportDao.findById(badReportId);
+                                    if (badReport != null) {
+                                        badNum++;
+                                        badWorkerIdSet.add(badReport.getWorker_id());
+                                    }
+                                }
+                                bugDetail.setGoodNum(goodNum);
+                                bugDetail.setBadNum(badNum);
+                                bugDetail.setGoodWorkerId(goodWorkerIdSet);
+                                bugDetail.setBadWorkerId(badWorkerIdSet);
+                            }
+                            //bugHistory属性
+                            BugHistory bugHistory = bugHistoryDao.findByid(bugId);
+                            if (bugHistory != null) {
+                                bugDetail.setParent(bugHistory.getParent());
+                                bugDetail.setChildren(bugHistory.getChildren());
+                                bugDetail.setRoot(bugHistory.getRoot());
+                            }
+                            //testCase属性
+                            bugDetail.setTestCaseId(testCase.getId());
+                            bugDetail.setTestCaseName(testCase.getName());
+                            bugDetail.setTestCaseFront(testCase.getFront());
+                            bugDetail.setTestCaseBehind(testCase.getBehind());
+                            bugDetail.setTestCaseDescription(testCase.getDescription());
+                            bugDetail.setTestCaseCreateTime(TransUtil.formatTimeMillis(testCase.getCreate_time_millis()));
+                            //report属性
+                            bugDetail.setReportId(report.getId());
+                            bugDetail.setReportName(report.getName());
+                            bugDetail.setScriptLocation(report.getScript_location());
+                            bugDetail.setReportLocation(report.getReport_location());
+                            bugDetail.setLogLocation(report.getLog_location());
+                            bugDetail.setDeviceModel(report.getDevice_model());
+                            bugDetail.setDeviceBrand(report.getDevice_brand());
+                            bugDetail.setDeviceOs(report.getDevice_os());
+                            //worker属性
+                            bugDetail.setWorkerId(report.getWorker_id());
+                            //众测任务属性
+                            bugDetail.setCaseAppName(crowdCase.getName());
+                            bugDetail.setCasePaperType(crowdCase.getPaper_type());
+                            bugDetail.setCaseTestType(crowdCase.getTest_type());
+                            bugDetail.setCaseDescription(crowdCase.getDescription());
+                            bugDetail.setCaseRequireDoc("");
+                            bugDetail.setCaseTakeId(report.getCase_take_id());
+                            //cp序列号
+                            bugDetail.setCpSerialNum(cpSerialNum);
+                            bugDetailList.add(bugDetail);
+                        }
+                    }
+                }
+            }
+        }
+        return bugDetailList;
+    }
+
+    public List<BugDetail> saveBugDetail(MultipartFile sourceZipFile, MultipartFile sourceJsonFile, String originalCaseId, String cpSerialNum) {
+        try {
+            //读取文件流并保存在本地
+
+            String zipFilePath= localPrefix +"/xinchuangdata/input/imageZip/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".zip";
+            File zipFile=new File(zipFilePath);
+            if(!zipFile.getParentFile().exists()) { zipFile.getParentFile().mkdirs(); }
+            if(!sourceZipFile.isEmpty()) { sourceZipFile.transferTo(zipFile); }
+            String jsonFilePath= localPrefix +"/xinchuangdata/input/"+originalCaseId+"/"+cpSerialNum+"/"+originalCaseId+".json";
+            File jsonFile=new File(jsonFilePath);
+            if(!jsonFile.getParentFile().exists()) { jsonFile.getParentFile().mkdirs(); }
+            if(!sourceJsonFile.isEmpty()) { sourceJsonFile.transferTo(jsonFile); }
+            //读取本地文件
+            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(jsonFile));
+            ByteArrayOutputStream buf = new ByteArrayOutputStream();
+            int result = bufferedInputStream.read();
+            while (result != -1) {
+                buf.write((byte) result);
+                result = bufferedInputStream.read();
+            }
+            String json = buf.toString();
+            //转为bugDetail
+            List<BugDetail> bugDetailList = JSON.parseArray(json, BugDetail.class);
+            for (BugDetail bugDetail : bugDetailList) {
+                bugDetail.setOriginalCaseId(originalCaseId);
+                //修改图片文件路径为oss路径
+                String imageUrl = bugDetail.getImgUrl();
+                String[] imageUrlArray = imageUrl.split(",");
+                StringBuilder stringBuilder = new StringBuilder();
+                for (String imageUrlStr : imageUrlArray) {
+                    if(!"".equals(imageUrl)) {
+                        String[] filePath = imageUrlStr.split("/");
+                        String fileName = filePath[filePath.length - 1];
+                        String newImageUrl = ossImageUrlPrefix + originalCaseId + "/" + cpSerialNum + "/" + fileName;
+                        stringBuilder.append(newImageUrl).append(",");
+                    }
+                }
+                bugDetail.setImgUrl(stringBuilder.toString());
+                bugDetailDao.save(bugDetail);
+            }
+            //解压图片文件,上传至oss
+            String destPath= localPrefix +"/xinchuangdata/input/imageUnzip/"+originalCaseId+"/"+cpSerialNum;
+            File unzipFile=new File(destPath);
+            if(!unzipFile.getParentFile().exists()) { unzipFile.getParentFile().mkdirs(); }
+            unZip(zipFile,destPath,originalCaseId,cpSerialNum);
+            return bugDetailList;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return new ArrayList<>();
+    }
+
+    private void bugDetailToFile(List<BugDetail> bugDetailList, String caseId) {
+        String[] titles = {"bug_id", "bug_category", "severity", "recurrent", "bug_create_time", "bug_page", "title",
+                "description", "img_url",
+                "score", "parent", "children", "root", "good_num", "good_worker_id", "bad_num", "bad_worker_id",
+                "test_case_id", "test_case_name", "test_case_front", "test_case_behind", "test_case_description", "test_case_create_time",
+                "report_id", "report_name", "report_create_time", "script_location", "report_location", "log_location", "device_model", "device_brand", "device_os",
+                "worker_id",
+                "case_app_name", "case_paper_type", "case_test_type", "case_description", "case_require_doc",
+                "case_take_id", "originalCaseId", "cpSerialNum"};
+        File csvFile = exportCsv(titles, bugDetailList, caseId);
+        File jsonFile = exportJson(bugDetailList, caseId);
+        String csvObjectName = "xinchuang/output/"+caseId+"/"+cpSerialNum+"/" + csvFile.getName();
+        String jsonObjectName = "xinchuang/output/"+caseId+"/"+cpSerialNum+"/" + jsonFile.getName();
+        uploadToOss(csvObjectName,csvFile);
+        uploadToOss(jsonObjectName,jsonFile);
+    }
+
+    private File exportJson(List<BugDetail> bugDetailList, String caseId) {
+        try {
+            File file = new File(localPrefix+"/xinchuangdata/output/" + caseId + ".json");
+            if(!file.getParentFile().exists()) { file.getParentFile().mkdirs(); }
+            JSONArray jsonArray = new JSONArray(bugDetailList);
+            Writer write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
+            write.write(jsonArray.toString());
+            write.flush();
+            write.close();
+            return file;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    private <T> File exportCsv(String[] titles, List<T> list, String caseId) {
+        try {
+            File file = new File(localPrefix+"/xinchuangdata/output/" + caseId + ".csv");
+            if(!file.getParentFile().exists()) { file.getParentFile().mkdirs(); }
+            //构建输出流,同时指定编码
+            OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
+            //csv文件是逗号分隔,除第一个外,每次写入一个单元格数据后需要输入逗号
+            for (String title : titles) {
+                ow.write(title);
+                ow.write(",");
+            }
+            //写完文件头后换行
+            ow.write("\r\n");
+            //写内容
+            for (Object obj : list) {
+                //利用反射获取所有字段
+                Field[] fields = obj.getClass().getDeclaredFields();
+                for (Field field : fields) {
+                    //设置字段可见性
+                    field.setAccessible(true);
+                    //防止某个field没有赋值
+                    if (field.get(obj) == null) {
+                        ow.write("");
+                    } else {
+                        //解决csv文件中对于逗号和双引号的转义问题
+                        ow.write("\"" + field.get(obj).toString().replaceAll("\"", "\"\"") + "\"");
+                    }
+                    ow.write(",");
+                }
+                //写完一行换行
+                ow.write("\r\n");
+            }
+            ow.flush();
+            ow.close();
+            return file;
+        } catch (IOException | IllegalAccessException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private void uploadToOss(String objectName,File file) {
+        if (file != null) {
+            OSS ossClient = OssAliyun.initShangHaiOss();
+            OssAliyun.uploadFile(ossClient, bucketName, objectName, file);
+        } else {
+            System.out.println("file is null");
+        }
+    }
+
+    /**
+     * zip解压
+     *
+     * @param srcFile     zip源文件
+     * @param destDirPath 解压后的目标文件夹
+     * @throws RuntimeException 解压失败会抛出运行时异常
+     */
+
+    private void unZip(File srcFile, String destDirPath,String originalCaseId,String fromCpSerialNum) throws RuntimeException {
+        long start = System.currentTimeMillis();
+        // 判断源文件是否存在
+        if (!srcFile.exists()) {
+            throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
+        }
+        // 开始解压
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(srcFile);
+            Enumeration<?> entries = zipFile.entries();
+
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = (ZipEntry) entries.nextElement();
+                System.out.println("解压" + entry.getName());
+                // 如果是文件夹,就创建个文件夹
+                if (entry.isDirectory()) {
+                    String dirPath = destDirPath + "/" + entry.getName();
+                    File dir = new File(dirPath);
+                    dir.mkdirs();
+                } else {
+                    // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
+                    File targetFile = new File(destDirPath + "/" + entry.getName());
+                    // 保证这个文件的父文件夹必须要存在
+                    if (!targetFile.getParentFile().exists()) {
+                        targetFile.getParentFile().mkdirs();
+                    }
+                    targetFile.createNewFile();
+                    // 将压缩文件内容写入到这个文件中
+                    InputStream is = zipFile.getInputStream(entry);
+                    FileOutputStream fos = new FileOutputStream(targetFile);
+                    int len;
+                    byte[] buf = new byte[BUFFER_SIZE];
+                    while ((len = is.read(buf)) != -1) {
+                        fos.write(buf, 0, len);
+                    }
+                    // 关流顺序,先打开的后关闭
+                    fos.close();
+                    is.close();
+                    //图片文件上传至oss
+                    String objectName = "xinchuang/image/"+originalCaseId+"/"+fromCpSerialNum+"/" + targetFile.getName();
+                    uploadToOss(objectName,targetFile);
+                }
+            }
+            long end = System.currentTimeMillis();
+            System.out.println("解压完成,耗时:" + (end - start) + " ms");
+        } catch (Exception e) {
+            throw new RuntimeException("unzip error from ZipUtils", e);
+        } finally {
+            if (zipFile != null) {
+                try {
+                    zipFile.close();
+
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void toZip(String srcDir, OutputStream out, boolean keepDirStructure)
+            throws RuntimeException {
+        long start = System.currentTimeMillis();
+        ZipOutputStream zos = null;
+        try {
+            zos = new ZipOutputStream(out);
+            File sourceFile = new File(srcDir);
+            compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
+            long end = System.currentTimeMillis();
+            System.out.println("压缩完成,耗时:" + (end - start) + " ms");
+        } catch (Exception e) {
+            throw new RuntimeException("zip error from ZipUtils", e);
+        } finally {
+            if (zos != null) {
+                try {
+                    zos.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * 递归压缩方法
+     *
+     * @param sourceFile       源文件
+     * @param zos              zip输出流
+     * @param name             压缩后的名称
+     * @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
+     *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
+     * @throws Exception
+     */
+    private void compress(File sourceFile, ZipOutputStream zos, String name,
+                          boolean keepDirStructure) throws Exception {
+        byte[] buf = new byte[BUFFER_SIZE];
+        if (sourceFile.isFile()) {
+            // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
+            zos.putNextEntry(new ZipEntry(name));
+            // copy文件到zip输出流中
+            int len;
+            FileInputStream in = new FileInputStream(sourceFile);
+            while ((len = in.read(buf)) != -1) {
+                zos.write(buf, 0, len);
+            }
+            // Complete the entry
+            zos.closeEntry();
+            in.close();
+        } else {
+            File[] listFiles = sourceFile.listFiles();
+            if (listFiles == null || listFiles.length == 0) {
+                // 需要保留原来的文件结构时,需要对空文件夹进行处理
+                if (keepDirStructure) {
+                    // 空文件夹的处理
+                    zos.putNextEntry(new ZipEntry(name + "/"));
+                    // 没有文件,不需要文件的copy
+                    zos.closeEntry();
+                }
+
+            } else {
+                for (File file : listFiles) {
+                    // 判断是否需要保留原来的文件结构
+                    if (keepDirStructure) {
+                        // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
+                        // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
+                        compress(file, zos, name + "/" + file.getName(), true);
+                    } else {
+                        compress(file, zos, file.getName(), false);
+                    }
+                }
+            }
+        }
+    }
+}

+ 55 - 14
src/main/java/edu/nju/util/OssAliyun.java

@@ -22,25 +22,16 @@ package edu.nju.util;
 
 import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
 
 import com.aliyun.oss.ClientException;
 import com.aliyun.oss.OSS;
 import com.aliyun.oss.OSSClientBuilder;
 import com.aliyun.oss.OSSException;
-import com.aliyun.oss.model.Bucket;
 import com.aliyun.oss.model.CannedAccessControlList;
 import com.aliyun.oss.model.CreateBucketRequest;
-import com.aliyun.oss.model.ListBucketsRequest;
-import com.aliyun.oss.model.OSSObject;
-import com.aliyun.oss.model.OSSObjectSummary;
-import com.aliyun.oss.model.ObjectAcl;
-import com.aliyun.oss.model.ObjectListing;
 import com.aliyun.oss.model.PutObjectRequest;
 
 /**
@@ -49,16 +40,64 @@ import com.aliyun.oss.model.PutObjectRequest;
  */
 public class OssAliyun {
 
-    private static String endpoint = "http://oss-cn-shanghai.aliyuncs.com";
+    private static String shangHaiEndpoint = "http://oss-cn-shanghai.aliyuncs.com";
+    private static String hangZhouEndpoint = "http://oss-cn-hangzhou.aliyuncs.com";
     private static String accessKeyId = "LTAI4FdrT3HsfdR5edBVN7ws";
     private static String accessKeySecret = "yroxrpm46DzTyzHrLBZzS3MRNIicP6";
     private static String bucketName = "mooctest-site";
 
+    public static OSS initHangZhouOss(){
+        return new OSSClientBuilder().build(hangZhouEndpoint, accessKeyId, accessKeySecret);
+    }
+
+    public static OSS initShangHaiOss(){
+        return new OSSClientBuilder().build(shangHaiEndpoint, accessKeyId, accessKeySecret);
+
+    }
+
+    public static void uploadFile(OSS ossClient,String bucketName,String objectName,File file){
+        try {
+            /*
+             * Determine whether the bucket exists
+             */
+            if (!ossClient.doesBucketExist(bucketName)) {
+                /*
+                 * Create a new OSS bucket
+                 */
+                ossClient.createBucket(bucketName);
+                CreateBucketRequest createBucketRequest= new CreateBucketRequest(bucketName);
+                createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
+                ossClient.createBucket(createBucketRequest);
+            }
+
+            /*
+             * Upload an object to your bucket
+             */
+            System.out.println("Uploading a new object to OSS from a file\n");
+            ossClient.putObject(new PutObjectRequest(bucketName, objectName, file));
+        } catch (OSSException oe) {
+            System.out.println("Caught an OSSException, which means your request made it to OSS, "
+                    + "but was rejected with an error response for some reason.");
+            System.out.println("Error Message: " + oe.getErrorMessage());
+            System.out.println("Error Code:       " + oe.getErrorCode());
+            System.out.println("Request ID:      " + oe.getRequestId());
+            System.out.println("Host ID:           " + oe.getHostId());
+        } catch (ClientException ce) {
+            System.out.println("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.");
+            System.out.println("Error Message: " + ce.getMessage());
+        } finally {
+            ossClient.shutdown();
+        }
+
+    }
+
     public static void uploadFile(String objectName,File file) throws IOException {
         /*
          * Constructs a client instance with your account for accessing OSS
          */
-        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        OSS ossClient = new OSSClientBuilder().build(shangHaiEndpoint, accessKeyId, accessKeySecret);
         try {
             /*
              * Determine whether the bucket exists
@@ -137,7 +176,7 @@ public class OssAliyun {
         /*
          * Constructs a client instance with your account for accessing OSS
          */
-        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        OSS ossClient = new OSSClientBuilder().build(shangHaiEndpoint, accessKeyId, accessKeySecret);
         try {
             /*
              * Determine whether the bucket exists
@@ -181,7 +220,9 @@ public class OssAliyun {
         BufferedReader reader = new BufferedReader(new InputStreamReader(input));
         while (true) {
             String line = reader.readLine();
-            if (line == null) break;
+            if (line == null) {
+                break;
+            }
 
             System.out.println("    " + line);
         }
@@ -191,7 +232,7 @@ public class OssAliyun {
     }
 
     public static void main(String[]arg){
-        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        OSS ossClient = new OSSClientBuilder().build(shangHaiEndpoint, accessKeyId, accessKeySecret);
         String objectName="paperjson/1492-2612.json";
         try {
             /*

+ 106 - 0
src/main/java/edu/nju/util/TransUtil.java

@@ -0,0 +1,106 @@
+package edu.nju.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @Author JiaWei Xu
+ * @Date 2020-12-25 11:12
+ * @Email xjwhhh233@outlook.com
+ */
+public class TransUtil {
+
+    private static final String UNDETERMINED = "待定";
+    private static final String LIGHTER = "较轻";
+    private static final String NORMAL = "一般";
+    private static final String SERIOUS = "严重";
+    private static final String URGENT = "紧急";
+    private static final String OTHER = "其他";
+    private static final String IRREGULAR = "无规律";
+    private static final String SMALL_PROBABILITY = "小概率复现";
+    private static final String BIG_PROBABILITY = "大概率复现";
+    private static final String CERTAIN = "必现";
+
+
+    public static int severityTransFromString(String str) {
+        if (UNDETERMINED.equals(str)) {
+            return 1;
+        }
+        if (LIGHTER.equals(str)) {
+            return 2;
+        }
+        if (NORMAL.equals(str)) {
+            return 3;
+        }
+        if (SERIOUS.equals(str)) {
+            return 4;
+        }
+        if (URGENT.equals(str)) {
+            return 5;
+        }
+        return 0;
+    }
+
+    public static int recurrentTransFromString(String str) {
+        if (OTHER.equals(str)) {
+            return 1;
+        }
+        if (IRREGULAR.equals(str)) {
+            return 2;
+        }
+        if (SMALL_PROBABILITY.equals(str)) {
+            return 3;
+        }
+        if (BIG_PROBABILITY.equals(str)) {
+            return 4;
+        }
+        if (CERTAIN.equals(str)) {
+            return 5;
+        }
+        return 0;
+    }
+
+    public static String severityTransFromInt(int num) {
+        switch (num) {
+            case 1:
+                return UNDETERMINED;
+            case 2:
+                return LIGHTER;
+            case 3:
+                return NORMAL;
+            case 4:
+                return SERIOUS;
+            case 5:
+                return URGENT;
+            default:
+                return "";
+        }
+    }
+
+    public static String recurrentTransFromInt(int num) {
+        switch (num) {
+            case 1:
+                return OTHER;
+            case 2:
+                return IRREGULAR;
+            case 3:
+                return SMALL_PROBABILITY;
+            case 4:
+                return BIG_PROBABILITY;
+            case 5:
+                return CERTAIN;
+            default:
+                return "";
+        }
+    }
+
+    public static String formatTimeMillis(String millis){
+        Date date = new Date();
+        date.setTime(Long.parseLong(millis));
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
+    }
+
+//    public static void main(String[] args){
+//        formatTimeMillis("1");
+//    }
+}