Explorar o código

Merge branch 'private-conflict' into 'PrivateReleaseWithRelease'

Private conflict



See merge request !1357

guochao %!s(int64=4) %!d(string=hai) anos
pai
achega
4e014a72a9
Modificáronse 42 ficheiros con 1075 adicións e 406 borrados
  1. 11 5
      mooctest-site-server/pom.xml
  2. 1 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/configure/MongoDBConfiguration.java
  3. 0 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/CaseExtendsDao.java
  4. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/GradeGeneralDao.java
  5. 11 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/Group2WorkerDao.java
  6. 10 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/AssignedCase.java
  7. 11 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/CasePDF.java
  8. 3 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/CatchService.java
  9. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/GroupService.java
  10. 17 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/UploadRecordService.java
  11. 274 126
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/common/impl/PdfServiceImpl.java
  12. 49 4
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/CatchServiceImpl.java
  13. 13 16
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/GroupServiceImpl.java
  14. 1 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ManagerPropertyServiceImpl.java
  15. 16 10
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/SubmitRecordServiceImpl.java
  16. 26 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/UploadRecordServiceImpl.java
  17. 26 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/util/DateUtil/DateUtil.java
  18. 23 14
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/CaseController.java
  19. 0 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/TestController.java
  20. 3 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/fromKibug/ScoreRuleController.java
  21. 11 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/AssignedCaseVO.java
  22. 7 1
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/AssignedTaskVO.java
  23. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/MenuVO.java
  24. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/SimilarityResultVO.java
  25. 0 4
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/forMongo/ReportForMongoDTO.java
  26. 26 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/forMongo/SimilarityResultDTO.java
  27. 21 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/forMongo/SimilarityResultExtendsDTO.java
  28. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/AssignedCaseVOWrapper.java
  29. 32 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/SimilarityResultVOWrapper.java
  30. 17 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/exception/ResultNotFoundException.java
  31. 3 2
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/CaseLogic.java
  32. 3 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/SimilarityLogic.java
  33. 15 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/exception/CaseNotFoundException.java
  34. 71 107
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/CaseLogicImpl.java
  35. 17 11
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/GroupLogicImpl.java
  36. 19 43
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/MenuLogicImpl.java
  37. 210 46
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/SimilarityLogicImpl.java
  38. 61 0
      mooctest-site-server/src/test/groovy/MenuLogicImplTest.groovy
  39. 4 1
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/GroupServiceTest.java
  40. 5 2
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/ctrl/CaseControllerTest.java
  41. 2 0
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/ctrl/fromKibug/ScoreRuleControllerTest.java
  42. 48 3
      mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/logic/impl/CaseLogicImplTest.java

+ 11 - 5
mooctest-site-server/pom.xml

@@ -481,11 +481,17 @@
             <artifactId>spring-boot-starter-log4j2</artifactId>
             <version>${spring.boot.version}</version>
         </dependency>
-<!--        <dependency>-->
-<!--            <groupId>org.springframework.cloud</groupId>-->
-<!--            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
-<!--            <version>0.9.0.RELEASE</version>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-core</artifactId>
+            <version>2.0-M3-groovy-3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
+            <version>3.0.4</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 1 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/configure/MongoDBConfiguration.java

@@ -28,5 +28,5 @@ public class MongoDBConfiguration {
     private String timeAnalysisCollection;
     private String targetGraphCollection;
     private String base64Auth;
-
+    private String similarityResultCollection;
 }

+ 0 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/CaseExtendsDao.java

@@ -23,7 +23,6 @@ public interface CaseExtendsDao extends PagingAndSortingRepository<CaseExtends,
             "where target.id=case_.app_id;")
     List<Object[]> getCaseInfoById(@Param("caseId") long caseId);
 
-
     @Query(value="SELECT `name` from case_entity WHERE id IN (:caseId)",nativeQuery = true)
     List<String> findCaseName(@Param("caseId") List<Long> caseId);
 }

+ 2 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/GradeGeneralDao.java

@@ -47,4 +47,6 @@ public interface GradeGeneralDao extends JpaRepository<GradeGeneral,Long>, Updat
   GradeGeneral findByUploadIdAndType(long uploadId, String type);
 
   List<GradeGeneral> findByUploadId(long uploadId);
+
+  List<GradeGeneral> findByUploadIdIn(List<Long> uploadIds);
 }

+ 11 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/Group2WorkerDao.java

@@ -1,13 +1,14 @@
 package cn.iselab.mooctest.site.dao;
 
 import cn.iselab.mooctest.site.models.Group2Worker;
-import cn.iselab.mooctest.site.models.GroupWorkerCount;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.data.repository.query.Param;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigInteger;
 import java.util.List;
 
 /**
@@ -27,4 +28,13 @@ public interface Group2WorkerDao extends CrudRepository<Group2Worker, Long>,Pagi
 
     @Query(value = "select group_id as groupId,count(group_id) as workersCount from group_2_worker group by group_id", nativeQuery = true)
     List<Object[]> getGroupWorkerCount();
+
+    @Query(value = "SELECT\n" +
+            " IFNULL( gw.count1, 0 ) \n" +
+            "FROM\n" +
+            " group_entity AS ge\n" +
+            " LEFT JOIN ( SELECT group_id, count( group_id ) AS count1 FROM group_2_worker GROUP BY group_id ) AS gw ON ge.id = gw.group_id \n" +
+            "WHERE\n" +
+            " ge.id IN (:groupIds) ORDER BY ge.id DESC", nativeQuery = true)
+    List<BigInteger> countGroupId(@Param("groupIds") List<Long> groupIds);
 }

+ 10 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/AssignedCase.java

@@ -18,6 +18,8 @@ public class AssignedCase {
 
     private List<Result> results;
 
+    private  Integer  uploadTimes;
+
     public List<Result> getResults() {
         return results;
     }
@@ -57,4 +59,12 @@ public class AssignedCase {
     public void setCaseName(String caseName) {
         this.caseName = caseName;
     }
+
+    public Integer getUploadTimes() {
+        return uploadTimes;
+    }
+
+    public void setUploadTimes(Integer uploadTimes) {
+        this.uploadTimes = uploadTimes;
+    }
 }

+ 11 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/data/CasePDF.java

@@ -1,11 +1,15 @@
 package cn.iselab.mooctest.site.data;
 
+import cn.iselab.mooctest.site.models.GradeGeneral;
+import cn.iselab.mooctest.site.models.UploadRecord;
 import cn.iselab.mooctest.site.web.data.forMongo.ReportForMongoDTO;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.poi.ss.formula.functions.T;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @Author ROKG
@@ -31,4 +35,11 @@ public class CasePDF {
     private ReportForMongoDTO report;
 
     private List<String> answerCode;
+
+
+    //提每道题的提记录列表。
+    private   List<UploadRecord> uploadRecordList;
+
+
+    private Map<Long,Double> scoreMap;
 }

+ 3 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/CatchService.java

@@ -3,6 +3,7 @@ package cn.iselab.mooctest.site.service;
 import cn.iselab.mooctest.site.web.data.forMongo.NodeCatch.CatchDTO;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author sean
@@ -15,4 +16,6 @@ public interface CatchService {
     List<CatchDTO> findById(String id);
 
     String pushCatchDTOs(List<CatchDTO> catchDTOList);
+
+    Map<Long, List<CatchDTO>> findByIds(Map<String, Long> lastIdAndUserIdMap);
 }

+ 1 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/GroupService.java

@@ -6,6 +6,7 @@ import cn.iselab.mooctest.site.models.User;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
+import java.math.BigInteger;
 import java.util.List;
 import java.util.Map;
 

+ 17 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/UploadRecordService.java

@@ -0,0 +1,17 @@
+package cn.iselab.mooctest.site.service;
+
+import cn.iselab.mooctest.site.data.UploadRecordDTO;
+import cn.iselab.mooctest.site.models.UploadRecord;
+
+import java.util.List;
+
+/**
+ * @author xx
+ * @version 1.0
+ * @className UploadRecordService
+ * @deacription TODO
+ * @date 2020/11/2 13:33
+ **/
+public interface UploadRecordService {
+    List<UploadRecord> findByCaseId(long workerId,long examId, long caseId );
+}

+ 274 - 126
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/common/impl/PdfServiceImpl.java

@@ -12,6 +12,7 @@ import cn.iselab.mooctest.site.service.common.MongoAPIService;
 import cn.iselab.mooctest.site.service.common.PdfService;
 import cn.iselab.mooctest.site.service.fromKibug.CaseTakeService;
 import cn.iselab.mooctest.site.service.updownload.DownloadService;
+import cn.iselab.mooctest.site.util.DateUtil.DateUtil;
 import cn.iselab.mooctest.site.util.MD5Util.MD5Util;
 import cn.iselab.mooctest.site.util.data.PdfUtils;
 import cn.iselab.mooctest.site.web.data.forMongo.BugDTO;
@@ -19,6 +20,7 @@ import cn.iselab.mooctest.site.web.data.forMongo.ReportForMongoDTO;
 import cn.iselab.mooctest.site.web.data.fromKibug.ReportCaseVO;
 import cn.iselab.mooctest.site.web.exception.HttpBadRequestException;
 import cn.iselab.mooctest.site.web.logic.fromDev.PluginLogic;
+import cn.iselab.mooctest.site.web.logic.fromDev.UpDownloadLogic;
 import com.itextpdf.text.*;
 import com.itextpdf.text.pdf.*;
 import net.lingala.zip4j.core.ZipFile;
