瀏覽代碼

新增数据包导入功能

ting.yin 2 年之前
父節點
當前提交
0fb24c17b6

+ 3 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/AnswerCardDao.java

@@ -18,4 +18,7 @@ public interface AnswerCardDao extends PagingAndSortingRepository<AnswerCard, An
     @Query("select q from AnswerCard q where q.pk.examId=?1 order by q.pk.number")
     @Query("select q from AnswerCard q where q.pk.examId=?1 order by q.pk.number")
     List<AnswerCard> findByExamId(Integer examId);
     List<AnswerCard> findByExamId(Integer examId);
 
 
+    @Query("select q from AnswerCard q where q.pk.examId=?1 and q.subjectCode=?2 order by q.pk.number")
+    List<AnswerCard> findByExamIdAndSubjectCode(Integer examId, String subjectCode);
+
 }
 }

+ 2 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/AnswerCardService.java

@@ -16,4 +16,6 @@ public interface AnswerCardService {
 
 
     void delete(AnswerCard card);
     void delete(AnswerCard card);
 
 
+    AnswerCard findByExamIdAndSubjectCode(Integer examId, String subjectCode);
+
 }
 }

+ 6 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/AnswerCardServiceImpl.java

@@ -50,4 +50,10 @@ public class AnswerCardServiceImpl extends BaseQueryService<AnswerCard> implemen
     public void delete(AnswerCard card) {
     public void delete(AnswerCard card) {
         this.cardDao.delete(card);
         this.cardDao.delete(card);
     }
     }
+
+    @Override
+    public AnswerCard findByExamIdAndSubjectCode(Integer examId, String subjectCode) {
+        List<AnswerCard> cards = cardDao.findByExamIdAndSubjectCode(examId, subjectCode);
+        return cards.isEmpty() ? null : cards.get(0);
+    }
 }
 }

+ 137 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java

@@ -1,7 +1,13 @@
 package cn.com.qmth.stmms.admin.exam;
 package cn.com.qmth.stmms.admin.exam;
 
 
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.LinkedList;
@@ -15,9 +21,11 @@ import javax.servlet.http.HttpServletResponse;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
 import net.sf.json.JSONObject;
 
 
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.task.AsyncTaskExecutor;
 import org.springframework.core.task.AsyncTaskExecutor;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.domain.Sort.Direction;
@@ -38,11 +46,16 @@ import cn.com.qmth.stmms.admin.dto.SubjectQuestionDTO;
 import cn.com.qmth.stmms.admin.dto.SubjectiveQuestionDTO;
 import cn.com.qmth.stmms.admin.dto.SubjectiveQuestionDTO;
 import cn.com.qmth.stmms.admin.thread.ScoreCalculateThread;
 import cn.com.qmth.stmms.admin.thread.ScoreCalculateThread;
 import cn.com.qmth.stmms.admin.thread.ScoreReportThread;
 import cn.com.qmth.stmms.admin.thread.ScoreReportThread;
+import cn.com.qmth.stmms.admin.vo.StructFile;
+import cn.com.qmth.stmms.admin.vo.StructQuestion;
+import cn.com.qmth.stmms.biz.common.domain.card.CardFile;
+import cn.com.qmth.stmms.biz.exam.model.AnswerCard;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.SelectiveGroup;
 import cn.com.qmth.stmms.biz.exam.model.SelectiveGroup;
+import cn.com.qmth.stmms.biz.exam.service.AnswerCardService;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
@@ -52,6 +65,7 @@ import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.exam.service.SelectiveGroupService;
 import cn.com.qmth.stmms.biz.exam.service.SelectiveGroupService;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamQuestionSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
+import cn.com.qmth.stmms.biz.file.enums.FormatType;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
@@ -65,6 +79,7 @@ import cn.com.qmth.stmms.biz.report.service.ReportSubjectTeacherService;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.CardSource;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.LogType;
 import cn.com.qmth.stmms.common.enums.LogType;
 import cn.com.qmth.stmms.common.enums.ObjectivePolicy;
 import cn.com.qmth.stmms.common.enums.ObjectivePolicy;
@@ -76,6 +91,9 @@ import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.ImportExcel;
 import cn.com.qmth.stmms.common.utils.ImportExcel;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 
 