@@ -28,6 +30,7 @@ import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.rendering.PDFRenderer;
 import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.assertj.core.util.Lists;
 import org.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -57,8 +60,18 @@ public class PdfServiceImpl extends BaseService implements PdfService {
     public static String SAVE_PATH = "/var/www/download/";
     public static String PDF_PATH = "pdf/";
 
+
+    @Autowired
+    GradeGeneralDao gradeGeneralDao;
+    @Autowired
+    GeneralCalculateScoreService generalCalculateScoreService;
+
     @Autowired
     ExamService examService;
+    @Autowired
+    private UploadRecordService uploadRecordService;
+    @Autowired
+    WeightGeneralDao weightGeneralDao;
 
     @Autowired
     PluginLogic pluginLogic;
@@ -111,39 +124,12 @@ public class PdfServiceImpl extends BaseService implements PdfService {
     @Override
     public ReportPDF getReportPDF(Long examId, Long workerId) throws Exception {
         ReportPDF reportPDF = new ReportPDF();
-        Exam exam = examService.getTask(examId);
-        if (exam == null) {
-            reportPDF.setFailReason("exam  not exist");
-            return  reportPDF;
-
-        }
-        reportPDF.setExamName(exam.getName());
-        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
-        reportPDF.setDateTime(exam.getBeginTime() + "-" + format.format(exam.getEndTime()));
-        UserDTOForMT user = userService.findByUserId(workerId);
-        if (user == null) {
-            reportPDF.setFailReason("user not  exist");
-            return reportPDF;
-        }
-        reportPDF.setWorkerName(user.getName());
-        reportPDF.setSchool(user.getSchool());
-        List<Group> groups = groupService.getByExamId(exam.getId());
-        if (groups.isEmpty()) {
-           reportPDF.setFailReason("group not exits");
-            return reportPDF;
-        }else if(!groups.isEmpty()) {
-            reportPDF.setGroupName(groups.get(0).getName());
-        }
-        UserDTOForMT teacher = userService.findByUserId(exam.getOwnerId());
-        if (teacher == null) {
-           reportPDF.setFailReason("teacher not exits");
-            return reportPDF;
-        }
-        reportPDF.setTeacherName(teacher.getName());
+        //校验本场考试信息
+        verfiyParamter(reportPDF, examId, workerId);
         SubmitRecord submitRecord = submitRecordService.getAssignedTask(examId, workerId);
-        if (submitRecord == null) {
-           reportPDF.setFailReason("score not exits");
-            return reportPDF;
+
+        if (submitRecord == null || submitRecord.getScore() == null ||"".equals(submitRecord.getScore())) {
+            throw new HttpBadRequestException("score not exits");
         }
          /*
          成绩保留两位小数。
@@ -152,55 +138,144 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         instance.setMaximumFractionDigits(2);
         reportPDF.setScore(instance.format(submitRecord.getScore()));
 
+
         List<SubmitRecord> submitRecords = submitRecordService.getAssignedTasks(examId);
         List<Double> scores = calculateScoreService.calculateScores(submitRecords.stream().map(assignedTask1 -> assignedTask1.getScore()).collect(Collectors.toList()));
-        reportPDF.setMax(scores.get(0).toString());
-        reportPDF.setMin(scores.get(1).toString());
-        reportPDF.setAvg(scores.get(2).toString());
-        reportPDF.setVariance(scores.get(3).toString());
+        //初始化pdf的得分
+        initPDF(reportPDF, scores);
 
         List<CasePDF> pdfs = new ArrayList<>();
         List<AssignedCase> cases = submitRecordService.wrapAssignedCases(submitRecord);
-        //List<Long> caseIds=cases.stream().map(casse-> casse.getCaseId()).collect(Collectors.toList());
+        List<Long> caseIds = cases.stream().map(casse -> casse.getCaseId()).collect(Collectors.toList());
         List<CaseBlock> caseBlocks = exam2CaseService.getCasesByExamId(examId);
-        for (CaseBlock caseBlock : caseBlocks) {
-            if (caseBlock.getCaseIds().size() < 1) {
-                continue;
-            }
-            long caseId = caseBlock.getCaseIds().get(0);
+        for (long caseId : caseIds) {
             CasePDF pdf = new CasePDF();
+            List<Double> scoreByUpload = new ArrayList<>();
+            Map<Long, List<GradeGeneral>> preScoreDetail = new HashMap<>();
             CaseExtends caseExtends = caseService.getCaseExtendsById(caseId);
+            //获取当前用户当前case的所有提交记录
+            List<UploadRecord> uploadRecords = uploadRecordService.findByCaseId(workerId, examId, caseId);
+            //每一个case上传的所有id
+            List<Long> uploadIds = uploadRecords.stream().map(UploadRecord::getId).collect(Collectors.toList());//130-132
+            //所有提交的所有类型的得分。
+            List<GradeGeneral> gradeGenerals = gradeGeneralDao.findByUploadIdIn(uploadIds);
+            Map<Long, List<GradeGeneral>> collect = gradeGenerals.stream().collect(Collectors.groupingBy(GradeGeneral::getUploadId));
+
+            //得分规则
+            List<WeightGeneral> weightGeneralList = weightGeneralDao.findByExamIdAndCaseId(examId, caseId);
+            //计算每一次提交的所有得分详情
+            calculateAllScoresByUploadId(uploadIds, collect, preScoreDetail);
+            Map<Long, Double> finalScoreMap = new HashMap<>();
+            double finalScore = 0;
+            //每一次提交所有得分map
+            setFinalScoreMap(preScoreDetail, uploadIds, finalScoreMap, weightGeneralList);
+            pdf.setScoreMap(finalScoreMap);
+
+
+            //每一个case的提交列表
+            if (uploadRecords != null && uploadRecords.size() != 0) {
+                pdf.setUploadRecordList(uploadRecords);
+            }
+
             pdf.setCaseName(caseExtends.getName());
             pdf.setDescription(caseExtends.getDescription());
-            for (AssignedCase assignedCase : cases) {
-                if (assignedCase.getCaseId() == caseId) {
-                    DecimalFormat decimalFormat=new DecimalFormat("######0.00");
-                    pdf.setCaseScore(String.valueOf(decimalFormat.format(assignedCase.getMaxScore())));
-                }
+            wrapPDF(caseId, cases, pdf, examId, workerId, caseExtends);
+            pdfs.add(pdf);
+        }
+        reportPDF.setCases(pdfs);
+        return reportPDF;
+    }
+
+    private void wrapPDF(Long caseId, List<AssignedCase> cases, CasePDF pdf, Long examId, Long workerId, CaseExtends caseExtends) throws Exception {
+        for (AssignedCase assignedCase : cases) {
+            if (assignedCase.getCaseId() == caseId) {
+                DecimalFormat decimalFormat = new DecimalFormat("######0.00");
+                pdf.setCaseScore(String.valueOf(decimalFormat.format(assignedCase.getMaxScore())));
             }
             if (caseExtends.getAnswerWay() == AnswerWayConstants.REPORT)
                 pdf.setReport(this.getReport(examId, workerId, caseId));
             if (caseExtends.getAnswerWay() == AnswerWayConstants.DEV_ECLIPSE) {
-                String downloadUrl=pluginLogic.getAnalysisSignature(examId,workerId,caseExtends.getName());
+                String downloadUrl = pluginLogic.getAnalysisSignature(examId, workerId, caseExtends.getName());
                 pdf.setAnswerCode(this.getResultCode(downloadUrl));
             }
-            pdfs.add(pdf);
         }
-        reportPDF.setCases(pdfs);
-        return reportPDF;
     }
 
-    @Override
-    public void generatePDF(ReportPDF pdf, String fileName) throws Exception {
-        PdfReader reader = new PdfReader(templatePdfPath);
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        PdfStamper ps = new PdfStamper(reader, bos);
+    //所有提交的总map
+    private void setFinalScoreMap(Map<Long, List<GradeGeneral>> preScoreDetail, List<Long> uploadIds, Map<Long, Double> finalScoreMap, List<WeightGeneral> weightGeneralList) {
+
+
+        for (int i = 0; i < preScoreDetail.size(); i++) {
+            double finalScore = 0;
+            Map<String, Double> typeScore = new HashMap<>();
+            if (preScoreDetail.get(uploadIds.get(i)) != null) {
+                for (GradeGeneral gradeGeneral : preScoreDetail.get(uploadIds.get(i))) {
+
+                    typeScore.put(gradeGeneral.getType(), gradeGeneral.getScore());
+
+                }
+                for (WeightGeneral weightGeneral : weightGeneralList) {
+                    if (typeScore.get(weightGeneral.getType()) == null) {
+                        finalScore += 0;
+                    } else {
+                        finalScore += typeScore.get(weightGeneral.getType()) * weightGeneral.getWeight() / 100;
+                    }
+                    finalScoreMap.put(uploadIds.get(i), finalScore);
+                }
+            }
+        }
+    }
+
+    //计算每一次提交的所有得分
+    private void calculateAllScoresByUploadId(List<Long> uploadIds, Map<Long, List<GradeGeneral>> collect, Map<Long, List<GradeGeneral>> preScoreDetail) {
+        for (int i = 0; i < uploadIds.size(); i++) {
+            List<GradeGeneral> gradeGeneralList = new ArrayList<>();
+            gradeGeneralList = collect.get(uploadIds.get(i));
+            preScoreDetail.put(uploadIds.get(i), gradeGeneralList);
+        }
+    }
 
-        ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
-        fontList.add(PdfUtils.getBaseFont());
-        AcroFields fields = ps.getAcroFields();
-        fields.setSubstitutionFonts(fontList);
 
+    //设置 成绩最大值,最小值,方差
+    private void initPDF(ReportPDF reportPDF, List<Double> scores) {
+        reportPDF.setMax(scores.get(0).toString());
+        reportPDF.setMin(scores.get(1).toString());
+        reportPDF.setAvg(scores.get(2).toString());
+        reportPDF.setVariance(scores.get(3).toString());
+
+    }
+
+    //校验考试 ,用户,班级,教师信息。
+    private void verfiyParamter(ReportPDF reportPDF, Long examId, Long workerId) {
+
+        Exam exam = examService.getTask(examId);
+        if (exam == null) {
+            throw new HttpBadRequestException("exam not exits");
+        }
+        reportPDF.setExamName(exam.getName());
+        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
+        reportPDF.setDateTime(exam.getBeginTime() + "-" + format.format(exam.getEndTime()));
+        UserDTOForMT user = userService.findByUserId(workerId);
+        if (user == null) {
+            throw new HttpBadRequestException("user not exits");
+        }
+        reportPDF.setWorkerName(user.getName());
+        reportPDF.setSchool(user.getSchool());
+        List<Group> groups = groupService.getByExamId(exam.getId());
+        if (groups.isEmpty()) {
+            throw new HttpBadRequestException("group not exits");
+        } else if (!groups.isEmpty()) {
+            reportPDF.setGroupName(groups.get(0).getName());
+        }
+        UserDTOForMT teacher = userService.findByUserId(exam.getOwnerId());
+        if (teacher == null) {
+            throw new HttpBadRequestException("teacher not  exits");
+        }
+        reportPDF.setTeacherName(teacher.getName());
+    }
+
+    //pdf上面的数据
+    private void copyPropertiesFromPdf(AcroFields fields, ReportPDF pdf) throws IOException, DocumentException {
         fields.setField("examName", pdf.getExamName());
         fields.setField("workerName", pdf.getWorkerName());
         fields.setField("school", pdf.getSchool());
@@ -213,6 +288,19 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         fields.setField("avg", pdf.getAvg());
         fields.setField("variance", pdf.getVariance());
 
+    }
+
+    @Override
+    public void generatePDF(ReportPDF pdf, String fileName) throws Exception {
+        PdfReader reader = new PdfReader(templatePdfPath);
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        PdfStamper ps = new PdfStamper(reader, bos);
+        ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
+        fontList.add(PdfUtils.getBaseFont());
+        AcroFields fields = ps.getAcroFields();
+        fields.setSubstitutionFonts(fontList);
+
+
         //插入全班成绩柱状图
         String barUrl;
         if (pdf.getBarGraph() != null) {
@@ -229,10 +317,9 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         ps.setFormFlattening(true);
         ps.close();
 //       根据学生姓名和文件名进行MD5加密
+        String md5_fileName = MD5Util.string2MD5(fileName);
 
-        String md5_fileName=MD5Util.string2MD5(fileName);
-
-        FileOutputStream fos = new FileOutputStream(FileUtils.getUserDirectory() + "/" +md5_fileName+ "1.pdf");
+        FileOutputStream fos = new FileOutputStream(FileUtils.getUserDirectory() + "/" + md5_fileName + "1.pdf");
         //方法一,生成的pdf文件较大
 //        fos.write(bos.toByteArray());
 //        fos.close();
@@ -248,28 +335,79 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         fos.close();
 
         Document document = new Document();
-        FileOutputStream out = new FileOutputStream(FileUtils.getUserDirectory() +"/" +md5_fileName + "2.pdf");
+        FileOutputStream out = new FileOutputStream(FileUtils.getUserDirectory() + "/" + md5_fileName + "2.pdf");
         PdfWriter writer = PdfWriter.getInstance(document, out);
         document.open();
-
         List<CasePDF> casePDFS = pdf.getCases();
+        System.out.println(casePDFS.size() + "mmmmmmmmmmmm");
         if (casePDFS != null) {
             for (CasePDF casePDF : casePDFS) {
+
                 document.newPage();
                 Paragraph paragraph = new Paragraph("题目" + (pdf.getCases().indexOf(casePDF) + 1), PdfUtils.getTitle());
                 paragraph.setLeading(22f);
                 document.add(paragraph);
                 document.add(new Paragraph("案例名称:" + casePDF.getCaseName() + "    案例分数:" + casePDF.getCaseScore(), PdfUtils.getContent()));
-                document.add(new Paragraph("案例描述:" + (casePDF.getDescription()!=null?casePDF.getDescription():"无"), PdfUtils.getContent()));
-                //生成本题学生的提交内容
-                generateCase(casePDF,document);
+                document.add(new Paragraph("案例描述:" + (casePDF.getDescription() != null ? casePDF.getDescription() : "无"), PdfUtils.getContent()));
+                generateCase(casePDF, document);
+                PdfPTable table = renderTable(casePDF);
+                document.add(new Paragraph("提交记录:", PdfUtils.getContent()));
+                document.add(Chunk.NEWLINE);
+                //提交记录表格 每一列的宽度 共3列
+                document.add(table);
             }
         }
         document.close();
-        String[] files = {FileUtils.getUserDirectory() + "/"+md5_fileName + "1.pdf", FileUtils.getUserDirectory() + "/"+md5_fileName + "2.pdf"};
-        mergePDFS(files, SAVE_PATH + PDF_PATH + "/" + md5_fileName + ".pdf");
+        String[] files = {String.format("%s/%s1.pdf", FileUtils.getUserDirectory(), md5_fileName), String.format("%s/%s2.pdf", FileUtils.getUserDirectory(), md5_fileName)};
+        mergePDFS(files, String.format("%s%s/%s.pdf", SAVE_PATH, PDF_PATH, md5_fileName));
     }
 
+    //生成表格
+    private PdfPTable renderTable(CasePDF casePDF) throws IOException, DocumentException {
+        float[] widths = {144, 113, 144};
+        PdfPTable table = new PdfPTable(widths);
+               /*
+                提交记录表格第一行的数据 因为第一行数据固定 所以 后面增加了不是第一行的判断。
+                //每道题的提交次数
+                */
+
+        String uploadTime = "";
+        int uploadTimesCount = casePDF.getUploadRecordList().size();
+        Object[][] datas = new Object[uploadTimesCount + 1][3];
+        datas[0][0] = "提交次序";
+        datas[0][1] = "score";
+        datas[0][2] = "提交时间";
+        int columnWidth = 3;//表格的列数
+        for (int i = 0; i < uploadTimesCount + 1; i++) {
+            if (i != 0) {
+                uploadTime = DateUtil.timeStamp2Date(casePDF.getUploadRecordList().get(i - 1).getUploadTime());
+                datas[i][2] = uploadTime;
+                if (casePDF.getScoreMap().get(casePDF.getUploadRecordList().get(i - 1).getId()) != null) {
+                    datas[i][1] = casePDF.getScoreMap().get(casePDF.getUploadRecordList().get(i - 1).getId());
+                } else {
+                    datas[i][1] = "暂无分数信息";
+                }
+            }
+
+            for (int j = 0; j < columnWidth; j++) {
+
+                PdfPCell pdfCell = new PdfPCell(); //表格的单元格
+                if (j == 0 && i != 0) {
+                    datas[i][j] = String.format("第%d次提交", i);
+                }
+                System.out.println(datas[i][j] + "hhhhhhhhhh");
+                Paragraph submit_paragraph = new Paragraph((datas[i][j]).toString(), getPdfChineseFont());
+                pdfCell.setPhrase(submit_paragraph);
+                table.addCell(pdfCell);
+            }
+
+
+        }
+
+        return table;
+    }
+
+
     @Override
     public void generateReportCase(ReportCaseVO vo, Document document) throws Exception {
         PdfPTable table = new PdfPTable(2);
@@ -419,7 +557,7 @@ public class PdfServiceImpl extends BaseService implements PdfService {
 
     private ReportForMongoDTO getReport(long examId, long participantId, long caseId) throws Exception {
         CaseTake caseTake = CaseTakeService.getCaseTake(examId, caseId, participantId);
-        if(caseTake==null){
+        if (caseTake == null) {
             return null;
         }
         List<ReportForMongoDTO> reports = mongoAPIService.getReportFromMongo(caseTake.getId());
@@ -473,7 +611,7 @@ public class PdfServiceImpl extends BaseService implements PdfService {
             CaseExtends caseExtends = caseService.getCaseExtendsById(caseId);
             casePDF.setCaseName(caseExtends.getName());
             casePDF.setDescription(caseExtends.getDescription());
-            getCaseContent(caseExtends,casePDF);
+            getCaseContent(caseExtends, casePDF);
             casePDFs.add(casePDF);
         }
         examPDF.setCasePDFs(casePDFs);
@@ -550,10 +688,11 @@ public class PdfServiceImpl extends BaseService implements PdfService {
             over.addImage(img);
         }
 
+
         ps.setFormFlattening(true);
         ps.close();
-        String md5_fileName=MD5Util.string2MD5(fileName);
-        FileOutputStream fos = new FileOutputStream(FileUtils.getUserDirectory() + md5_fileName+ "1.pdf");
+        String md5_fileName = MD5Util.string2MD5(fileName);
+        FileOutputStream fos = new FileOutputStream(FileUtils.getUserDirectory() + md5_fileName + "1.pdf");
         Document doc = new Document(PageSize.A4);
         PdfCopy copy = new PdfCopy(doc, fos);
         doc.open();
@@ -577,15 +716,15 @@ public class PdfServiceImpl extends BaseService implements PdfService {
                 paragraph.setLeading(22f);
                 document.add(paragraph);
                 document.add(new Paragraph("案例名称:" + casePDF.getCaseName(), PdfUtils.getContent()));
-                document.add(new Paragraph("案例描述:" + (casePDF.getDescription()==null?"无":casePDF.getDescription()), PdfUtils.getContent()));
-                if(casePDF.getContent()!=null){
+                document.add(new Paragraph("案例描述:" + (casePDF.getDescription() == null ? "无" : casePDF.getDescription()), PdfUtils.getContent()));
+                if (casePDF.getContent() != null) {
                     document.add(new Paragraph("案例内容:" + casePDF.getContent(), PdfUtils.getContent()));
-                }else if(casePDF.getCodes()!=null&&casePDF.getCodes().size()>0){
+                } else if (casePDF.getCodes() != null && casePDF.getCodes().size() > 0) {
                     document.add(new Paragraph("案例内容:", PdfUtils.getContent()));
-                    List<String> codes=casePDF.getCodes();
-                    for(String str:codes){
-                        document.add(new Paragraph("类"+(codes.indexOf(str)+1), PdfUtils.getContent()));
-                        Paragraph paragraph1=new Paragraph(str,PdfUtils.getCode());
+                    List<String> codes = casePDF.getCodes();
+                    for (String str : codes) {
+                        document.add(new Paragraph("类" + (codes.indexOf(str) + 1), PdfUtils.getContent()));
+                        Paragraph paragraph1 = new Paragraph(str, PdfUtils.getCode());
                         paragraph.setIndentationLeft(25);
                         document.add(paragraph1);
                     }
@@ -599,15 +738,15 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         for (Long participantId : participantIds) {
             ReportPDF reportPDF;
             try {
-                reportPDF = this.getReportPDF(examId, participantId );
-            }catch (Exception e){
+                reportPDF = this.getReportPDF(examId, participantId);
+            } catch (Exception e) {
                 e.printStackTrace();
                 continue;
             }
             if (casePDFS != null) {
                 document.newPage();
                 Paragraph paragraph1 = new Paragraph("编号:" + participantIds.indexOf(participantId) + "    考生姓名:" + userDao.findOne(participantId).getName()
-                        + "    考试成绩:" + reportPDF.getScore() + " , " + getRank(examId, participantId),PdfUtils.getTitle());
+                        + "    考试成绩:" + reportPDF.getScore() + " , " + getRank(examId, participantId), PdfUtils.getTitle());
                 document.add(paragraph1);
                 document.add(new Paragraph(18f, " "));
                 for (CasePDF casePDF : reportPDF.getCases()) {
@@ -616,42 +755,41 @@ public class PdfServiceImpl extends BaseService implements PdfService {
                     document.add(paragraph);
 
                     document.add(new Paragraph("案例名称:" + casePDF.getCaseName() + "    案例分数:" + casePDF.getCaseScore(), PdfUtils.getContent()));
-                    document.add(new Paragraph("案例描述:" + (casePDF.getDescription()==null?"无":casePDF.getDescription()), PdfUtils.getContent()));
+                    document.add(new Paragraph("案例描述:" + (casePDF.getDescription() == null ? "无" : casePDF.getDescription()), PdfUtils.getContent()));
                     //生成本题学生的提交内容
-                    generateCase(casePDF,document);
+                    generateCase(casePDF, document);
                 }
             }
         }
         document.close();
-        String[] files = {FileUtils.getUserDirectory() + "/" + md5_fileName+"1.pdf", FileUtils.getUserDirectory() + "/" +md5_fileName+ "2.pdf"};
+        String[] files = {FileUtils.getUserDirectory() + "/" + md5_fileName + "1.pdf", FileUtils.getUserDirectory() + "/" + md5_fileName + "2.pdf"};
         mergePDFS(files, SAVE_PATH + PDF_PATH + "/" + fileName + ".pdf");
     }
 
     /**
-     *
      * @param pdfUrl pdf路径
-     * @param type 图片保存格式 png/jpg ...
+     * @param type   图片保存格式 png/jpg ...
      * @return
      */
     @Override
     public String pdf2imageList(String pdfUrl, String type) {
         // 将pdf装图片 并且自定义图片得格式大小
         File file = new File(pdfUrl);
-        return (String)this.pdf2imageList(file,type).get("url");
+        return (String) this.pdf2imageList(file, type).get("url");
     }
 
     @Override
     public Map<String, Object> pdf2imageList(File file, String type) {
-        String tmpPath = System.getProperty("java.io.tmpdir")+File.separator;
+        String tmpPath = System.getProperty("java.io.tmpdir") + File.separator;
         String result_path = tmpPath;
-        String parentPath = "mooctest." + UUID.randomUUID().toString().replaceAll("-","");
+        String parentPath = "mooctest." + UUID.randomUUID().toString().replaceAll("-", "");
         String imagesParentPath = result_path + parentPath + File.separator;
         File dir = new File(imagesParentPath);
-        if(!dir.exists()) {
+        if (!dir.exists()) {
             dir.mkdirs();
         }
         HashMap<String, Object> result = new HashMap<>();
-        result.put("url",imagesParentPath);
+        result.put("url", imagesParentPath);
         try {
             PDDocument doc = PDDocument.load(file);
             PDFRenderer renderer = new PDFRenderer(doc);
@@ -660,7 +798,7 @@ public class PdfServiceImpl extends BaseService implements PdfService {
             for (int i = 0; i < pageCount; i++) {
                 BufferedImage image = renderer.renderImageWithDPI(i, 80); // Windows native DPI
                 // BufferedImage srcImage = resize(image, 240, 240);//产生缩略图
-                ImageIO.write(image, type, new File(imagesParentPath + (i+1) +"."+type));
+                ImageIO.write(image, type, new File(imagesParentPath + (i + 1) + "." + type));
             }
             doc.close();
         } catch (IOException e) {
@@ -669,16 +807,18 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         return result;
     }
 
-    private void generateCase(CasePDF casePDF, Document document) throws Exception{
+    private void generateCase(CasePDF casePDF, Document document) throws Exception {
         ReportForMongoDTO report = casePDF.getReport();
         //如果case的answerWay为report,则将测试用例和缺陷报告写入pdf中
         if (report != null) {
             if (report.getBugVOS() == null && report.getReportCases() == null) {
                 document.add(new Paragraph("考试结果:请下载zip文件包查看报告中的测试脚本等附件。", PdfUtils.getContent()));
+
             } else {
                 document.add(new Paragraph("考试结果:", PdfUtils.getContent()));
             }
             document.add(new Paragraph("测试用例:", PdfUtils.getContent()));
+
             List<ReportCaseVO> caseVOS = report.getReportCases();
             if (caseVOS != null) {
                 for (ReportCaseVO vo : caseVOS) {
@@ -692,11 +832,11 @@ public class PdfServiceImpl extends BaseService implements PdfService {
                     generateBug(dto, document);
                 }
             }
-        }else if(casePDF.getAnswerCode()!=null&&casePDF.getAnswerCode().size()>0){
+        } else if (casePDF.getAnswerCode() != null && casePDF.getAnswerCode().size() > 0) {
             document.add(new Paragraph("考试结果:", PdfUtils.getContent()));
-            for(String str:casePDF.getAnswerCode()){
-                document.add(new Paragraph("类"+(casePDF.getAnswerCode().indexOf(str)+1)+":",PdfUtils.getContent()));
-                Paragraph paragraph=new Paragraph(str,PdfUtils.getCode());
+            for (String str : casePDF.getAnswerCode()) {
+                document.add(new Paragraph("类" + (casePDF.getAnswerCode().indexOf(str) + 1) + ":", PdfUtils.getContent()));
+                Paragraph paragraph = new Paragraph(str, PdfUtils.getCode());
                 paragraph.setIndentationLeft(25);
                 document.add(paragraph);
             }
@@ -717,7 +857,7 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         String path;
         try {
             path = downloadService.download(downloadUrl);
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
             return null;
         }
@@ -752,31 +892,31 @@ public class PdfServiceImpl extends BaseService implements PdfService {
                     , projectDirectory.getAbsolutePath()), e);
             return null;
         }
-        List<String> codes=new ArrayList<>();
-        getJavaCode(new File(projectDirectory.getPath()),codes);
+        List<String> codes = new ArrayList<>();
+        getJavaCode(new File(projectDirectory.getPath()), codes);
         FileUtils.deleteQuietly(projectDirectory);
         FileUtils.deleteQuietly(extractDirectory);
         return codes;
     }
 
-    private void getJavaCode(File projectFile,List<String> codes){
-        File[] files=projectFile.listFiles();
-        if(files==null||files.length==0)
+    private void getJavaCode(File projectFile, List<String> codes) {
+        File[] files = projectFile.listFiles();
+        if (files == null || files.length == 0)
             return;
-        for(File file:files){
-            if(file.isDirectory()){
-                getJavaCode(file,codes);
+        for (File file : files) {
+            if (file.isDirectory()) {
+                getJavaCode(file, codes);
             }
-            if(FilenameUtils.getExtension(file.getName()).equals("java")){
-                StringBuilder stringBuilder=new StringBuilder();
-                try{
+            if (FilenameUtils.getExtension(file.getName()).equals("java")) {
+                StringBuilder stringBuilder = new StringBuilder();
+                try {
                     BufferedReader br = new BufferedReader(new FileReader(file));
                     String s;
-                    while((s = br.readLine())!=null){
-                        stringBuilder.append(System.lineSeparator()+s.replaceAll("\t","      "));
+                    while ((s = br.readLine()) != null) {
+                        stringBuilder.append(System.lineSeparator() + s.replaceAll("\t", "      "));
                     }
                     br.close();
-                }catch(Exception e){
+                } catch (Exception e) {
                     e.printStackTrace();
                     continue;
                 }
@@ -785,15 +925,15 @@ public class PdfServiceImpl extends BaseService implements PdfService {
         }
     }
 
-    private List<String> getTargetCode(String downloadUrl){
+    private List<String> getTargetCode(String downloadUrl) {
         String path;
         try {
             path = downloadService.download(downloadUrl);
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
             return null;
         }
-        File file=new File(path);
+        File file = new File(path);
 
         try {
             ZipFile zipFile = new ZipFile(path);
@@ -803,19 +943,19 @@ public class PdfServiceImpl extends BaseService implements PdfService {
                     file.getAbsolutePath()), e);
             return null;
         }
-        List<String> strs=new ArrayList<>();
-        getJavaCode(new File(FileUtils.getUserDirectory()+"/"+FilenameUtils.removeExtension(file.getName())+"/src"),strs);
+        List<String> strs = new ArrayList<>();
+        getJavaCode(new File(FileUtils.getUserDirectory() + "/" + FilenameUtils.removeExtension(file.getName()) + "/src"), strs);
         FileUtils.deleteQuietly(file);
-        FileUtils.deleteQuietly(new File(FileUtils.getUserDirectory()+"/"+FilenameUtils.removeExtension(file.getName())));
+        FileUtils.deleteQuietly(new File(FileUtils.getUserDirectory() + "/" + FilenameUtils.removeExtension(file.getName())));
         return strs;
     }
 
-    private void getCaseContent(CaseExtends caseExtends,CasePDF pdf){
-        JSONObject object=new JSONObject(caseExtends.getProperties());
-        if(caseExtends.getAnswerWay()==AnswerWayConstants.DEV_ECLIPSE){
-            Target target =targetService.getAppById(caseExtends.getAppId());
+    private void getCaseContent(CaseExtends caseExtends, CasePDF pdf) {
+        JSONObject object = new JSONObject(caseExtends.getProperties());
+        if (caseExtends.getAnswerWay() == AnswerWayConstants.DEV_ECLIPSE) {
+            Target target = targetService.getAppById(caseExtends.getAppId());
             pdf.setCodes(getTargetCode(target.getUrl()));
-        }else {
+        } else {
             if (object.has("require")) {
                 String path;
                 try {
@@ -839,4 +979,12 @@ public class PdfServiceImpl extends BaseService implements PdfService {
             }
         }
     }
+
+    public Font getPdfChineseFont() throws IOException, DocumentException {
+        BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H",
+                BaseFont.NOT_EMBEDDED);
+        Font fontChinese = new Font(bfChinese, 12, Font.NORMAL);
+        return fontChinese;
+
+    }
 }

+ 49 - 4
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/CatchServiceImpl.java

@@ -12,9 +12,7 @@ import org.springframework.http.*;
 import org.springframework.stereotype.Service;
 import org.springframework.web.client.RestTemplate;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 
 /**
  * @author sean
@@ -50,7 +48,7 @@ public class CatchServiceImpl implements CatchService {
 
         RestTemplate rt = new RestTemplate();
         String url = MongoAPIUtils.generateUrlWithResourceId(mongoDBConfig.getDb()
-                ,mongoDBConfig.getCatchCollection(),id);
+                ,mongoDBConfig.getCatchCollection(), id);
         ResponseEntity<JSONObject> dto = rt.exchange(url, HttpMethod.GET, entity, JSONObject.class);
         return extractCatchData(dto);
     }
@@ -90,4 +88,51 @@ public class CatchServiceImpl implements CatchService {
 
         return location.substring(location.lastIndexOf("/") + 1, location.length());
     }
+
+    @Override
+    public Map<Long, List<CatchDTO>> findByIds(Map<String, Long> lastIdAndUserIdMap) {
+        HttpHeaders headers = MongoAPIUtils.createAuthHeaderForMongo();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        HttpEntity<String> entity=new HttpEntity<>(headers);
+
+        StringBuilder filter = new StringBuilder();
+        // 因为是查询id字段,用 $or 或者 $in 都会走索引
+        filter.append("{\"$or\":[");
+        Iterator<String> iterator = lastIdAndUserIdMap.keySet().iterator();
+        while (iterator.hasNext()){
+            filter.append("{\"_id\":{\"$oid\":").append("\"").append(iterator.next()).append("\"").append("}}");
+            if (!iterator.hasNext()){
+                break;
+            }
+            filter.append(",");
+        }
+        filter.append("]}");
+
+        RestTemplate rt = new RestTemplate();
+        String url = MongoAPIUtils.generateFilterUrl(mongoDBConfig.getDb(), mongoDBConfig.getCatchCollection());
+        ResponseEntity<JSONObject> dto = rt.exchange(url, HttpMethod.GET, entity, JSONObject.class, filter);
+
+        return extractCatchDataForFindByIds(dto, lastIdAndUserIdMap);
+    }
+
+    private Map<Long, List<CatchDTO>> extractCatchDataForFindByIds(ResponseEntity<JSONObject> dto, Map<String, Long> lastIdAndUserIdMap) {
+        Map<Long, List<CatchDTO>> result = new HashMap<>();
+        JSONArray resultArray = dto.getBody().getJSONArray("_embedded");
+        if(resultArray.size() == 0){
+            return null;
+        }
+        Gson gson = new Gson();
+        for (int i = 0; i < resultArray.size(); i++) {
+            JSONObject current = resultArray.getJSONObject(i);
+            String id = current.getJSONObject("_id").getString("$oid");
+            JSONArray currentArray = current.getJSONArray("list");
+            List<CatchDTO> catchDTOS = new ArrayList<>();
+            for (int j = 0; j < currentArray.size(); j++) {
+                CatchDTO catchDTO = gson.fromJson(currentArray.get(j).toString(), CatchDTO.class);
+                catchDTOS.add(catchDTO);
+            }
+            result.put(lastIdAndUserIdMap.get(id), catchDTOS);
+        }
+        return result;
+    }
 }

+ 13 - 16
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/GroupServiceImpl.java

@@ -25,11 +25,9 @@ import org.springframework.stereotype.Service;
 import java.math.BigInteger;
 import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * @author sean
@@ -127,13 +125,12 @@ public class GroupServiceImpl implements GroupService {
 
     @Override
     public List<Integer> getWorkerCounts(List<Long> groupIds) {
-
-        List<Integer> workerCounts = new ArrayList<>();
-        for (Long groupId : groupIds) {
-            workerCounts.add(group2WorkerDao.countByGroupId(groupId));
+        List<Integer> workerCount = new ArrayList<>();
+        List<BigInteger> workerCounts = group2WorkerDao.countGroupId(groupIds);
+        for (BigInteger count : workerCounts) {
+            workerCount.add(count.intValue());
         }
-
-        return workerCounts;
+        return workerCount;
     }
 
     @Override
@@ -265,19 +262,19 @@ public class GroupServiceImpl implements GroupService {
 
     @Override
     public Map<Long, Long> getOwnerAndWorkersCountDTO(List<Long> ownerIds) {
-        List<Group> groups = groupDao.findByOwnerIdInAndIsActiveAndIsDeleted(ownerIds,true,false);
+        List<Group> groups = groupDao.findByOwnerIdInAndIsActiveAndIsDeleted(ownerIds, true, false);
         List<OwnerAndWorkersCountDTO> ownerAndWorkersCountDTOS = Lists.newArrayList();
-        Map<Long,Long> groupWorkerCountMap = group2WorkerDao.getGroupWorkerCount()
+        Map<Long, Long> groupWorkerCountMap = group2WorkerDao.getGroupWorkerCount()
                 .stream()
-                .map(o -> new GroupWorkerCount(((BigInteger)o[0]).longValue(), ((BigInteger)o[1]).longValue()))
+                .map(o -> new GroupWorkerCount(((BigInteger) o[0]).longValue(), ((BigInteger) o[1]).longValue()))
                 .collect(Collectors.toMap(GroupWorkerCount::getGroupId, GroupWorkerCount::getWorkersCount));
 
         groups.stream()
                 .collect(Collectors.groupingBy(Group::getOwnerId))
-                .forEach((ownerId,value) -> {
+                .forEach((ownerId, value) -> {
                     long sum = value
                             .stream()
-                            .mapToLong(group -> groupWorkerCountMap.getOrDefault(group.getId(),0L))
+                            .mapToLong(group -> groupWorkerCountMap.getOrDefault(group.getId(), 0L))
                             .sum();
                     ownerAndWorkersCountDTOS.add(new OwnerAndWorkersCountDTO(ownerId, sum));
                 });
@@ -287,7 +284,7 @@ public class GroupServiceImpl implements GroupService {
 
     @Override
     public int updateGroupSize(long groupId) {
-        return  groupDao.updateGroupSize(groupId);
+        return groupDao.updateGroupSize(groupId);
     }
 
 }

+ 1 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/ManagerPropertyServiceImpl.java

@@ -30,7 +30,7 @@ public class ManagerPropertyServiceImpl implements ManagerPropertyService {
         managerProperty.setGroupSize(50);
         managerProperty.setGroupNum(10);
         managerProperty.setCreateTime(new Timestamp(System.currentTimeMillis()));
-
+        managerProperty.setClassSize(50);
         Long expireTime = 1531584000000L;
         managerProperty.setExpireTime(new Timestamp(expireTime));
         managerProperty.setClassSize(50);

+ 16 - 10
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/SubmitRecordServiceImpl.java

@@ -3,13 +3,11 @@ package cn.iselab.mooctest.site.service.impl;
 import cn.iselab.mooctest.site.dao.SubmitRecordDao;
 import cn.iselab.mooctest.site.dao.CaseExtendsDao;
 import cn.iselab.mooctest.site.dao.ContestMentorSubmitRecordDao;
+import cn.iselab.mooctest.site.dao.UploadRecordDao;
 import cn.iselab.mooctest.site.data.AssignedCase;
 import cn.iselab.mooctest.site.data.Result;
 import cn.iselab.mooctest.site.data.SubmitRecord2UserInfoDTO;
-import cn.iselab.mooctest.site.models.ContestMentorSubmitRecord;
-import cn.iselab.mooctest.site.models.SubmitRecord;
-import cn.iselab.mooctest.site.models.CaseExtends;
-import cn.iselab.mooctest.site.models.Exam;
+import cn.iselab.mooctest.site.models.*;
 import cn.iselab.mooctest.site.service.CaseService;
 import cn.iselab.mooctest.site.service.SubmitRecordService;
 import cn.iselab.mooctest.site.service.BaseService;
@@ -19,10 +17,9 @@ import cn.iselab.mooctest.site.util.data.Converter;
 import cn.iselab.mooctest.site.web.exception.HttpNotFoundException;
 import cn.iselab.mooctest.site.web.logic.CalculateSocreLogic;
 import com.google.common.collect.Lists;
-
 import java.text.DecimalFormat;
-import java.util.Comparator;
-import java.util.Map;
+import java.util.*;
+
 import org.apache.shiro.SecurityUtils;
 import org.json.JSONArray;
 import org.json.JSONObject;
@@ -32,8 +29,6 @@ import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.stream.Collectors;
 
 /**
@@ -45,6 +40,8 @@ public class SubmitRecordServiceImpl extends BaseService implements SubmitRecord
 
     @Autowired
     private SubmitRecordDao submitRecordDao;
+    @Autowired
+    private UploadRecordDao uploadRecordDao;
 
     @Autowired
     private ContestMentorSubmitRecordDao contestMentorSubmitRecordDao;
@@ -83,13 +80,22 @@ public class SubmitRecordServiceImpl extends BaseService implements SubmitRecord
         String result = submitRecord.getResult();
         JSONObject resultJson = new JSONObject(result).getJSONObject("results");
 
+
+
         for (Object key : resultJson.keySet()) {
 
             String keyStr = (String) key;
             JSONObject caseJson = resultJson.getJSONObject(keyStr);
-
             AssignedCase assignedCase = new AssignedCase();
             assignedCase.setCaseId(caseJson.getLong("id"));
+        List<UploadRecord> uploadRecords=uploadRecordDao.findByWorkerIdAndExamIdAndCaseId(submitRecord.getParticipantId(),submitRecord.getExamId(),caseJson.getLong("id"));
+            if(uploadRecords!=null) {
+                assignedCase.setUploadTimes(uploadRecords.size());
+            }else{
+                assignedCase.setUploadTimes(0);
+            }
+
+
             long targetId = 0;
             if (caseJson.has("name")) {
                 assignedCase.setCaseName(caseJson.getString("name"));

+ 26 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/UploadRecordServiceImpl.java

@@ -0,0 +1,26 @@
+package cn.iselab.mooctest.site.service.impl;
+
+import cn.iselab.mooctest.site.dao.UploadRecordDao;
+import cn.iselab.mooctest.site.models.UploadRecord;
+import cn.iselab.mooctest.site.service.UploadRecordService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author xx
+ * @version 1.0
+ * @className UploadRecordServiceImpl
+ * @deacription TODO
+ * @date 2020/11/2 13:35
+ **/
+@Service
+public class UploadRecordServiceImpl implements UploadRecordService {
+    @Autowired
+    private UploadRecordDao uploadRecordDao;
+    @Override
+    public List<UploadRecord> findByCaseId(long workerId,long examId, long caseId ) {
+         return uploadRecordDao.findByWorkerIdAndExamIdAndCaseId(workerId,examId,caseId);
+    }
+}

+ 26 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/util/DateUtil/DateUtil.java

@@ -0,0 +1,26 @@
+package cn.iselab.mooctest.site.util.DateUtil;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author xx
+ * @version 1.0
+ * @className DateUtil
+ * @deacription TODO
+ * @date 2020/11/2 15:03
+ **/
+public class DateUtil {
+    public static String timeStamp2Date(String time) {
+        Long timeLong = Long.parseLong(time);
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//要转换的时间格式
+        Date date;
+        try {
+            date = sdf.parse(sdf.format(timeLong));
+            return sdf.format(date);
+        } catch (ParseException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

+ 23 - 14
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/CaseController.java

@@ -8,7 +8,9 @@ import cn.iselab.mooctest.site.service.fromKibug.CaseTakeService;
 import cn.iselab.mooctest.site.web.data.SearchConditionVO;
 import cn.iselab.mooctest.site.web.data.UserStarCaseVO;
 import cn.iselab.mooctest.site.web.exception.HttpBadRequestException;
+import cn.iselab.mooctest.site.web.exception.HttpNotFoundException;
 import cn.iselab.mooctest.site.web.logic.GeneralCalculateScoreLogic;
+import cn.iselab.mooctest.site.web.logic.exception.CaseNotFoundException;
 import cn.iselab.mooctest.site.web.response.ErrorResult;
 import cn.iselab.mooctest.site.web.response.ResponseMessage;
 import cn.iselab.mooctest.site.web.response.StatusCode;
@@ -64,13 +66,13 @@ public class CaseController extends BaseSearchController {
         if (activePage == null || rowsOnPage == null) {
             throw new IllegalArgumentException("缺少分页信息");
         }
-        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-//        if (isPublic != null && isPublic == false) {
-//            userId = user.getId();
-//        }
-        if (isPublic == null || isPublic == true) {
+        Long userId;
+        if (isPublic == null || isPublic) {
             userId = null;
         }
+        else {
+            userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+        }
         Sort sortById = new Sort(Sort.Direction.DESC, "id");
         Pageable pageable = PageRequest.of(Integer.parseInt(activePage) - 1, Integer.parseInt(rowsOnPage), sortById);
         return caseLogic.searchCases(isPublic, userId, null, null,keyword, pageable);
@@ -152,9 +154,6 @@ public class CaseController extends BaseSearchController {
         if (!SecurityUtils.getSubject().isPermitted(new CasePermission(permissionStr))) {
             throw new UnauthenticatedException("forbidden");
         }
-//        if(caseId!=caseExtendsVO.getId()){
-//            throw new IllegalArgumentException("Id are not same");
-//        }
         caseLogic.update(caseExtendsVO, caseId);
     }
 
@@ -171,7 +170,16 @@ public class CaseController extends BaseSearchController {
         if (!SecurityUtils.getSubject().isPermitted(new CasePermission(permissionStr))) {
             throw new UnauthenticatedException("forbidden");
         }
-        return caseLogic.updateCaseHidden(caseId);
+        // 判断是否为管理员
+        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
+        if (!isAdmin) {
+            throw new HttpBadRequestException("当前用户无此操作权限");
+        }
+        try {
+            return caseLogic.updateCaseHidden(caseId);
+        } catch (CaseNotFoundException e) {
+            throw new HttpNotFoundException(e.getMessage());
+        }
     }
 
     @RequiresPermissions("case:update")
@@ -182,17 +190,18 @@ public class CaseController extends BaseSearchController {
         if (!SecurityUtils.getSubject().isPermitted(new CasePermission(permissionStr))) {
             throw new UnauthenticatedException("forbidden");
         }
-//        if(caseId!=caseExtendsVO.getId()){
-//            throw new IllegalArgumentException("Id are not same");
-//        }
         caseLogic.publicityCase(caseId);
     }
 
     @RequiresPermissions("case:view")
     @RequestMapping(value = UrlConstants.API + "case/{caseId}", method = RequestMethod.DELETE)
     public CaseExtendsVO deleteCase(@PathVariable("caseId") long caseId) {
-//        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        return caseLogic.delete(caseId);
+        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
+        try {
+            return caseLogic.delete(caseId, isAdmin);
+        } catch (CaseNotFoundException e) {
+            throw new HttpNotFoundException(String.format("case %s not exists", caseId));
+        }
     }
 
     @RequiresPermissions("case:view")

+ 0 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/TestController.java

@@ -154,9 +154,6 @@ public class TestController {
 
     @RequestMapping(value = "/api/currUser", method = RequestMethod.GET)
     public UserVO getCurrentUser() {
-//        String username = (String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal();
-//        userLogic.findUserByEmail(username);
-//        UserVO UserVO = userLogic.findUserByEmail(username);
         Subject subject = SecurityUtils.getSubject();
         long userId = (long) SecurityUtils.getSubject().getSession().getAttribute("userId");
         LOG.info("get current user whose id is: " + userId);

+ 3 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/fromKibug/ScoreRuleController.java

@@ -87,8 +87,10 @@ public class ScoreRuleController  extends BaseController {
      * @return
      */
     private boolean isTddCase(Long caseId) {
+        final Long javaTddSubjectId = 51L;
+        final Long pythonTddSubjectId = 78L;
         Long subjectId = caseService.getCaseById(caseId).getSubjectId();
-        return subjectId.equals(51L) || subjectId.equals(78L);
+        return javaTddSubjectId.equals(subjectId)||pythonTddSubjectId.equals(subjectId);
     }
 
     @RequestMapping(value= UrlConstants.API_KIBUG+"scoreRule/{taskId}/{caseId}", method = RequestMethod.POST)

+ 11 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/AssignedCaseVO.java

@@ -21,6 +21,9 @@ public class AssignedCaseVO extends BaseVO {
 
     private List<ResultVO> results;
 
+    //提交次数
+    private  Integer uploadTimes;
+
     public Long getCaseId() {
         return caseId;
     }
@@ -60,4 +63,12 @@ public class AssignedCaseVO extends BaseVO {
     public void setResults(List<ResultVO> results) {
         this.results = results;
     }
+
+    public Integer getUploadTimes() {
+        return uploadTimes;
+    }
+
+    public void setUploadTimes(Integer uploadTimes) {
+        this.uploadTimes = uploadTimes;
+    }
 }

+ 7 - 1
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/AssignedTaskVO.java

@@ -8,7 +8,7 @@ import java.util.List;
  * @author sean
  * @date 2017-03-16.
  */
-@JsonInclude(JsonInclude.Include.NON_NULL)
+
 public class AssignedTaskVO extends BaseVO {
 
     private Long id;
@@ -39,6 +39,12 @@ public class AssignedTaskVO extends BaseVO {
 
     private List<AssignedCaseVO> assignedCases;
 
+
+    private  Integer  uploadTimes;
+
+
+
+
     private Integer status;
 
     private String resultZip;

+ 2 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/MenuVO.java

@@ -1,11 +1,13 @@
 package cn.iselab.mooctest.site.web.data;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.EqualsAndHashCode;
 
 /**
  * @author sean
  * @date 2017-07-11.
  */
+@EqualsAndHashCode
 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class MenuVO extends BaseVO{
 

+ 2 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/SimilarityResultVO.java

@@ -2,8 +2,10 @@ package cn.iselab.mooctest.site.web.data;
 
 
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 @Data
+@NoArgsConstructor
 public class SimilarityResultVO implements Comparable<SimilarityResultVO>{
     private String userName1;
     private String userName2;

+ 0 - 4
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/forMongo/ReportForMongoDTO.java

@@ -1,10 +1,6 @@
 package cn.iselab.mooctest.site.web.data.forMongo;
 
-import cn.iselab.mooctest.site.models.fromKibug.Bug;
-import cn.iselab.mooctest.site.models.fromKibug.Report;
-import cn.iselab.mooctest.site.web.data.fromKibug.BugVO;
 import cn.iselab.mooctest.site.web.data.fromKibug.ReportCaseVO;
-import com.sun.org.apache.regexp.internal.RE;
 import lombok.Data;
 
 import java.util.List;

+ 26 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/forMongo/SimilarityResultDTO.java

@@ -0,0 +1,26 @@
+package cn.iselab.mooctest.site.web.data.forMongo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 保存相似度分析的结果
+ * CREATED BY WANGPEIXU
+ * 20201028
+ */
+@Data
+public class SimilarityResultDTO implements Serializable, Comparable<SimilarityResultDTO>{
+
+    private Long examId;
+    private Long caseId;
+    private String type;
+    private Long userCaseId1;
+    private Long userCaseId2;
+    private Double similarity;
+
+    @Override
+    public int compareTo(SimilarityResultDTO o) {
+        return this.similarity.compareTo(o.similarity);
+    }
+}

+ 21 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/forMongo/SimilarityResultExtendsDTO.java

@@ -0,0 +1,21 @@
+package cn.iselab.mooctest.site.web.data.forMongo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * mongodb中保存的相似度结果数据对象结构
+ * 对应于mongo中的similarityResult集合
+ * created by wangpeixu
+ * 20201105
+ */
+@Data
+public class SimilarityResultExtendsDTO implements Serializable {
+
+    private Long examId;
+    private Long caseId;
+    private String type;
+    private List<SimilarityResultDTO> dtos;
+}

+ 1 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/AssignedCaseVOWrapper.java

@@ -23,6 +23,7 @@ public class AssignedCaseVOWrapper extends BaseWrapper<AssignedCaseVO, AssignedC
         vo.setWeight(assignedCase.getWeight());
         vo.setMaxScore(assignedCase.getMaxScore());
         vo.setResults(resultVOWrapper.wrap(assignedCase.getResults()));
+        vo.setUploadTimes(assignedCase.getUploadTimes());
         return vo;
     }
 

+ 32 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/wrapper/SimilarityResultVOWrapper.java

@@ -0,0 +1,32 @@
+package cn.iselab.mooctest.site.web.data.wrapper;
+
+
+import cn.iselab.mooctest.site.web.data.SimilarityResultVO;
+import cn.iselab.mooctest.site.web.data.forMongo.SimilarityResultDTO;
+import org.springframework.stereotype.Service;
+
+/**
+ * CREATED BY WANGPEIXU
+ * 20201028
+ */
+@Service
+public class SimilarityResultVOWrapper extends BaseWrapper<SimilarityResultVO, SimilarityResultDTO>{
+
+    @Override
+    public SimilarityResultVO wrap(SimilarityResultDTO similarityResultDTO) {
+        SimilarityResultVO similarityResultVO = new SimilarityResultVO();
+        similarityResultVO.setUserCaseId1(similarityResultDTO.getUserCaseId1());
+        similarityResultVO.setUserCaseId2(similarityResultDTO.getUserCaseId2());
+        similarityResultVO.setSimilarity(similarityResultDTO.getSimilarity());
+        return similarityResultVO;
+    }
+
+    @Override
+    public SimilarityResultDTO unwrap(SimilarityResultVO data) {
+        SimilarityResultDTO similarityResultDTO = new SimilarityResultDTO();
+        similarityResultDTO.setUserCaseId1(data.getUserCaseId1());
+        similarityResultDTO.setUserCaseId2(data.getUserCaseId2());
+        similarityResultDTO.setSimilarity(data.getSimilarity());
+        return similarityResultDTO;
+    }
+}

+ 17 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/exception/ResultNotFoundException.java

@@ -0,0 +1,17 @@
+package cn.iselab.mooctest.site.web.exception;
+
+/**
+ * @author xx
+ * @version 1.0
+ * @className ResultNotFoundException
+ * @deacription TODO
+ * @date 2020/11/9 13:32
+ **/
+public class ResultNotFoundException extends  RuntimeException {
+
+    public  ResultNotFoundException(String message){
+        super(message);
+
+    }
+
+}

+ 3 - 2
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/CaseLogic.java

@@ -4,6 +4,7 @@ import cn.iselab.mooctest.site.web.data.CaseExtendsVO;
 import cn.iselab.mooctest.site.web.data.CaseVO;
 import cn.iselab.mooctest.site.web.data.KeyAnalysisVO;
 import cn.iselab.mooctest.site.web.data.forMongo.CaseGraphDTO;
+import cn.iselab.mooctest.site.web.logic.exception.CaseNotFoundException;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 
@@ -27,7 +28,7 @@ public interface CaseLogic {
 
     void publicityCase(Long caseId);
 
-    CaseExtendsVO delete(Long caseExtendsId);
+    CaseExtendsVO delete(Long caseExtendsId, final boolean isAdmin) throws CaseNotFoundException;
 
     CaseExtendsVO getCaseExtends(Long caseId) throws Exception;
 
@@ -47,5 +48,5 @@ public interface CaseLogic {
 
     List<CaseExtendsVO> getPythonCommunityExerciseCases(long examId) throws Exception;
 
-    CaseExtendsVO updateCaseHidden(Long caseId);
+    CaseExtendsVO updateCaseHidden(Long caseId) throws CaseNotFoundException;
 }

+ 3 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/SimilarityLogic.java

@@ -11,10 +11,10 @@ import java.util.List;
  */
 public interface SimilarityLogic {
 
-    public List<SimilarityResultVO> getTopSimilarResultInCase(Long examId, Long caseId, String type);
+    List<SimilarityResultVO> getTopSimilarResultInCase(Long examId, Long caseId, String type);
 
-    public List<SimilarityResultVO> getTopSimilarResultInCase(Long examId, Long caseId, String type, Long userId);
+    List<SimilarityResultVO> getTopSimilarResultInCase(Long examId, Long caseId, String type, Long userId);
 
-    public HashMap<String, int[]> getMatrixOfProgress(Long examId, Long caseId, String type, Long userId);
+    HashMap<String, int[]> getMatrixOfProgress(Long examId, Long caseId, String type, Long userId);
 
 }

+ 15 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/exception/CaseNotFoundException.java

@@ -0,0 +1,15 @@
+package cn.iselab.mooctest.site.web.logic.exception;
+
+/**
+ * @author: 黄勇
+ * @create: 2020/10/21 13:55
+ */
+
+public class CaseNotFoundException extends Throwable {
+    public CaseNotFoundException() {
+    }
+
+    public CaseNotFoundException(String message) {
+        super(message);
+    }
+}

+ 71 - 107
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/CaseLogicImpl.java

@@ -33,10 +33,10 @@ import cn.iselab.mooctest.site.web.logic.CaseLogic;
 import cn.iselab.mooctest.site.web.logic.OSSLogic;
 import cn.iselab.mooctest.site.web.logic.Oauth2Logic;
 import cn.iselab.mooctest.site.web.logic.drools.CaseExtendsDroolsVarifyLogic;
+import cn.iselab.mooctest.site.web.logic.exception.CaseNotFoundException;
 import cn.iselab.mooctest.site.web.logic.fromDev.UpDownloadLogic;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
-import org.apache.shiro.SecurityUtils;
 import org.json.JSONArray;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -54,10 +54,7 @@ import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -68,78 +65,55 @@ import java.util.stream.Collectors;
 public class CaseLogicImpl implements CaseLogic {
 
     @Autowired
+    EventUtil eventUtil;
+    @Autowired
+    UpDownloadLogic upDownloadLogic;
+    @Autowired
+    Oauth2Logic oauth2Logic;
+    @Autowired
+    CaseGraphService caseGraphService;
+    @Autowired
+    TargetGraphService targetGraphService;
+    @Autowired
+    DownloadService downloadService;
+    @Autowired
+    WebIDEUrlService webIDEUrlService;
+    @Autowired
+    WeightGeneralService weightGeneralService;
+    @Autowired
+    AnswerWay2CaseBlockService answerWay2CaseBlockService;
+    @Autowired
+    User2RoleService user2RoleService;
+    @Autowired
+    VisitControlService visitControlService;
+    @Autowired
+    ExamPythonCommunityService examPythonCommunityService;
+    @Autowired
     private TargetService targetService;
-
     @Autowired
     private CaseService caseService;
-
     @Autowired
     private ExamService examService;
-
     @Autowired
     private UserService userService;
-
     @Autowired
     private RoleService roleService;
-
     @Autowired
     private IndexService indexService;
-
     @Autowired
     private ReportService reportService;
-
     @Autowired
     private CasePermissionService casePermissionService;
-
     @Autowired
     private Exam2CaseService exam2CaseService;
-
     @Autowired
     private SubmitRecordService submitRecordService;
-
     @Autowired
     private PaperService paperService;
-
     @Autowired
     private AnswerAnnexService answerAnnexService;
-
-    @Autowired
-    EventUtil eventUtil;
-
     @Autowired
     private CaseExtendsVOWrapper caseExtendsVOWrapper;
-
-    @Autowired
-    UpDownloadLogic upDownloadLogic;
-
-    @Autowired
-    Oauth2Logic oauth2Logic;
-
-    @Autowired
-    CaseGraphService caseGraphService;
-
-    @Autowired
-    TargetGraphService targetGraphService;
-
-    @Autowired
-    DownloadService downloadService;
-
-    @Autowired
-    WebIDEUrlService webIDEUrlService;
-
-    @Autowired
-    WeightGeneralService weightGeneralService;
-
-    @Autowired
-    AnswerWay2CaseBlockService answerWay2CaseBlockService;
-
-    @Autowired
-    User2RoleService user2RoleService;
-    @Autowired
-    VisitControlService visitControlService;
-
-    @Autowired
-    ExamPythonCommunityService examPythonCommunityService;
     @Autowired
     private CaseExtendsDroolsVarifyLogic caseExtendsDroolsVarifyLogic;
 
@@ -271,20 +245,15 @@ public class CaseLogicImpl implements CaseLogic {
     }
 
     @Override
-    public CaseExtendsVO updateCaseHidden(Long caseId) {
+    public CaseExtendsVO updateCaseHidden(Long caseId) throws CaseNotFoundException {
         CaseExtends caseExtends = caseService.getCaseExtendsById(caseId);
         if (caseExtends == null) {
-            throw new IllegalArgumentException("Cannot find the caseExtends");
+            throw new CaseNotFoundException(String.format("Cannot find the caseExtends %s", caseId));
         }
         // 判断是否为私有case,私有case无影藏的操作
-        if (caseExtends.getVisible() == false) {
+        if (!caseExtends.getVisible()) {
             throw new IllegalArgumentException("Cannot hide the private case");
         }
-        // 判断是否为管理员
-        boolean isAdmin = SecurityUtils.getSubject().hasRole("admin");
-        if (!isAdmin) {
-            throw new HttpBadRequestException("当前用户无此操作权限");
-        }
         caseExtends.setHidden(caseExtends.getHidden() == 0 ? 1 : 0);
         CaseExtends savedCase = caseService.saveOnly(caseExtends);
         return Converter.convert(CaseExtendsVO.class, savedCase);
@@ -356,10 +325,9 @@ public class CaseLogicImpl implements CaseLogic {
      * @param description 案例描述
      * @param caseId      案例id
      * @param caseName    案例名称
-     * @throws Exception 异常
      */
     private void save2crowdPlatform(String fileUrl, String paper_type, String test_type, String
-            description, long caseId, String caseName) throws Exception {
+            description, long caseId, String caseName) {
         String filename = fileUrl.split("/")[fileUrl.split("/").length - 1];
         MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
         params.add("file", fileUrl);
@@ -375,7 +343,7 @@ public class CaseLogicImpl implements CaseLogic {
         ResponseEntity<String> responseEntity = restTemplate.postForEntity
                 (crowdAddress + "/Bug/api/extra/uploadExamUrl", params, String.class);
         if (responseEntity.getStatusCode().equals(HttpStatus.OK)) {
-            InputStream jsonInputStream = new ByteArrayInputStream(responseEntity.getBody().getBytes());
+            InputStream jsonInputStream = new ByteArrayInputStream(Objects.requireNonNull(responseEntity.getBody()).getBytes());
             ossLogic.uploadByUrl(bucketName, "json/" + caseId + ".json", jsonInputStream);
         } else {
             throw new ServerException(60000, "保存到众测接口失败");
@@ -389,7 +357,7 @@ public class CaseLogicImpl implements CaseLogic {
         if (caseExtends == null) {
             throw new IllegalArgumentException("Cannot find the caseExtends");
         }
-        if (caseExtends.getVisible() == true) {
+        if (caseExtends.getVisible()) {
             return;
         }
         caseExtends.setVisible(true);
@@ -401,38 +369,21 @@ public class CaseLogicImpl implements CaseLogic {
     }
 
     @Override
-    public CaseExtendsVO delete(Long caseExtendsId) {
-        Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
-        boolean isAdmin=false;
-        UserDTOForMT user = userService.findByUserId(userId);
-        String userName = user.getName();
-        List<User2Role> user2Roles=user2RoleService.getByUserId(userId);
-        for(int i=0;i<user2Roles.size();i++){
-            if(user2Roles.get(i).getRoleId()==1){
-                isAdmin=true;
-            }
-        }
-
+    public CaseExtendsVO delete(Long caseExtendsId, final boolean isAdmin) throws CaseNotFoundException {
         CaseExtends caseExtends = caseService.getCaseExtendsById(caseExtendsId);
-        System.out.println("userName===========" + userName);
-
         if (caseExtends == null) {
-            throw new IllegalArgumentException("Cannot find the caseExtends");
-        }
-        // 如果是公开的不允许删除,管理员除外
-        if (isAdmin==false) {
-            if (caseExtends.getVisible() == true) {
-                throw new IllegalArgumentException("Cannot delete the public case");
-            }
+            throw new CaseNotFoundException();
         }
-        // 判断是否是case的创建者
-//        if (!caseExtends.getOwnerId().equals(userId)){
-//            throw new IllegalArgumentException("Cannot delete the private case. Because you are not the owner of this case.");
-//        }
+
         // 判断是否已删除
-        if (caseExtends.getDeleted() == true) {
+        if (caseExtends.getDeleted()) {
             throw new IllegalArgumentException("The case is deleted");
         }
+
+        // 如果是公开的不允许删除,管理员除外
+        if (!isAdmin && caseExtends.getVisible()) {
+            throw new IllegalArgumentException("Cannot delete the public case");
+        }
         caseExtends.setDeleted(true);
         CaseExtends caseExtendsDeleted = caseService.saveOnly(caseExtends);
         return Converter.convert(CaseExtendsVO.class, caseExtendsDeleted);
@@ -441,14 +392,11 @@ public class CaseLogicImpl implements CaseLogic {
     @Override
     public CaseExtendsVO getCaseExtends(Long caseId) throws Exception {
         CaseExtends caseExtends = caseService.getCaseExtendsById(caseId);
-        CaseExtendsVO  caseExtendsVO=new CaseExtendsVO();
-        if(caseExtends.getDeleted()==true){
+        CaseExtendsVO caseExtendsVO = new CaseExtendsVO();
+        if (caseExtends.getDeleted()) {
             caseExtendsVO.setDeleted(true);
-            return  caseExtendsVO;
-        }else {
-            if (caseExtends == null) {
-                throw new IllegalArgumentException();
-            }
+            return caseExtendsVO;
+        } else {
             CaseExtendsVO re = Converter.convert(CaseExtendsVO.class, caseExtends);
 
             re.setManagerId(caseExtends.getOwnerId());
@@ -505,36 +453,37 @@ public class CaseLogicImpl implements CaseLogic {
                     caseExtendsVO.setAppLocation(target.getUrl());
                 }
             }
-            List<Integer> showTags = caseExtendsVO.getShowBtns();
-            if (showTags.get(CaseBlockConstants.ONLINE) == 1) {
+            AnswerWayButtonBar showTags = new AnswerWayButtonBar(caseExtendsVO.getShowBtns());
+
+            if (showTags.has(CaseBlockConstants.ONLINE)) {
                 Exam exam = examService.getTask(examId);
                 caseExtendsVO.setWebIDE(webIDEUrlService.generateUrlForWebIDE(userId, examId,
                         caseExtends.getAppId(), caseid, caseExtends.getName(), caseExtendsVO
                                 .getAnswerWay(), new Gson().fromJson(exam.getSwitchConfig(), new TypeToken<Map<String, Boolean>>() {
                         }.getType())));
             }
-            if (showTags.get(CaseBlockConstants.SECRET) == 1) {
+            if (showTags.has(CaseBlockConstants.SECRET)) {
                 caseExtendsVO.setSecret(indexService.getSecret(userId, examId));
-
             }
-            if (showTags.get(CaseBlockConstants.MY_REPORT) == 1) {
+            if (showTags.has(CaseBlockConstants.MY_REPORT)) {
                 Report report = reportService.getReport(examId, caseid, userId);
                 if (report != null) {
                     caseExtendsVO.setReportId(report.getId());
                 }
             }
-            if (showTags.get(CaseBlockConstants.EXAM_ENTRANCE) == 1) {
+            if (showTags.has(CaseBlockConstants.EXAM_ENTRANCE)) {
                 caseExtendsVO.setEntrance(oauth2Logic.getEntrance(userId, examId, caseid, OwningPartyConstants.thirdUrlMap.get(caseExtendsVO.getAnswerWay()), null));
             }
-            if (showTags.get(CaseBlockConstants.BUG_MARK) == 1) {
+            if (showTags.has(CaseBlockConstants.BUG_MARK)) {
                 if (isManager) {
-                    caseExtendsVO.setCaseReportUrl("/report/judgeGrade/" + examId + "/" + caseid);
+                    caseExtendsVO.setCaseReportUrl(String.format("/report/judgeGrade/%d/%d", examId, caseid));
                 }
             }
-            if (showTags.size() > 7 && showTags.get(CaseBlockConstants.UPLOAD_FILE) == 1) {
+
+            if (showTags.has(CaseBlockConstants.UPLOAD_FILE)) {
                 caseExtendsVO.setAnnexList(answerAnnexService.getUrls(userId, examId, caseid));
             }
-            if (isWorker == true) {
+            if (isWorker) {
                 caseExtendsVO.setSubmitted(submitRecordService.isSubmitted(examId, userId, caseExtendsVO.getId(), caseExtendsVO.getCaseId()));
             }
             caseVoList.add(caseExtendsVO);
@@ -642,4 +591,19 @@ public class CaseLogicImpl implements CaseLogic {
         }
         return list;
     }
+
+    private class AnswerWayButtonBar {
+        private final List<Integer> answerWayButtons;
+
+        public AnswerWayButtonBar(List<Integer> answerWayButtons) {
+            this.answerWayButtons = answerWayButtons;
+        }
+
+        public boolean has(int answerWayButton) {
+            if ((this.answerWayButtons.size() <= answerWayButton)) {
+                return false;
+            }
+            return this.answerWayButtons.get(answerWayButton) == 1;
+        }
+    }
 }

+ 17 - 11
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/GroupLogicImpl.java

@@ -32,7 +32,9 @@ import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
+import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -67,20 +69,24 @@ public class GroupLogicImpl extends BaseLogic implements GroupLogic {
 
     @Override
     public List<GroupVO> getOwnerGroups(long ownerId) {
-        List<Group> groups = groupService.getGroupsByOwnerId(ownerId);
-        List<GroupVO> groupVOs = groupVOWrapper.wrap(groups);
-        List<Integer> workerCounts = groupService.getWorkerCounts(groupVOs.stream()
-                .map(GroupVO::getId).collect(Collectors.toList()));
+        List<GroupVO> groupVOs = groupVOWrapper.wrap(groupService.getGroupsByOwnerId(ownerId));
         ManagerProperty managerProperty = managerPropertyService.getManagerPropertyByUserId(ownerId);
-        for(int i=0;i < groupVOs.size(); i++){
-            if(groupVOs.get(i).getId()!=0&&groupVOs.get(i).getClassSize().intValue()>managerProperty.getClassSize().intValue()&&groupService.ifExamsAssignedtoGroup(groupVOs.get(i).getId())){
-                groupVOs.remove(i);
-                workerCounts.remove(i);
-                i--;
-            }else {
-                groupVOs.get(i).setWorkerCount(workerCounts.get(i));
+        Iterator<GroupVO> iterator = groupVOs.iterator();
+        while (iterator.hasNext()) {
+            GroupVO groupVO = iterator.next();
+            boolean isGroup0 = groupVO.getId() != 0;
+            boolean isOverLimited = groupVO.getClassSize().intValue() > managerProperty.getClassSize().intValue();
+            boolean isAssigned = groupService.ifExamsAssignedtoGroup(groupVO.getId());
+            boolean hidden = isGroup0 && isOverLimited && isAssigned;
+            if (hidden) {
+                iterator.remove();
             }
         }
+        List<Integer> workerCounts = groupService.getWorkerCounts(groupVOs.stream()
+               .map(GroupVO::getId).collect(Collectors.toList()));
+         for (int i = 0; i < groupVOs.size(); i++) {
+            groupVOs.get(i).setWorkerCount(workerCounts.get(i).intValue());
+        }
         return groupVOs;
     }
 

+ 19 - 43
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/MenuLogicImpl.java

@@ -1,10 +1,8 @@
 package cn.iselab.mooctest.site.web.logic.impl;
 
 import cn.iselab.mooctest.site.common.enums.RoleType;
-import cn.iselab.mooctest.site.models.Role;
 import cn.iselab.mooctest.site.service.MenuService;
 import cn.iselab.mooctest.site.service.RoleService;
-import cn.iselab.mooctest.site.service.UserService;
 import cn.iselab.mooctest.site.web.data.MenuVO;
 import cn.iselab.mooctest.site.web.data.wrapper.MenuVOWrapper;
 import cn.iselab.mooctest.site.web.logic.MenuLogic;
@@ -13,8 +11,8 @@ import java.util.Map;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @author sean
@@ -23,8 +21,8 @@ import java.util.List;
 @Service
 public class MenuLogicImpl implements MenuLogic {
 
-    @Autowired
-    private UserService userService;
+    public static final String STUDENT_MENUS = "student";
+    public static final String MANAGER_MENUS = "manager";
 
     @Autowired
     private RoleService roleService;
@@ -37,48 +35,26 @@ public class MenuLogicImpl implements MenuLogic {
 
     @Override
     public List<MenuVO> getMenuListByUserId(Long userId) {
-        List<Role> roles = roleService.getRolesOfUser(userId);
-        List<MenuVO> menuVOs = new ArrayList<>();
-        for (Role role : roles) {
-            List<MenuVO> menuVOList = menuVOWrapper.wrap(menuService.getMenuListByRoleId(role.getId()));
-            for (MenuVO m : menuVOList) {
-                if (!menuVOs.stream().anyMatch(menuVO -> menuVO.toString().equals(m.toString()))) {
-                    menuVOs.add(m);
-                }
-            }
-        }
-        return menuVOs;
+        return roleService.getRolesOfUser(userId).stream().
+                flatMap(role -> menuVOWrapper.wrap(menuService.getMenuListByRoleId(role.getId())).stream())
+                .distinct()
+                .collect(Collectors.toList());
     }
 
     @Override
     public Map<String, List<MenuVO>> getMenuMapByUserId(long userId) {
-
-        List<Role> roles = roleService.getRolesOfUser(userId);
-        List<MenuVO> studentMenus = new ArrayList<>();
-        List<MenuVO> managerMenus = new ArrayList<>();
-
-        for(Role role: roles){
-            List<MenuVO> menuVOList = menuVOWrapper.wrap(menuService.getMenuListByRoleId(role.getId()));
-            for(MenuVO vo: menuVOList){
-                if(vo.getRoleId() == RoleType.WORKER.getCode()){
-                    if (studentMenus.stream().noneMatch(menuVO -> menuVO.toString().equals(vo
-                            .toString()))) {
-                        studentMenus.add(vo);
-                    }
-                }else{
-                    if (managerMenus.stream().noneMatch(menuVO -> menuVO.toString().equals(vo
-                            .toString()))) {
-                        managerMenus.add(vo);
-                    }
-                }
-            }
-
-        }
-
-        Map<String, List<MenuVO>> menuMap = new HashMap<>();
-        menuMap.put("student", studentMenus);
-        menuMap.put("manager", managerMenus);
-
+        List<MenuVO> allMenus = roleService.getRolesOfUser(userId).stream().
+                flatMap(role -> menuVOWrapper.wrap(menuService.getMenuListByRoleId(role.getId())).stream())
+                .distinct()
+                .collect(Collectors.toList());
+        List<MenuVO> studentMenus = allMenus.stream()
+                .filter(vo -> vo.getRoleId() == RoleType.WORKER.getCode()).collect(Collectors.toList());
+        List<MenuVO> managerMenus = allMenus.stream()
+                .filter(vo -> vo.getRoleId() != RoleType.WORKER.getCode()).collect(Collectors.toList());
+
+        Map<String, List<MenuVO>> menuMap = new HashMap<>(2);
+        menuMap.put(STUDENT_MENUS, studentMenus);
+        menuMap.put(MANAGER_MENUS, managerMenus);
         return menuMap;
     }
 

+ 210 - 46
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/SimilarityLogicImpl.java

@@ -1,20 +1,35 @@
 package cn.iselab.mooctest.site.web.logic.impl;
 
+import cn.iselab.mooctest.site.common.enums.RoleType;
+import cn.iselab.mooctest.site.configure.MongoDBConfiguration;
 import cn.iselab.mooctest.site.models.Exam;
 import cn.iselab.mooctest.site.service.CaseGraphService;
 import cn.iselab.mooctest.site.service.CatchService;
 import cn.iselab.mooctest.site.service.ExamService;
 import cn.iselab.mooctest.site.service.UserCatchService;
+import cn.iselab.mooctest.site.util.mongodb.MongoAPIUtils;
 import cn.iselab.mooctest.site.web.data.SimilarityResultVO;
 import cn.iselab.mooctest.site.web.data.UserVO;
 import cn.iselab.mooctest.site.web.data.forMongo.NodeCatch.CatchDTO;
 import cn.iselab.mooctest.site.web.data.forMongo.NodeCatch.UserCatchDTO;
+import cn.iselab.mooctest.site.web.data.forMongo.SimilarityResultDTO;
+import cn.iselab.mooctest.site.web.data.forMongo.SimilarityResultExtendsDTO;
 import cn.iselab.mooctest.site.web.data.forMongo.graph.Node;
+import cn.iselab.mooctest.site.web.data.wrapper.SimilarityResultVOWrapper;
+import cn.iselab.mooctest.site.web.logic.RoleLogic;
 import cn.iselab.mooctest.site.web.logic.SimilarityLogic;
 import cn.iselab.mooctest.site.web.logic.UserLogic;
 import com.google.common.collect.Ordering;
+import com.google.gson.Gson;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+import org.apache.commons.collections.map.HashedMap;
+import org.apache.shiro.SecurityUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
 
 import java.sql.Timestamp;
 import java.util.*;
@@ -43,59 +58,69 @@ public class SimilarityLogicImpl implements SimilarityLogic {
 
     private static int NUMBEROFUNIS = 40;
 
+    @Autowired
+    private MongoDBConfiguration mongoDBConfig;
+
+    @Autowired
+    private SimilarityResultVOWrapper similarityResultVOWrapper;
+
+    @Autowired
+    private RoleLogic roleLogic;
+
+    @Value("${similarity.similarityComputeAmountControl}")
+    private Integer SimilarityComputeAmountControl;
+
     @Override
     public List<SimilarityResultVO> getTopSimilarResultInCase(Long examId, Long caseId, String type) {
         List<SimilarityResultVO> result = new LinkedList<>();
+        Ordering ordering = Ordering.natural();
+        // 1、在获取数据进行实时计算之前看看数据库中是否有已经计算好的数据,如果有数据封装一下就直接返回,否则就做实时计算
+        List<SimilarityResultDTO> similarityResults = getSimilarityResults(examId, caseId, type);
+        if (similarityResults != null) {
+            similarityResults = ordering.greatestOf(similarityResults, 10);
+            // DTO -> VO
+            fillData(similarityResults, result);
+            return result;
+        }
+
+        // 2、如果数据库中没有已经计算好的数据,说明这是第一次进行相似度的分析,做数据的准备工作
         HashMap<Long, int[]> usersVector = getUsersMap(examId, caseId, type);
         if (usersVector == null) {
             return result;
         }
-        Object[] ids = usersVector.keySet().toArray();
-        for (int i = 0; i < usersVector.size(); i++) {
-            for (int j = i + 1; j < usersVector.size(); j++) {
-                long userId1 = (Long) ids[i];
-                long userId2 = (Long) ids[j];
-                double tempSimilarity = getSimilarity(usersVector.get(userId1), usersVector.get(userId2));
-                result.add(new SimilarityResultVO(null, null, userId1, userId2, tempSimilarity));
-            }
-        }
-        Ordering ordering = Ordering.natural();
+
+        // 3、数据准备好之后进行两两学生的相似度分析
+        computeSimilarity(usersVector, result);
+
+        // 4、当第一次进行计算时,将这场考试计算好的两两学生的相似度分析结果落库
+        doSaveSimilarityData(result, examId, caseId, type);
+
+        // 5、对计算结果进行排序,取两两学生相似度最好的前10位返回前端展示
         result = ordering.greatestOf(result, 10);
-        int index = 1;
-        for (SimilarityResultVO similarityResultVO : result) {
-            UserVO userVO1 = userLogic.findUserById(similarityResultVO.getUserCaseId1());
-            UserVO userVO2 = userLogic.findUserById(similarityResultVO.getUserCaseId2());
-            similarityResultVO.setRank(index++);
-            similarityResultVO.setUserName1(userVO1.getName());
-            similarityResultVO.setUserName2(userVO2.getName());
-        }
+
+        // 6、根据用户的id来获取用户的姓名,填充到SimilarityResultVO中
+        fillResultWithUserNameAndRank(result);
         return result;
     }
 
     @Override
     public List<SimilarityResultVO> getTopSimilarResultInCase(Long examId, Long caseId, String type, Long userId) {
         List<SimilarityResultVO> result = new LinkedList<>();
-        HashMap<Long, int[]> usersVector = getUsersMap(examId, caseId, type);
-        if (usersVector == null) {
-            return result;
-        }
-        int[] userVector = usersVector.get(userId);
-        for (Long id : usersVector.keySet()) {
-            if (!id.equals(userId)) {
-                double tempSimilarity = getSimilarity(userVector, usersVector.get(id));
-                SimilarityResultVO similarityResultVO = new SimilarityResultVO(null, null, caseId, id, tempSimilarity);
-                result.add(similarityResultVO);
-            }
-        }
         Ordering ordering = Ordering.natural();
-        result = ordering.greatestOf(result, 10);
-        UserVO user1VO = userLogic.findUserById(userId);
-        int index = 1;
-        for (SimilarityResultVO similarityResultVO : result) {
-            similarityResultVO.setUserName1(user1VO.getName());
-            similarityResultVO.setRank(index++);
-            similarityResultVO.setUserName2(userLogic.findUserById(similarityResultVO.getUserCaseId2()).getName());
+        /*
+          在获取数据进行实时计算之前看看数据库中是否有已经计算好的数据
+          这里一般是有数据的,因为两两相似度计算好之后,某一个人和其他人的相似度分析结果也就有了
+         */
+        List<SimilarityResultDTO> similarityResults = new ArrayList<>();
+        // 1、用userId 和 userCaseId1、userCaseId2都做匹配,获取结果
+        List<SimilarityResultDTO> matchResult = getSimilarityResultsWithSingleUser(userId, examId, caseId, type);
+        if (matchResult != null){
+            similarityResults.addAll(matchResult);
         }
+        // 2、根据相似度排序,获取前10名
+        similarityResults = ordering.greatestOf(similarityResults, 10);
+        // 3、完善单个用户的VO对象中的排名和name数据
+        fillResultWithUserNameAndRankForSingleUser(similarityResults, result,  userId, caseId);
         return result;
     }
 
@@ -133,7 +158,6 @@ public class SimilarityLogicImpl implements SimilarityLogic {
     // 根据type获得所有的node
     // 初始化矩阵
     //
-
     private List<String> getSortedTimeStamp(List<CatchDTO> catchDTOS, String type) {
         List<String> result = null;
         Set<String> timestampSet = new HashSet<>();
@@ -151,7 +175,9 @@ public class SimilarityLogicImpl implements SimilarityLogic {
             initMatrix.put(nodeName, new int[sortedTimestamp.size()]);
         }
         for (CatchDTO catchDTO : resourceCatchDTOS) {
-            initMatrix.get(catchDTO.getNodeName())[sortedTimestamp.indexOf(catchDTO.getUploadTime())] = 1;
+            if (initMatrix.containsKey(catchDTO.getNodeName())) {
+                initMatrix.get(catchDTO.getNodeName())[sortedTimestamp.indexOf(catchDTO.getUploadTime())] = 1;
+            }
         }
         return initMatrix;
     }
@@ -201,18 +227,28 @@ public class SimilarityLogicImpl implements SimilarityLogic {
 
     private HashMap<Long, int[]> getUsersMap(Long examId, Long caseId, String type) {
         HashMap<Long, int[]> result = new HashMap<>();
+        // 1、查找当场考试当个案例中所有学生的提交的命中的node的信息
         List<UserCatchDTO> userCatchDTOList = userCatchService.getUserCatchDTOs(null, examId, caseId);
+
+        // 2、判断一下当前的用户是不是管理员,否则学生的数量超过一定阈值的时候就停止计算
+        if (userCatchDTOList.size() >= SimilarityComputeAmountControl){
+            Long userId = (Long) SecurityUtils.getSubject().getSession().getAttribute("userId");
+            if (!roleLogic.getRoleIdsByUserId(userId).contains((long) RoleType.ADMIN.getCode())) {
+                return null;
+            }
+        }
+        // 3、开始获取元node
         HashMap<String, Integer> nodeNameMap = getNodeNameMap(caseId, type);
-        if (userCatchDTOList == null) {
+
+        // 4、准备每一个学生的 catch node 的向量, (userId, [1,0,0,1,0,...])的形式
+        Map<String, Long> lastIdAndUserIdMap = userCatchDTOList.stream().collect(Collectors.toMap(UserCatchDTO::getLatestId, UserCatchDTO::getUserId));
+        Map<Long, List<CatchDTO>> userIdAndCatchDTOSMap = catchService.findByIds(lastIdAndUserIdMap);
+        if (userIdAndCatchDTOSMap == null) {
             return null;
         }
         for (UserCatchDTO userCatchDTO : userCatchDTOList) {
-            List<String> ids = userCatchDTO.getSubmitIds();
-            List<CatchDTO> catchDTOs = new ArrayList<>();
-            for (String id : ids) {
-                catchDTOs.addAll(catchService.findById(id));
-            }
-            result.put(userCatchDTO.getUserId(), getUserVector(catchDTOs, nodeNameMap, type));
+            Long currentUserId = userCatchDTO.getUserId();
+            result.put(currentUserId, getUserVector(userIdAndCatchDTOSMap.get(currentUserId), nodeNameMap, type));
         }
         return result;
     }
@@ -256,6 +292,9 @@ public class SimilarityLogicImpl implements SimilarityLogic {
             if (type.equals(catchDTO.getCategory())) {
                 result.add(catchDTO.getNodeName());
             }
+//            if (type.equals(catchDTO.getCategory())) {
+            result.add(catchDTO.getNodeName());
+//            }
         }
         return result;
     }
@@ -276,4 +315,129 @@ public class SimilarityLogicImpl implements SimilarityLogic {
     }
 
 
+    private List<SimilarityResultVO> fillResultWithUserNameAndRank(List<SimilarityResultVO> result){
+        int index = 1;
+        for (SimilarityResultVO similarityResultVO : result) {
+            UserVO userVO1 = userLogic.findUserById(similarityResultVO.getUserCaseId1());
+            UserVO userVO2 = userLogic.findUserById(similarityResultVO.getUserCaseId2());
+            similarityResultVO.setRank(index++);
+            similarityResultVO.setUserName1(userVO1.getName());
+            similarityResultVO.setUserName2(userVO2.getName());
+        }
+        return result;
+    }
+
+    private List<SimilarityResultVO> computeSimilarity(HashMap<Long, int[]> usersVector, List<SimilarityResultVO> result){
+        Object[] ids = usersVector.keySet().toArray();
+        for (int i = 0; i < usersVector.size(); i++) {
+            for (int j = i + 1; j < usersVector.size(); j++) {
+                long userId1 = (Long) ids[i];
+                long userId2 = (Long) ids[j];
+                double tempSimilarity = getSimilarity(usersVector.get(userId1), usersVector.get(userId2));
+                result.add(new SimilarityResultVO(null, null, userId1, userId2, tempSimilarity));
+            }
+        }
+        return result;
+    }
+
+    /**
+     *
+     * @param userId 当前要查询的用户的id
+     * @param examId 当前的考试id
+     * @param caseId 当前的案例的 id
+     * @param type 当前的测试评估的类型 (取值 operation、line、mutation。。。)
+     * @return
+     */
+    private List<SimilarityResultDTO> getSimilarityResultsWithSingleUser(Long userId, Long examId, Long caseId, String type) {
+        List<SimilarityResultDTO> dtos = getSimilarityResults(examId, caseId, type);
+        return dtos.stream().filter(similarityResultDTO -> (similarityResultDTO.getUserCaseId1().equals(userId)
+                || similarityResultDTO.getUserCaseId2().equals(userId))).collect(Collectors.toList());
+    }
+
+    private List<SimilarityResultDTO> getSimilarityResults(Long examId, Long caseId, String type) {
+        HttpHeaders headers = MongoAPIUtils.createAuthHeaderForMongo();
+        HttpEntity<String> entity = new HttpEntity<>(headers);
+
+        RestTemplate rt = new RestTemplate();
+        Map queryParams = new HashedMap();
+        queryParams.put("examId", examId);
+        queryParams.put("caseId", caseId);
+        queryParams.put("type", type);
+
+        String filter = MongoAPIUtils.generateFilterStr(queryParams);
+        String url = MongoAPIUtils.generateFilterUrl(mongoDBConfig.getDb(), mongoDBConfig.getSimilarityResultCollection());
+        ResponseEntity<JSONObject> responseEntity = rt.exchange(url, HttpMethod.GET, entity, JSONObject.class, filter);
+        return extractSimilarityResults(responseEntity);
+    }
+
+    private List<SimilarityResultDTO> extractSimilarityResults(ResponseEntity<JSONObject> dto) {
+        JSONArray resultArray = dto.getBody().getJSONArray("_embedded");
+        if (resultArray.size() == 0) {
+            return null;
+        }
+        resultArray = resultArray.getJSONObject(0).getJSONArray("dtos");
+        List<SimilarityResultDTO> similarityResultDTOS = new ArrayList<>(resultArray.size());
+        Gson gson = new Gson();
+        resultArray.forEach(result -> {
+            SimilarityResultDTO similarityResultDTO = gson.fromJson(result.toString(), SimilarityResultDTO.class);
+            similarityResultDTOS.add(similarityResultDTO);
+        });
+        return similarityResultDTOS;
+    }
+
+    private List<SimilarityResultVO> fillData(List<SimilarityResultDTO> similarityResults, List<SimilarityResultVO> result){
+        for (SimilarityResultDTO single : similarityResults) {
+            result.add(similarityResultVOWrapper.wrap(single));
+        }
+        fillResultWithUserNameAndRank(result);
+        return result;
+    }
+
+    private void doSaveSimilarityData(List<SimilarityResultVO> result, Long examId, Long caseId, String type){
+        List<SimilarityResultDTO> dtos = new ArrayList<>(result.size());
+        for (SimilarityResultVO single : result) {
+            SimilarityResultDTO dto = similarityResultVOWrapper.unwrap(single);
+            dto.setExamId(examId);
+            dto.setCaseId(caseId);
+            dto.setType(type);
+            dtos.add(dto);
+        }
+        SimilarityResultExtendsDTO extendsDTO = new SimilarityResultExtendsDTO();
+        extendsDTO.setExamId(examId);
+        extendsDTO.setCaseId(caseId);
+        extendsDTO.setType(type);
+        extendsDTO.setDtos(dtos);
+        // 批量保存
+        saveSimilarityResultExtends(extendsDTO);
+    }
+
+    private void saveSimilarityResultExtends(SimilarityResultExtendsDTO extendsDTO) {
+        RestTemplate rt = new RestTemplate();
+        HttpHeaders headers = MongoAPIUtils.createAuthHeaderForMongo();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        HttpEntity<SimilarityResultExtendsDTO> httpEntity = new HttpEntity<>(extendsDTO, headers);
+
+        String url = MongoAPIUtils.generateCommonUrl(mongoDBConfig.getDb(), mongoDBConfig.getSimilarityResultCollection());
+        rt.exchange(url, HttpMethod.POST, httpEntity, String.class);
+    }
+
+    private List<SimilarityResultVO> fillResultWithUserNameAndRankForSingleUser(List<SimilarityResultDTO> similarityResults,
+                                                                                List<SimilarityResultVO> result, Long userId, Long caseId) {
+        UserVO user1VO = userLogic.findUserById(userId);
+        int index = 1;// 设置排名
+        for (SimilarityResultDTO single : similarityResults) {
+            SimilarityResultVO similarityResultVO = new SimilarityResultVO();
+            similarityResultVO.setSimilarity(single.getSimilarity());
+            // 对于单个的学生的相似度比较这里设置的是caseId
+            similarityResultVO.setUserCaseId1(caseId);
+            // 因为第一个人已经知道了,设置第二个id为要和特定的人比的那个人的id
+            similarityResultVO.setUserCaseId2(userId.equals(single.getUserCaseId1()) ? single.getUserCaseId2() : single.getUserCaseId1());
+            similarityResultVO.setUserName1(user1VO.getName());
+            similarityResultVO.setRank(index++);
+            similarityResultVO.setUserName2(userLogic.findUserById(similarityResultVO.getUserCaseId2()).getName());
+            result.add(similarityResultVO);
+        }
+        return result;
+    }
 }
+

+ 61 - 0
mooctest-site-server/src/test/groovy/MenuLogicImplTest.groovy

@@ -0,0 +1,61 @@
+import cn.iselab.mooctest.site.common.enums.RoleType
+import cn.iselab.mooctest.site.models.Menu
+import cn.iselab.mooctest.site.models.Role
+import cn.iselab.mooctest.site.service.MenuService
+import cn.iselab.mooctest.site.service.RoleService
+import cn.iselab.mooctest.site.web.data.wrapper.MenuVOWrapper
+import cn.iselab.mooctest.site.web.logic.impl.MenuLogicImpl
+import spock.lang.Specification
+
+class MenuLogicImplTest extends Specification {
+    def "test getMenuListByUserId"() {
+        given:
+        def roleService = Stub(RoleService.class)
+        roleService.getRolesOfUser(1L) >> {return [new Role(id: 1L), new Role(id: 2L)] }
+        def menuService = Stub(MenuService.class)
+        menuService.getMenuListByRoleId(1L) >> {return [new Menu(id: 1L, menu: "menu1")]}
+        menuService.getMenuListByRoleId(2L) >> {return [new Menu(id: 2L, menu: "menu2")]}
+        def menuLogic = new MenuLogicImpl(
+                roleService: roleService,
+                menuService: menuService,
+                menuVOWrapper: new MenuVOWrapper())
+        when:
+        def result = menuLogic.getMenuListByUserId(1L)
+
+        then:
+        result != null
+        result.size() == 2
+        result[0].menu == "menu1"
+        result[1].menu == "menu2"
+    }
+    def "test getMenuMapByUserId"() {
+        given:
+        def roleService = Stub(RoleService.class)
+        roleService.getRolesOfUser(1L) >> {return [new Role(id: 1L, ), new Role(id: 2L)] }
+        def menuService = Stub(MenuService.class)
+        menuService.getMenuListByRoleId(1L) >> {return [
+                new Menu(id: 1L, menu: "menu1", roleId: RoleType.WORKER.code),
+                new Menu(id: 1L, menu: "menu1", roleId: RoleType.WORKER.code)
+        ]}
+        menuService.getMenuListByRoleId(2L) >> {return [
+                new Menu(id: 2L, menu: "menu2", roleId: RoleType.MOOCTEST_TEACHER.code),
+                new Menu(id: 2L, menu: "menu2", roleId: RoleType.MOOCTEST_TEACHER.code)
+        ]}
+        def menuLogic = new MenuLogicImpl(
+                roleService: roleService,
+                menuService: menuService,
+                menuVOWrapper: new MenuVOWrapper())
+        when:
+        def result = menuLogic.getMenuMapByUserId(1L)
+
+        then:
+        result != null
+        result.size() == 2
+        result["student"] != null
+        result["manager"] != null
+        result["student"].size() == 1
+        result["student"][0].menu == "menu1"
+        result["manager"].size() == 1
+        result["manager"][0].menu == "menu2"
+    }
+}

+ 4 - 1
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/service/impl/GroupServiceTest.java

@@ -24,6 +24,7 @@ import org.springframework.data.domain.*;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -182,7 +183,9 @@ public class GroupServiceTest {
         //arrange
         List<Long> groupIds = new ArrayList<>();
         groupIds.add(123L);
-        when(group2WorkerDao.countByGroupId(123L)).thenReturn(12);
+        List<BigInteger> expectResult = new LinkedList<>();
+        expectResult.add(new BigInteger("12"));
+        when(group2WorkerDao.countGroupId(anyList())).thenReturn(expectResult);
         //action
         List<Integer> result = groupService.getWorkerCounts(groupIds);
         //assert

+ 5 - 2
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/ctrl/CaseControllerTest.java

@@ -11,8 +11,10 @@ import cn.iselab.mooctest.site.web.data.forMongo.CaseGraphDTO;
 import cn.iselab.mooctest.site.web.data.forMongo.graph.Category;
 import cn.iselab.mooctest.site.web.data.forMongo.graph.Edge;
 import cn.iselab.mooctest.site.web.data.forMongo.graph.Node;
+import cn.iselab.mooctest.site.web.exception.HttpNotFoundException;
 import cn.iselab.mooctest.site.web.logic.CaseLogic;
 import cn.iselab.mooctest.site.web.logic.GeneralCalculateScoreLogic;
+import cn.iselab.mooctest.site.web.logic.exception.CaseNotFoundException;
 import org.apache.shiro.session.Session;
 import org.apache.shiro.subject.Subject;
 import org.json.JSONArray;
@@ -209,10 +211,10 @@ public class CaseControllerTest extends AbstractShiroTest{
     }
 
     @Test
-    public void should_returnCaseExtendsVO_when_deleteCase() throws Exception{
+    public void should_return_CaseExtendsVO_when_admin_deleteCase_not_visible_case() throws Exception, CaseNotFoundException {
         //arrange
         expectCase.setVisible(false);
-        when(caseLogic.delete(1L)).thenReturn(expectCase);
+        when(caseLogic.delete(1L, false)).thenReturn(expectCase);
         //action
         MvcResult result = mockMvc.perform(delete("/api/case/1"))
                 .andDo(print()).andExpect(status().isOk()).andReturn();
@@ -221,6 +223,7 @@ public class CaseControllerTest extends AbstractShiroTest{
         Assert.assertEquals(expectCase.getId(),jsonObject.getLong("id"),0);
     }
 
+
     @Test
     public void should_returnCaseExtendsVO_when_getCase() throws Exception{
         //arrange

+ 2 - 0
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/ctrl/fromKibug/ScoreRuleControllerTest.java

@@ -35,6 +35,7 @@ import java.util.List;
 import java.util.Map;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.when;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -80,6 +81,7 @@ public class ScoreRuleControllerTest {
         kibugCase.setAnswerWay((long) AnswerWayConstants.APP_ECLIPSE);
         when(examService.getTask(234)).thenReturn(kibugExam);
         when(caseService.getCaseExtendsById(567)).thenReturn(kibugCase);
+        when(caseService.getCaseById(567)).thenReturn(kibugCase);
 
 
         Exam kijamExam = new Exam();

+ 48 - 3
mooctest-site-server/src/test/java/cn/iselab/mooctest/site/web/logic/impl/CaseLogicImplTest.java

@@ -29,6 +29,7 @@ import cn.iselab.mooctest.site.web.data.forMongo.graph.Node;
 import cn.iselab.mooctest.site.web.data.wrapper.CaseExtendsVOWrapper;
 import cn.iselab.mooctest.site.web.logic.Oauth2Logic;
 import cn.iselab.mooctest.site.web.logic.drools.CaseExtendsDroolsVarifyLogic;
+import cn.iselab.mooctest.site.web.logic.exception.CaseNotFoundException;
 import cn.iselab.mooctest.site.web.logic.fromDev.UpDownloadLogic;
 import com.google.gson.Gson;
 import org.apache.shiro.session.Session;
@@ -43,7 +44,7 @@ import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
-import sun.font.TrueTypeFont;
+import org.springframework.test.web.servlet.MvcResult;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -53,6 +54,9 @@ import java.util.Map;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 /**
  * Created by shanshan on 2017/6/29.
@@ -270,9 +274,27 @@ public class CaseLogicImplTest extends AbstractShiroTest {
         //assert
         verify(caseService).save(caseExtends);
     }
+    @Test
+    public void should_publicityCase_when_case_is_visibled(){
+        //arrange
+        caseExtends.setVisible(true);
+        when(caseService.getCaseExtendsById(11L)).thenReturn(caseExtends);
+        //action
+        caseLogic.publicityCase(11L);
+        //assert
+        verify(caseService, times(0)).save(caseExtends);
+    }
+    @Test(expected = IllegalArgumentException.class)
+    public void should_publicityCase_when_without_case_extents(){
+        //arrange
+        when(caseService.getCaseExtendsById(11L)).thenReturn(null);
+        //action
+        caseLogic.publicityCase(11L);
+    }
+
 
     @Test
-    public void should_returnCaseExtendsVO_when_deleteCaseExtends(){
+    public void should_returnCaseExtendsVO_when_deleteCaseExtends() throws CaseNotFoundException {
         //arrange
         UserDTOForMT user = new UserDTOForMT();
         user.setName("TEST");
@@ -288,11 +310,34 @@ public class CaseLogicImplTest extends AbstractShiroTest {
         when(user2RoleService.getByUserId(anyLong())).thenReturn(user2Roles);
 
         //action
-        CaseExtendsVO result = caseLogic.delete(11L);
+        CaseExtendsVO result = caseLogic.delete(11L, false);
         //assert
         Assert.assertEquals(caseExtendsVO.getId(),result.getId());
     }
 
+    @Test(expected = CaseNotFoundException.class)
+    public void should_throw_exception_when_deleteCase_case_no_found() throws Exception, CaseNotFoundException {
+        //action
+        caseLogic.delete(11L, false);
+    }
+    @Test(expected = IllegalArgumentException.class)
+    public void should_throw_exception_when_no_admin_deleteCase_visible_case() throws Exception, CaseNotFoundException {
+        //arrange
+        caseExtends.setVisible(true);
+        when(caseService.getCaseExtendsById(11L)).thenReturn(caseExtends);
+
+        //action
+        caseLogic.delete(11L, false);
+    }
+    @Test(expected = IllegalArgumentException.class)
+    public void should_throw_exception_when_no_admin_deleteCase_deleted_case() throws Exception, CaseNotFoundException {
+        //arrange
+        caseExtends.setDeleted(true);
+        when(caseService.getCaseExtendsById(11L)).thenReturn(caseExtends);
+
+        //action
+        caseLogic.delete(11L, false);
+    }
     @Test
     public void should_returnCaseExtends_when_givenCaseExtendsId() throws Exception{
         //arrange