+import com.aliyun.oss.common.utils.BinaryUtil;
+import com.qmth.boot.tools.io.ZipReader;
+
 @Controller("examPaperController")
 @Controller("examPaperController")
 @RequestMapping("/admin/exam/paper")
 @RequestMapping("/admin/exam/paper")
 public class PaperController extends BaseExamController {
 public class PaperController extends BaseExamController {
@@ -137,6 +155,12 @@ public class PaperController extends BaseExamController {
     @Autowired
     @Autowired
     private SelectiveGroupService selectiveGroupService;
     private SelectiveGroupService selectiveGroupService;
 
 
+    @Autowired
+    private AnswerCardService answerCardService;
+
+    @Value("${file.store}")
+    private String fileStore;
+
     @Logging(menu = "试卷管理查询", type = LogType.QUERY)
     @Logging(menu = "试卷管理查询", type = LogType.QUERY)
     @RequestMapping
     @RequestMapping
     public String list(Model model, HttpServletRequest request, ExamSubjectSearchQuery query,
     public String list(Model model, HttpServletRequest request, ExamSubjectSearchQuery query,
@@ -805,4 +829,117 @@ public class PaperController extends BaseExamController {
         }
         }
         return true;
         return true;
     }
     }
+
+    @Logging(menu = "导入数据包", type = LogType.IMPORT_FILE)
+    @RequestMapping(value = "/importData", method = RequestMethod.POST)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String importData(HttpServletRequest request, MultipartFile file, RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+        List<String> error = new ArrayList<String>();
+        try {
+            byte[] data = IOUtils.toByteArray(file.getInputStream());
+            File target = new File(fileStore, "temp.zip");
+            target.getParentFile().mkdirs();
+            FileOutputStream ous = new FileOutputStream(target);
+            ous.write(data);
+            IOUtils.closeQuietly(ous);
+
+            ZipReader zipReader = new ZipReader(target);
+            for (String subjectCode : zipReader.list(false)) {
+                try {
+                    ExamSubject subject = subjectService.find(examId, subjectCode);
+                    if (subject == null) {
+                        error.add("[" + subjectCode + "] 科目代码不存在;");
+                        continue;
+                    }
+                    if (subject.getTotalScore() != 0) {
+                        error.add("[" + subjectCode + "] 科目代码已经存在题目;");
+                        continue;
+                    }
+                    InputStream cardIns = zipReader.read(subjectCode, "card.json");
+                    CardFile cardFile = CardFile.parse(cardIns);
+                    if (cardFile.getPages().size() != 1 && cardFile.getPages().size() % 2 != 0) {
+                        error.add("[" + subjectCode + "] 科目代码卡格式解析有误;");
+                    }
+                    byte[] out = cardFile.output();
+                    String md5 = BinaryUtil.encodeMD5(out);
+                    AnswerCard card = answerCardService.findByExamIdAndSubjectCode(examId, subject.getCode());
+                    if (card == null) {
+                        card = new AnswerCard();
+                        card.setExamId(examId);
+                        Integer number = answerCardService.findMaxNumberByExamId(examId) + 1;
+                        card.setNumber(number);
+                        card.setSubjectCode(subject.getCode());
+                    }
+                    card.setMd5(md5);
+                    card.setNeedAdapte(true);
+                    card.setSliceConfig(cardFile.getSliceConfig().toString());
+                    card.setSinglePage(cardFile.getPages().size() == 1);
+                    card.setPaperCount(card.getSinglePage() ? 1 : cardFile.getPages().size() / 2);
+                    card.setSource(CardSource.WEB);
+                    card.setUpdateTime(new Date());
+                    answerCardService.save(card);
+
+                    fileService.uploadAnswerCard(new ByteArrayInputStream(out), md5, examId, card.getNumber());
+                    subject.setSliceConfig(cardFile.getSliceConfig().toString());
+                    subject.setCardType(FormatType.JSON);
+                    subjectService.save(subject);
+
+                    InputStream structIns = zipReader.read(subjectCode, "struct.json");
+                    StructFile structFile = StructFile.parse(structIns);
+                    if (!structFile.getObjective().isEmpty()) {
+                        List<ExamQuestion> oList = new ArrayList<ExamQuestion>();
+                        for (StructQuestion structQuestion : structFile.getSubjective()) {
+                            ExamQuestion q = structQuestion.transform();
+                            q.setPaperType(structFile.getPaperType());
+                            q.setExamId(examId);
+                            q.setSubjectCode(subject.getCode());
+                            oList.add(q);
+                        }
+                        questionService.save(oList);
+                        examService.updateObjectiveStatus(examId, ObjectiveStatus.WAITING);
+                        subjectService.updateScore(examId, subject.getCode(), true,
+                                questionService.sumTotalScore(examId, subject.getCode(), true));
+                    }
+                    if (!structFile.getSubjective().isEmpty()) {
+                        List<ExamQuestion> sList = new ArrayList<ExamQuestion>();
+                        Map<Integer, Double> mainMap = new HashMap<Integer, Double>();
+                        for (StructQuestion structQuestion : structFile.getSubjective()) {
+                            ExamQuestion q = structQuestion.transform();
+                            q.setPaperType(structFile.getPaperType());
+                            q.setExamId(examId);
+                            q.setSubjectCode(subject.getCode());
+                            q.setGroupNumber(q.getMainNumber());
+                            sList.add(q);
+                            Double totalScore = mainMap.get(q.getMainNumber());
+                            if (totalScore == null) {
+                                mainMap.put(q.getMainNumber(), q.getTotalScore());
+                            } else {
+                                mainMap.put(q.getMainNumber(), totalScore + q.getTotalScore());
+                            }
+                        }
+                        questionService.save(sList);
+                        for (Integer number : mainMap.keySet()) {
+                            MarkGroup group = new MarkGroup(examId, subject.getCode(), number, null,
+                                    mainMap.get(number), null, null, null, null, 0, false, false, null, false);
+                            groupService.save(group);
+                        }
+                        subjectService.updateScore(examId, subject.getCode(), false,
+                                questionService.sumTotalScore(examId, subject.getCode(), false));
+                    }
+                } catch (Exception e) {
+                    log.error("parse zip data error", e);
+                    error.add("[" + subjectCode + "] 科目代码导入失败;");
+                }
+            }
+            target.delete();
+        } catch (IOException e) {
+            log.error("parse question excel error", e);
+            error.add("zip文件解析失败");
+        }
+        if (error.size() > 0) {
+            addMessage(redirectAttributes, StringUtils.join(error, "<br\\>"));
+        }
+        return "redirect:/admin/exam/paper";
+    }
 }
 }

+ 59 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/vo/StructFile.java

@@ -0,0 +1,59 @@
+package cn.com.qmth.stmms.admin.vo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * 卡格式文件内容结构
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class StructFile {
+
+    private String paperType;
+
+    private List<StructQuestion> objective = Collections.emptyList();
+
+    private List<StructQuestion> subjective = Collections.emptyList();
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public List<StructQuestion> getObjective() {
+        return objective;
+    }
+
+    public void setObjective(List<StructQuestion> objective) {
+        this.objective = objective;
+    }
+
+    public List<StructQuestion> getSubjective() {
+        return subjective;
+    }
+
+    public void setSubjective(List<StructQuestion> subjective) {
+        this.subjective = subjective;
+    }
+
+    /**
+     * 从输入流解析卡格式文件
+     *
+     * @param ins
+     * @return
+     * @throws IOException
+     */
+    public static StructFile parse(InputStream ins) throws IOException {
+        ObjectMapper mapper = new ObjectMapper();
+        StructFile obj = mapper.readValue(ins, StructFile.class);
+        return obj;
+    }
+}

+ 101 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/vo/StructQuestion.java

@@ -0,0 +1,101 @@
+package cn.com.qmth.stmms.admin.vo;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class StructQuestion {
+
+    @JsonProperty("mainNumber")
+    private Integer mainNumber;
+
+    @JsonProperty("subNumber")
+    private String subNumber;
+
+    @JsonProperty("mainTitle")
+    private String mainTitle;
+
+    @JsonProperty("answer")
+    private String answer;
+
+    @JsonProperty("totalScore")
+    private Double totalScore;
+
+    @JsonProperty("intervalScore")
+    private Double intervalScore;
+
+    public ExamQuestion transform() {
+        ExamQuestion question = new ExamQuestion();
+        question.setMainNumber(mainNumber);
+        question.setSubNumber(subNumber);
+        question.setMainTitle(StringUtils.trimToNull(mainTitle));
+        question.setAnswer(StringUtils.trimToNull(answer));
+        question.setTotalScore(totalScore);
+        if (intervalScore == null) {
+            question.setIntervalScore(intervalScore);
+        } else {
+            if (totalScore % 1 == 0) {
+                question.setIntervalScore(1d);
+            } else {
+                question.setIntervalScore(0.5);
+            }
+        }
+        return question;
+    }
+
+    public StructQuestion() {
+    }
+
+    public Integer getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(Integer mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    public String getSubNumber() {
+        return subNumber;
+    }
+
+    public void setSubNumber(String subNumber) {
+        this.subNumber = subNumber;
+    }
+
+    public String getMainTitle() {
+        return mainTitle;
+    }
+
+    public void setMainTitle(String mainTitle) {
+        this.mainTitle = mainTitle;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Double getIntervalScore() {
+        return intervalScore;
+    }
+
+    public void setIntervalScore(Double intervalScore) {
+        this.intervalScore = intervalScore;
+    }
+
+}

+ 2 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/CardController.java

@@ -154,8 +154,9 @@ public class CardController extends BaseApiController {
             result.accumulate("source", answerCard.getSource().toString());
             result.accumulate("source", answerCard.getSource().toString());
             result.accumulate("parameter", StringUtils.trimToEmpty(answerCard.getParameter()));
             result.accumulate("parameter", StringUtils.trimToEmpty(answerCard.getParameter()));
             result.accumulate("singlePage", answerCard.getSinglePage());
             result.accumulate("singlePage", answerCard.getSinglePage());
-            result.accumulate("remark", StringUtils.trimToEmpty(StringUtils.trimToEmpty(answerCard.getRemark())));
+            result.accumulate("remark", StringUtils.trimToEmpty(answerCard.getRemark()));
             result.accumulate("updateTime", answerCard.getUpdateTime().getTime());
             result.accumulate("updateTime", answerCard.getUpdateTime().getTime());
+            result.accumulate("sliceConfig", StringUtils.trimToEmpty(answerCard.getSliceConfig()));
             array.add(result);
             array.add(result);
         }
         }
         return array;
         return array;

+ 48 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/FileController.java

@@ -1,6 +1,9 @@
 package cn.com.qmth.stmms.api.controller;
 package cn.com.qmth.stmms.api.controller;
 
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
@@ -22,12 +25,15 @@ import org.springframework.web.multipart.MultipartFile;
 
 
 import cn.com.qmth.stmms.admin.utils.PaperJsonUtils;
 import cn.com.qmth.stmms.admin.utils.PaperJsonUtils;
 import cn.com.qmth.stmms.api.exception.ApiException;
 import cn.com.qmth.stmms.api.exception.ApiException;
+import cn.com.qmth.stmms.biz.common.domain.card.CardFile;
+import cn.com.qmth.stmms.biz.exam.model.AnswerCard;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamPackage;
 import cn.com.qmth.stmms.biz.exam.model.ExamPackage;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.service.AnswerCardService;
 import cn.com.qmth.stmms.biz.exam.service.ExamPackageService;
 import cn.com.qmth.stmms.biz.exam.service.ExamPackageService;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
@@ -38,6 +44,7 @@ import cn.com.qmth.stmms.biz.file.enums.FormatType;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.ApiUser;
 import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.CardSource;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
 import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
@@ -78,6 +85,9 @@ public class FileController extends BaseApiController {
     @Autowired
     @Autowired
     private MarkGroupService groupService;
     private MarkGroupService groupService;
 
 
+    @Autowired
+    private AnswerCardService answerCardService;
+
     private Exam validateExam(ApiUser au, Integer examId, ExamType... types) {
     private Exam validateExam(ApiUser au, Integer examId, ExamType... types) {
         Exam exam = examService.findById(examId);
         Exam exam = examService.findById(examId);
         if (exam == null || !exam.getSchoolId().equals(au.getSchoolId()) || exam.getStatus() != ExamStatus.START) {
         if (exam == null || !exam.getSchoolId().equals(au.getSchoolId()) || exam.getStatus() != ExamStatus.START) {
@@ -129,6 +139,15 @@ public class FileController extends BaseApiController {
         }
         }
     }
     }
 
 
+    private void validatePageCount(Integer pageCount) {
+        if (pageCount == null) {
+            throw ApiException.QUERY_PARAM_ERROR.replaceMessage("card page count is null");
+        }
+        if (pageCount != 1 && pageCount % 2 != 0) {
+            throw ApiException.QUERY_PARAM_ERROR.replaceMessage("card page count error");
+        }
+    }
+
     @RequestMapping(value = "/sheet/upload", method = RequestMethod.POST)
     @RequestMapping(value = "/sheet/upload", method = RequestMethod.POST)
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCHOOL_DEV, Role.SCANNER })
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCHOOL_DEV, Role.SCANNER })
     @ResponseBody
     @ResponseBody
@@ -311,18 +330,45 @@ public class FileController extends BaseApiController {
             validateFormatType(format, FormatType.JSON, FormatType.ZIP);
             validateFormatType(format, FormatType.JSON, FormatType.ZIP);
         }
         }
         String uri = "";
         String uri = "";
+        InputStream ins = null;
+        AnswerCard card = null;
         try {
         try {
+            if (format.equals(FormatType.JSON)) {
+                CardFile cardFile = CardFile.parse(file.getInputStream());
+                validatePageCount(cardFile.getPages().size());
+                byte[] out = cardFile.output();
+                ins = new ByteArrayInputStream(out);
+                card = answerCardService.findByExamIdAndSubjectCode(examId, subjectCode);
+                if (card == null) {
+                    card = new AnswerCard();
+                }
+                card.setExamId(examId);
+                card.setNumber(1);
+                card.setMd5(md5);
+                card.setNeedAdapte(cardFile.isAdapted());
+                card.setSliceConfig(cardFile.getSliceConfig().toString());
+                card.setSinglePage(cardFile.getPages().size() == 1);
+                card.setPaperCount(card.getSinglePage() ? 1 : cardFile.getPages().size() / 2);
+                card.setSource(CardSource.WEB);
+                card.setSubjectCode(subjectCode);
+                card.setUpdateTime(new Date());
+            } else {
+                ins = file.getInputStream();
+            }
             if (subject != null) {
             if (subject != null) {
-                fileService.uploadCard(file.getInputStream(), md5, examId, subjectCode, format);
+                fileService.uploadCard(ins, md5, examId, subjectCode, format);
                 subject.setCardType(format);
                 subject.setCardType(format);
                 subjectService.save(subject);
                 subjectService.save(subject);
                 uri = fileService.getCardUri(examId, subjectCode, format);
                 uri = fileService.getCardUri(examId, subjectCode, format);
             } else {
             } else {
-                fileService.uploadCard(file.getInputStream(), md5, examId, format);
+                fileService.uploadCard(ins, md5, examId, format);
                 exam.setCardType(format);
                 exam.setCardType(format);
                 examService.save(exam);
                 examService.save(exam);
                 uri = fileService.getCardUri(examId, format);
                 uri = fileService.getCardUri(examId, format);
             }
             }
+            if (card != null) {
+                answerCardService.save(card);
+            }
         } catch (Exception e) {
         } catch (Exception e) {
             log.error("card upload error", e);
             log.error("card upload error", e);
             throw ApiException.FILE_UPLOAD_ERROR.replaceMessage("card upload error: " + e.getMessage());
             throw ApiException.FILE_UPLOAD_ERROR.replaceMessage("card upload error: " + e.getMessage());

+ 18 - 3
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/paperList.jsp

@@ -30,7 +30,7 @@
     </form>
     </form>
 </div>
 </div>
 <div id="groupImportBox" class="hide">
 <div id="groupImportBox" class="hide">
-    <form id="subImportForm" action="${ctx}/admin/exam/paper/importGroup" method="post" enctype="multipart/form-data"
+    <form id="groupImportForm" action="${ctx}/admin/exam/paper/importGroup" method="post" enctype="multipart/form-data"
           style="padding-left:20px;text-align:center;" class="form-search" onsubmit="loading('正在导入,请稍等...');"><br/>
           style="padding-left:20px;text-align:center;" class="form-search" onsubmit="loading('正在导入,请稍等...');"><br/>
         <input name="objective" type="hidden" value="false"/>
         <input name="objective" type="hidden" value="false"/>
         <input name="file" type="file" style="width:330px"/><br/><br/>  
         <input name="file" type="file" style="width:330px"/><br/><br/>  
@@ -38,6 +38,14 @@
         <a href="${ctx}/admin/exam/paper/template?objective=false">下载模板</a>
         <a href="${ctx}/admin/exam/paper/template?objective=false">下载模板</a>
     </form>
     </form>
 </div>
 </div>
+<div id="dataImportBox" class="hide">
+    <form id="dataImportForm" action="${ctx}/admin/exam/paper/importData" method="post" enctype="multipart/form-data"
+          style="padding-left:20px;text-align:center;" class="form-search" onsubmit="loading('正在导入,请稍等...');"><br/>
+        <input name="objective" type="hidden" value="false"/>
+        <input name="file" type="file" style="width:330px"/><br/><br/>  
+        <input class="btn btn-primary" type="submit" value="导入" onclick="goDataImport()"/>
+    </form>
+</div>
 <%-- <%@include file="/WEB-INF/views/include/examInfoNavTabs.jsp" %> --%>
 <%-- <%@include file="/WEB-INF/views/include/examInfoNavTabs.jsp" %> --%>
 <form id="searchForm" action="${ctx}/admin/exam/paper" method="post" class="breadcrumb form-search">
 <form id="searchForm" action="${ctx}/admin/exam/paper" method="post" class="breadcrumb form-search">
     <input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber }"/>
     <input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber }"/>
@@ -106,10 +114,11 @@
             &nbsp;
             &nbsp;
             <a href="${ctx}/admin/exam/paper/report" class="btn">分析计算</a>
             <a href="${ctx}/admin/exam/paper/report" class="btn">分析计算</a>
         </c:if>
         </c:if>
-        <c:if test="${examLock}"
-        >&nbsp;
+        <c:if test="${examLock}">&nbsp;
             <a href="#" class="btn" disabled="disabled">正在计算</a>
             <a href="#" class="btn" disabled="disabled">正在计算</a>
         </c:if>
         </c:if>
+        &nbsp;
+        <input id="data-import" class="btn" type="button" value="导入数据包"/>
     </div>
     </div>
 </form>
 </form>
 <tags:message content="${message}"/>
 <tags:message content="${message}"/>
@@ -208,6 +217,12 @@
             bottomText: "导入文件不能超过5M,仅允许导入“xls”或“xlsx”格式文件!"
             bottomText: "导入文件不能超过5M,仅允许导入“xls”或“xlsx”格式文件!"
         });
         });
     });
     });
+    $("#data-import").click(function () {
+        $.jBox($("#dataImportBox").html(), {
+            title: "导入数据包", buttons: {"关闭": true},
+            bottomText: "导入文件不能超过5M,仅允许导入“zip”格式文件!"
+        });
+    });
     function goObjImport() {
     function goObjImport() {
         alert("导入后请务必重新统分");
         alert("导入后请务必重新统分");
     };
     };

+ 4 - 1
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/qualityList.jsp

@@ -75,7 +75,10 @@
 				<td>
 				<td>
 				<c:choose>
 				<c:choose>
 		        <c:when test="${marker.finishCount!=null && marker.rejectCount!=null && marker.finishCount>0 && (marker.finishCount-marker.rejectCount)>0}">
 		        <c:when test="${marker.finishCount!=null && marker.rejectCount!=null && marker.finishCount>0 && (marker.finishCount-marker.rejectCount)>0}">
-		        <fmt:formatNumber type="percent" maxIntegerDigits="3" value="${(marker.finishCount-marker.rejectCount)/marker.finishCount}" />
+		        	<fmt:formatNumber type="percent" maxIntegerDigits="3" value="${(marker.finishCount-marker.rejectCount)/marker.finishCount}" />
+		        </c:when>
+		        <c:when test="${marker.rejectCount==null || marker.rejectCount==0">
+		        	100%
 		        </c:when>
 		        </c:when>
 		        <c:otherwise>0%</c:otherwise>
 		        <c:otherwise>0%</c:otherwise>
 		        </c:choose>
 		        </c:choose>