Browse Source

试卷结构

ywx
王宁 2 months ago
parent
commit
b1008fcf2d
26 changed files with 1724 additions and 40 deletions
  1. 1
    1
      slog/src/main/resources/application.properties
  2. 67
    24
      smarking/src/main/java/com/xhkjedu/smarking/controller/paper/MsPaperQtypeController.java
  3. 28
    0
      smarking/src/main/java/com/xhkjedu/smarking/listener/HandleLogSender.java
  4. 174
    0
      smarking/src/main/java/com/xhkjedu/smarking/listener/MessageConsumer.java
  5. 137
    0
      smarking/src/main/java/com/xhkjedu/smarking/listener/MessageSender.java
  6. 4
    0
      smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperAnalyzeMapper.java
  7. 4
    0
      smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperFileMapper.java
  8. 9
    0
      smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperMapper.java
  9. 14
    0
      smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperQtypeMapper.java
  10. 14
    0
      smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperQtypeQuestionMapper.java
  11. 9
    0
      smarking/src/main/java/com/xhkjedu/smarking/model/paper/MsPaper.java
  12. 1
    1
      smarking/src/main/java/com/xhkjedu/smarking/model/paper/MsPaperFile.java
  13. 6
    0
      smarking/src/main/java/com/xhkjedu/smarking/model/paper/MsPaperQtype.java
  14. 731
    13
      smarking/src/main/java/com/xhkjedu/smarking/service/paper/MsPaperQtypeService.java
  15. 77
    0
      smarking/src/main/java/com/xhkjedu/smarking/utils/ConvertUtil.java
  16. 35
    0
      smarking/src/main/java/com/xhkjedu/smarking/vo/paper/MsPaperQsVo.java
  17. 122
    0
      smarking/src/main/java/com/xhkjedu/smarking/vo/paper/MsPaperQtypeQuestionVo.java
  18. 36
    0
      smarking/src/main/java/com/xhkjedu/smarking/vo/paper/MsPaperQtypeVo.java
  19. 33
    0
      smarking/src/main/java/com/xhkjedu/smarking/vo/paper/QuestionOrderVo.java
  20. 21
    0
      smarking/src/main/java/com/xhkjedu/smarking/vo/paper/QuestionPointVo.java
  21. 20
    1
      smarking/src/main/resources/application.properties
  22. 5
    0
      smarking/src/main/resources/mapper/paper/MsPaperAnalyzeMapper.xml
  23. 5
    0
      smarking/src/main/resources/mapper/paper/MsPaperFileMapper.xml
  24. 12
    0
      smarking/src/main/resources/mapper/paper/MsPaperMapper.xml
  25. 110
    0
      smarking/src/main/resources/mapper/paper/MsPaperQtypeMapper.xml
  26. 49
    0
      smarking/src/main/resources/mapper/paper/MsPaperQtypeQuestionMapper.xml

+ 1
- 1
slog/src/main/resources/application.properties View File

@@ -36,7 +36,7 @@ mapper.mappers=com.xhkjedu.base.TkMapper
36 36
 mybatis.configuration.call-setters-on-nulls=true
37 37
 mapper.identity=MYSQL
38 38
 #\u6253\u5370\u65E5\u5FD7
39
-logging.level.com.xhkjedu.sclass.mapper=debug
39
+logging.level.com.xhkjedu.slog.mapper=debug
40 40
 #\u5206\u9875
41 41
 spring.pagehelper.helper-dialect=mysql
42 42
 

+ 67
- 24
smarking/src/main/java/com/xhkjedu/smarking/controller/paper/MsPaperQtypeController.java View File

@@ -1,14 +1,16 @@
1 1
 package com.xhkjedu.smarking.controller.paper;
2 2
 
3
-import com.github.pagehelper.PageHelper;
4
-import com.github.pagehelper.PageInfo;
3
+import com.xhkjedu.smarking.model.paper.MsPaper;
4
+import com.xhkjedu.smarking.model.paper.MsPaperFile;
5 5
 import com.xhkjedu.smarking.model.paper.MsPaperQtype;
6
+import com.xhkjedu.smarking.model.paper.MsPaperQtypeQuestion;
6 7
 import com.xhkjedu.smarking.service.paper.MsPaperQtypeService;
7 8
 import com.xhkjedu.utils.N_Utils;
8
-import com.xhkjedu.utils.PageUtil;
9
-import com.xhkjedu.vo.PageResult;
10 9
 import com.xhkjedu.vo.ResultVo;
11
-import org.springframework.web.bind.annotation.*;
10
+import org.springframework.web.bind.annotation.PostMapping;
11
+import org.springframework.web.bind.annotation.RequestBody;
12
+import org.springframework.web.bind.annotation.RequestMapping;
13
+import org.springframework.web.bind.annotation.RestController;
12 14
 
13 15
 import javax.annotation.Resource;
14 16
 import java.util.List;
@@ -19,7 +21,7 @@ import java.util.List;
19 21
  * @Date 2024-10-15
20 22
  */
21 23
 @RestController
22
-@RequestMapping("/msPaperQtype")
24
+@RequestMapping("/pqtype")
23 25
 public class MsPaperQtypeController {
24 26
     @Resource
25 27
     private MsPaperQtypeService msPaperQtypeService;
@@ -32,26 +34,67 @@ public class MsPaperQtypeController {
32 34
      * @return com.xhkjedu.vo.ResultVo
33 35
      **/
34 36
     @PostMapping("/save")
35
-    public ResultVo save(@RequestBody MsPaperQtype msPaperQtype) {
36
-        msPaperQtypeService.add(msPaperQtype);
37
-        return new ResultVo(0, "保存成功!");
37
+    public ResultVo save(@RequestBody MsPaper paper) {
38
+        N_Utils.validation(new Object[]{paper.getMpid(),"试卷id",1});
39
+        List<MsPaperQtype> qtypes = paper.getQtypes();
40
+        if(qtypes == null || qtypes.size() == 0){
41
+            return new ResultVo(1,"未设置试题");
42
+        }
43
+        return msPaperQtypeService.savePaperQtype(paper);
38 44
     }
39 45
 
40
-    /**
41
-     * @Description 列表
42
-     * @Param [msPaperQtype]
43
-     * @Author auto
44
-     * @Date 2024-10-15
45
-     * @return com.xhkjedu.vo.ResultVo
46
+    /*
47
+     * @Description 试卷答案-客观题
48
+     * @Date 2024/10/17 15:47:05
49
+     * @Author WN
50
+     * @Param [qtype]
51
+     * @Return com.xhkjedu.vo.ResultVo
46 52
      **/
47
-    @PostMapping("/list")
48
-    public ResultVo queryList(@RequestBody MsPaperQtype msPaperQtype) {
49
-        Integer page = msPaperQtype.getPage();
50
-        Integer size = msPaperQtype.getPageSize();
51
-        N_Utils.validation(new Object[]{page, "显示页码", 1, size, "显示条数", 1});
52
-        PageHelper.startPage(page, size);
53
-        List<MsPaperQtype> list = msPaperQtypeService.queryList(msPaperQtype);
54
-        PageResult pageResult = PageUtil.getPageResult(new PageInfo<>(list));
55
-        return new ResultVo(0, "查询成功!", pageResult);
53
+    @PostMapping("/answer_obj")
54
+    public ResultVo answerObjective(@RequestBody MsPaperQtype qtype){
55
+        N_Utils.validation(new Object[]{qtype.getMpid(),"试卷id",1});
56
+        List<MsPaperQtypeQuestion> questions = qtype.getQuestions();
57
+        if(questions == null || questions.size() == 0){
58
+            return new ResultVo(1,"未设置试题答案");
59
+        }
60
+        return msPaperQtypeService.answerObjective(qtype);
56 61
     }
62
+
63
+    /*
64
+     * @Description 试卷答案-主观题答案
65
+     * @Date 2024/10/17 16:01:07
66
+     * @Author WN
67
+     * @Param [paper]
68
+     * @Return com.xhkjedu.vo.ResultVo
69
+     **/
70
+    @PostMapping("/answer_sub")
71
+    public ResultVo answerSubjective(@RequestBody MsPaper paper){
72
+        N_Utils.validation(new Object[]{paper.getMpid(),"试卷id",1});
73
+        List<MsPaperFile> pfiles = paper.getPfiles();
74
+        if(pfiles == null || pfiles.size() == 0){
75
+            return new ResultVo(1,"未上传文件");
76
+        }
77
+        msPaperQtypeService.answerSubjective(paper);
78
+        return new ResultVo(0,"保存成功");
79
+    }
80
+
81
+    /*
82
+     * @Description 试卷文件
83
+     * @Date 2024/10/17 16:01:07
84
+     * @Author WN
85
+     * @Param [paper]
86
+     * @Return com.xhkjedu.vo.ResultVo
87
+     **/
88
+    @PostMapping("/save_file")
89
+    public ResultVo savePaperFiles(@RequestBody MsPaper paper){
90
+        N_Utils.validation(new Object[]{paper.getMpid(),"试卷id",1});
91
+        List<MsPaperFile> pfiles = paper.getPfiles();
92
+        if(pfiles == null || pfiles.size() == 0){
93
+            return new ResultVo(1,"未上传文件");
94
+        }
95
+        msPaperQtypeService.savePaperFiles(paper);
96
+        return new ResultVo(0,"保存成功");
97
+    }
98
+
99
+
57 100
 }

+ 28
- 0
smarking/src/main/java/com/xhkjedu/smarking/listener/HandleLogSender.java View File

@@ -0,0 +1,28 @@
1
+package com.xhkjedu.smarking.listener;
2
+
3
+import com.alibaba.fastjson.JSON;
4
+import com.xhkjedu.model.slog.THandleLog;
5
+import org.springframework.amqp.core.AmqpTemplate;
6
+import org.springframework.beans.factory.annotation.Autowired;
7
+import org.springframework.beans.factory.annotation.Value;
8
+import org.springframework.stereotype.Component;
9
+
10
+/*
11
+ * @Description: 操作日志
12
+ * @Author: WN
13
+ * @Date: 2023/11/15 16:20:25
14
+ **/
15
+@Component
16
+public class HandleLogSender {
17
+    @Autowired
18
+    private AmqpTemplate rabbitTemplate;
19
+    //生产者mq
20
+    @Value("${rabbitmq.handleLogQueue}")
21
+    private String handleLogQueue;
22
+
23
+    //操作日志
24
+    public void handleLogSend(THandleLog handleLog) {
25
+        rabbitTemplate.convertAndSend(handleLogQueue, JSON.toJSONString(handleLog));
26
+    }
27
+
28
+}

+ 174
- 0
smarking/src/main/java/com/xhkjedu/smarking/listener/MessageConsumer.java View File

@@ -0,0 +1,174 @@
1
+// package com.xhkjedu.smarking.listener;
2
+//
3
+// import com.alibaba.fastjson.JSON;
4
+// import com.xhkjedu.sexam.mapper.exam.EBaseMapper;
5
+// import com.xhkjedu.sexam.mapper.paper.EPaperFileMapper;
6
+// import com.xhkjedu.sexam.mapper.paper.EPaperMapper;
7
+// import com.xhkjedu.sexam.mapper.paperstudent.EPaperStudentQuestionMapper;
8
+// import com.xhkjedu.sexam.mapper.paperstudent.EScanerrorMapper;
9
+// import com.xhkjedu.sexam.mapper.report.EReportGenerateMapper;
10
+// import com.xhkjedu.sexam.model.paper.EPaper;
11
+// import com.xhkjedu.sexam.model.paper.EPaperFile;
12
+// import com.xhkjedu.sexam.model.paperstudent.EPaperStudentQuestion;
13
+// import com.xhkjedu.sexam.model.paperstudent.EScanerror;
14
+// import com.xhkjedu.sexam.utils.ConvertUtil;
15
+// import com.xhkjedu.sexam.vo.report.EReportVo;
16
+// import lombok.extern.slf4j.Slf4j;
17
+// import org.springframework.amqp.core.Message;
18
+// import org.springframework.amqp.rabbit.annotation.RabbitHandler;
19
+// import org.springframework.amqp.rabbit.annotation.RabbitListener;
20
+// import org.springframework.beans.factory.annotation.Autowired;
21
+// import org.springframework.stereotype.Component;
22
+//
23
+// import javax.annotation.Resource;
24
+// import java.time.LocalDateTime;
25
+// import java.util.HashMap;
26
+// import java.util.Map;
27
+//
28
+// /**
29
+//  * 消息到达消费者监听类
30
+//  */
31
+// @Component
32
+// @Slf4j
33
+// public class MessageConsumer {
34
+//
35
+//     @Resource
36
+//     private EPaperStudentQuestionMapper ePaperStudentQuestionMapper;
37
+//     @Resource
38
+//     private EPaperMapper ePaperMapper;
39
+//     @Resource
40
+//     private EScanerrorMapper eScanerrorMapper;
41
+//     @Resource
42
+//     private EPaperFileMapper ePaperFileMapper;
43
+//     @Resource
44
+//     private EReportGenerateMapper eReportGenerateMapper;
45
+//     @Resource
46
+//     private EBaseMapper eBaseMapper;
47
+//     @Autowired
48
+//     private ConvertUtil convertUtil;
49
+//
50
+//
51
+//     /*
52
+//      * 学生答案图片合并完处理转换状态
53
+//      *
54
+//      * @param [message]
55
+//      * @return void
56
+//      * @author ywx
57
+//      * @date 2022/5/9 13:44
58
+//      */
59
+//     @RabbitHandler
60
+//     @RabbitListener(queues = "${rabbitmq.examImgMergeHandleQueue}")
61
+//     public void imgMergeHandleQueue(Message message) {
62
+//         try {
63
+//             String msg = new String(message.getBody(), "UTF-8");
64
+//             System.out.println(LocalDateTime.now() + "学生试题答案合并接收消息内容:" + msg);
65
+//             EPaperStudentQuestion psq = JSON.parseObject(msg, EPaperStudentQuestion.class);
66
+//             if (psq.getConverted() != 1) {//合并失败不修改学生答案
67
+//                 psq.setStuanswer(null);
68
+//             } else {
69
+//                 psq.setStuanswer(JSON.toJSONString(new String[]{psq.getStuanswer()}));
70
+//             }
71
+//             ePaperStudentQuestionMapper.updateByPrimaryKeySelective(psq);
72
+//         } catch (Exception e) {
73
+//             log.error("学生试题答案合并失败:" + e.getMessage());
74
+//         }
75
+//     }
76
+//
77
+//     /**
78
+//      * @Description 修改试卷答题卡pdf
79
+//      * @Param [message]
80
+//      * @Return void
81
+//      * @Author wn
82
+//      * @Date 2022/8/2 14:00
83
+//      **/
84
+//     @RabbitHandler
85
+//     @RabbitListener(queues = "${rabbitmq.examBaseToPdfHandleQueue}")
86
+//     public void baseToPdfHandleQueue(Message message) {
87
+//         try {
88
+//             String msg = new String(message.getBody(), "UTF-8");
89
+//             System.out.println(LocalDateTime.now() + "答题卡图片合并接收消息内容:" + msg);
90
+//             EPaper paper = JSON.parseObject(msg, EPaper.class);
91
+//             ePaperMapper.updatePaperScantronpdf(paper);
92
+//         } catch (Exception e) {
93
+//             log.error("答题卡图片合并失败:" + e.getMessage());
94
+//         }
95
+//     }
96
+//
97
+//     /**
98
+//      * @Description 扫码出错答题卡base64转图片
99
+//      * @Param [message]
100
+//      * @Return void
101
+//      * @Author wn
102
+//      * @Date 2022/9/19 9:46
103
+//      **/
104
+//     @RabbitHandler
105
+//     @RabbitListener(queues = "${rabbitmq.examBaseToScanImgHandleQueue}")
106
+//     public void baseToScanImgHandleQueue(Message message) {
107
+//         try {
108
+//             String msg = new String(message.getBody(), "UTF-8");
109
+//             System.out.println(LocalDateTime.now() + "扫码出错答题卡base64转图片接收消息内容:" + msg);
110
+//             EScanerror eScanerror = JSON.parseObject(msg, EScanerror.class);
111
+//             if (eScanerror.getConverted() != null && eScanerror.getConverted() == 1) {
112
+//                 eScanerrorMapper.updateErrorStupic(eScanerror.getSerrorid(), eScanerror.getStupic());
113
+//             }
114
+//         } catch (Exception e) {
115
+//             log.error("扫码出错答题卡base64转图片失败:" + e.getMessage());
116
+//         }
117
+//     }
118
+//
119
+//     /**
120
+//      * @Description 考试附件试卷图片转pdf
121
+//      * @Param [message]
122
+//      * @Return void
123
+//      * @Author wn
124
+//      * @Date 2022/11/1 10:45
125
+//      **/
126
+//     @RabbitHandler
127
+//     @RabbitListener(queues = "${rabbitmq.examImgToPdfHandleQueue}")
128
+//     public void examImageToPdfHandleQueue(Message message) {
129
+//         try {
130
+//             String msg = new String(message.getBody(), "UTF-8");
131
+//             System.out.println(LocalDateTime.now() + "考试附件试卷图片转pdf接收消息内容:" + msg);
132
+//             EPaperFile paperFile = JSON.parseObject(msg, EPaperFile.class);
133
+//             if (paperFile.getConverted() != null && paperFile.getConverted() == 1) {
134
+//                 ePaperFileMapper.updateSourcepathByEpid(paperFile.getEpid(),paperFile.getSourcepath());
135
+//             }
136
+//         } catch (Exception e) {
137
+//             log.error("考试附件试卷图片转pdf失败:" + e.getMessage());
138
+//         }
139
+//     }
140
+//
141
+//     @RabbitHandler
142
+//     @RabbitListener(queues = "${rabbitmq.examHtmlToPdfHandleQueue}")
143
+//     public void examHtmlToPdfHandleQueue(Message message) {
144
+//         try {
145
+//             String msg = new String(message.getBody(), "UTF-8");
146
+//             System.out.println(LocalDateTime.now() + "考试报告html转pdf接收消息内容:" + msg);
147
+//             EReportVo report = JSON.parseObject(msg, EReportVo.class);
148
+//             if (report.getConverted() != null && report.getConverted() != 1) return;
149
+//             eReportGenerateMapper.updatePdf(report);//修改考试pdf地址
150
+//             Integer examid = report.getExamid();
151
+//             Integer type = report.getType();
152
+//             if (3 == type) {
153
+//                 Integer classid = report.getClassid();
154
+//                 Integer num = eReportGenerateMapper.getNoPdfNumByExamAndClassId(examid, classid);//考试班级未生成pdf的数量
155
+//                 if (0 == num) {
156
+//                     Map map = new HashMap();
157
+//                     String savefolder = eReportGenerateMapper.getSaveFolder(examid);
158
+//                     map.put("savefolder", savefolder + "/" +  + classid);
159
+//                     map.put("examid", examid);
160
+//                     map.put("classid", classid);
161
+//                     map.put("type", 4);
162
+//                     convertUtil.htmlToPdf(map);
163
+//                 }
164
+//             } else if (4 == type) {
165
+//                 Integer num = eReportGenerateMapper.getNoPdfNumByExamId(examid);//考试未生成pdf的数量
166
+//                 if (0 == num) {
167
+//                     eBaseMapper.updateExamReportState(examid, 2);
168
+//                 }
169
+//             }
170
+//         } catch (Exception e) {
171
+//             log.error("考试报告html转pdf失败:" + e.getMessage());
172
+//         }
173
+//     }
174
+// }

+ 137
- 0
smarking/src/main/java/com/xhkjedu/smarking/listener/MessageSender.java View File

@@ -0,0 +1,137 @@
1
+package com.xhkjedu.smarking.listener;
2
+
3
+import com.alibaba.fastjson.JSON;
4
+import lombok.extern.slf4j.Slf4j;
5
+import org.springframework.amqp.core.AmqpTemplate;
6
+import org.springframework.beans.factory.annotation.Autowired;
7
+import org.springframework.beans.factory.annotation.Value;
8
+import org.springframework.stereotype.Component;
9
+
10
+import java.util.HashMap;
11
+import java.util.List;
12
+import java.util.Map;
13
+
14
+/**
15
+ * @author ywx
16
+ * @className MessageSender
17
+ * @description
18
+ * @date 2021/10/25 14:28
19
+ **/
20
+@Component
21
+@Slf4j
22
+public class MessageSender {
23
+    @Autowired
24
+    private AmqpTemplate rabbitTemplate;
25
+    //负载一生产者mq
26
+    @Value("${rabbitmq.markingImgMergeQueue}")
27
+    private String markingImgMergeQueue;
28
+    @Value("${rabbitmq.markingBaseToPdfQueue}")
29
+    private String markingBaseToPdfQueue;
30
+    @Value("${rabbitmq.markingBaseToScanImgQueue}")
31
+    private String markingBaseToScanImgQueue;
32
+    @Value("${rabbitmq.markingImgToPdfQueue}")
33
+    private String markingImgToPdfQueue;
34
+    @Value("${rabbitmq.markingHtmlToPdfQueue}")
35
+    private String markingHtmlToPdfQueue;
36
+    //负载二生产者mq
37
+    @Value("${rabbitmq.markingImgMergeQueue2}")
38
+    private String markingImgMergeQueue2;
39
+    @Value("${rabbitmq.markingBaseToPdfQueue2}")
40
+    private String markingBaseToPdfQueue2;
41
+    @Value("${rabbitmq.markingBaseToScanImgQueue2}")
42
+    private String markingBaseToScanImgQueue2;
43
+    @Value("${rabbitmq.markingImgToPdfQueue2}")
44
+    private String markingImgToPdfQueue2;
45
+    //负载二节点名称
46
+    private final String node2="node02/";
47
+
48
+    /**
49
+     * @Description 学生答案合并
50
+     * @Param [epsqid, userAnswers, device]
51
+     * @Return void
52
+     * @Author wn
53
+     * @Date 2022/7/28 16:46
54
+     **/
55
+    public void imgMerge(Integer epsqid, List<String> userAnswers, Integer device) {
56
+        Map map = new HashMap();
57
+        map.put("epsqid", epsqid);
58
+        map.put("stuanswers", userAnswers);
59
+        map.put("device", device);
60
+        if (userAnswers.toString().contains(node2)) {
61
+            rabbitTemplate.convertAndSend(markingImgMergeQueue2, JSON.toJSONString(map));
62
+        } else {
63
+            rabbitTemplate.convertAndSend(markingImgMergeQueue, JSON.toJSONString(map));
64
+        }
65
+    }
66
+
67
+    /**
68
+     * @Description 答题卡图片合并为pdf
69
+     * @Param [epid, imgs]
70
+     * @Return void
71
+     * @Author wn
72
+     * @Date 2022/8/2 13:57
73
+     **/
74
+    public void base64ToPdf(Integer markingid,Integer epid, List<String> imgs,String savefolder,Integer scantroncol,String filename) {
75
+        Map map = new HashMap();
76
+        map.put("markingid", markingid);
77
+        map.put("epid",epid);
78
+        map.put("imgs", imgs);
79
+        map.put("scantroncol", scantroncol);
80
+        map.put("savefolder",savefolder);
81
+        map.put("filename",filename);
82
+        if (imgs.toString().contains(node2)) {
83
+            rabbitTemplate.convertAndSend(markingBaseToPdfQueue2, JSON.toJSONString(map));
84
+        } else {
85
+            rabbitTemplate.convertAndSend(markingBaseToPdfQueue, JSON.toJSONString(map));
86
+        }
87
+    }
88
+
89
+    /**
90
+     * @Description  扫码出错图片base64转图片
91
+     * @Param [serrorid, imgs, savefolder]
92
+     * @Return void
93
+     * @Author wn
94
+     * @Date 2022/9/19 9:40
95
+     **/
96
+    public void base64ToScanImg(Integer serrorid,Integer markingid,List<String> imgs,String savefolder) {
97
+        Map map = new HashMap();
98
+        map.put("serrorid", serrorid);
99
+        map.put("markingid", markingid);
100
+        map.put("imgs", imgs);
101
+        map.put("savefolder",savefolder);
102
+        if (imgs.toString().contains(node2)) {
103
+            rabbitTemplate.convertAndSend(markingBaseToScanImgQueue2, JSON.toJSONString(map));
104
+        } else {
105
+            rabbitTemplate.convertAndSend(markingBaseToScanImgQueue, JSON.toJSONString(map));
106
+        }
107
+    }
108
+
109
+    /**
110
+     * @Description 考试附件试卷图片转pdf
111
+     * @Param [imgs, epid]
112
+     * @Return void
113
+     * @Author wn
114
+     * @Date 2022/11/1 10:43
115
+     **/
116
+    public void ImgToPdf(List<String> imgs,Integer epid) {
117
+        Map map = new HashMap();
118
+        map.put("epid",epid);
119
+        map.put("imgs", imgs);
120
+        if (imgs.toString().contains(node2)) {
121
+            rabbitTemplate.convertAndSend(markingImgToPdfQueue2, JSON.toJSONString(map));
122
+        } else {
123
+            rabbitTemplate.convertAndSend(markingImgToPdfQueue, JSON.toJSONString(map));
124
+        }
125
+    }
126
+
127
+    /**
128
+     * @Description 考试报告html转pdf
129
+     * @Param [map]
130
+     * @Author ywx
131
+     * @Date 2023/2/14 11:24
132
+     * @return void
133
+     **/
134
+    public void htmlToPdf(Map map) {
135
+        rabbitTemplate.convertAndSend(markingHtmlToPdfQueue, JSON.toJSONString(map));
136
+    }
137
+}

+ 4
- 0
smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperAnalyzeMapper.java View File

@@ -2,6 +2,7 @@ package com.xhkjedu.smarking.mapper.paper;
2 2
 
3 3
 import com.xhkjedu.base.TkMapper;
4 4
 import com.xhkjedu.smarking.model.paper.MsPaperAnalyze;
5
+import org.apache.ibatis.annotations.Param;
5 6
 
6 7
 /**
7 8
  * @Description 阅卷试卷分析表 Mapper 接口
@@ -9,4 +10,7 @@ import com.xhkjedu.smarking.model.paper.MsPaperAnalyze;
9 10
  * @Date 2024-10-15
10 11
  */
11 12
 public interface MsPaperAnalyzeMapper extends TkMapper<MsPaperAnalyze> {
13
+
14
+    //根据试卷ID删除试卷分析
15
+    int deleteByMpId(@Param("mpId")Integer mpid);
12 16
 }

+ 4
- 0
smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperFileMapper.java View File

@@ -2,6 +2,7 @@ package com.xhkjedu.smarking.mapper.paper;
2 2
 
3 3
 import com.xhkjedu.base.TkMapper;
4 4
 import com.xhkjedu.smarking.model.paper.MsPaperFile;
5
+import org.apache.ibatis.annotations.Param;
5 6
 
6 7
 /**
7 8
  * @Description 阅卷试卷附件表 Mapper 接口
@@ -9,4 +10,7 @@ import com.xhkjedu.smarking.model.paper.MsPaperFile;
9 10
  * @Date 2024-10-15
10 11
  */
11 12
 public interface MsPaperFileMapper extends TkMapper<MsPaperFile> {
13
+
14
+    //删除试卷指定类型附件
15
+    int deleteByMpidAndType(@Param("mpid")Integer mpid,@Param("filetype") Integer filetype);
12 16
 }

+ 9
- 0
smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperMapper.java View File

@@ -2,6 +2,7 @@ package com.xhkjedu.smarking.mapper.paper;
2 2
 
3 3
 import com.xhkjedu.base.TkMapper;
4 4
 import com.xhkjedu.smarking.model.paper.MsPaper;
5
+import org.apache.ibatis.annotations.Param;
5 6
 
6 7
 /**
7 8
  * @Description 阅卷试卷表 Mapper 接口
@@ -9,4 +10,12 @@ import com.xhkjedu.smarking.model.paper.MsPaper;
9 10
  * @Date 2024-10-15
10 11
  */
11 12
 public interface MsPaperMapper extends TkMapper<MsPaper> {
13
+
14
+    //设置试卷中试题数量
15
+    int updatePaperInfo(@Param("paper") MsPaper paper);
16
+
17
+    //更新试卷答案设置状态
18
+    int updatePaperAnswer(@Param("answered") Integer answered,@Param("mpid") Integer mpid);
19
+
20
+
12 21
 }

+ 14
- 0
smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperQtypeMapper.java View File

@@ -2,6 +2,10 @@ package com.xhkjedu.smarking.mapper.paper;
2 2
 
3 3
 import com.xhkjedu.base.TkMapper;
4 4
 import com.xhkjedu.smarking.model.paper.MsPaperQtype;
5
+import com.xhkjedu.smarking.vo.paper.MsPaperQtypeVo;
6
+import org.apache.ibatis.annotations.Param;
7
+
8
+import java.util.List;
5 9
 
6 10
 /**
7 11
  * @Description 阅卷试卷题型表 Mapper 接口
@@ -9,4 +13,14 @@ import com.xhkjedu.smarking.model.paper.MsPaperQtype;
9 13
  * @Date 2024-10-15
10 14
  */
11 15
 public interface MsPaperQtypeMapper extends TkMapper<MsPaperQtype> {
16
+
17
+    //根据试卷ID删除试卷结构
18
+    int deleteByMpId(@Param("mpId")Integer mpid);
19
+
20
+
21
+    //题库试题详情
22
+    List<MsPaperQtypeVo> listPaperQtypeQuestions(@Param("mpid") Integer mpid);
23
+
24
+    //附件试题详情
25
+    List<MsPaperQtypeVo> listPaperQtypeQuesitonsForFj(@Param("mpid") Integer mpid);
12 26
 }

+ 14
- 0
smarking/src/main/java/com/xhkjedu/smarking/mapper/paper/MsPaperQtypeQuestionMapper.java View File

@@ -2,6 +2,9 @@ package com.xhkjedu.smarking.mapper.paper;
2 2
 
3 3
 import com.xhkjedu.base.TkMapper;
4 4
 import com.xhkjedu.smarking.model.paper.MsPaperQtypeQuestion;
5
+import org.apache.ibatis.annotations.Param;
6
+
7
+import java.util.List;
5 8
 
6 9
 /**
7 10
  * @Description 阅卷试卷题型试题表 Mapper 接口
@@ -9,4 +12,15 @@ import com.xhkjedu.smarking.model.paper.MsPaperQtypeQuestion;
9 12
  * @Date 2024-10-15
10 13
  */
11 14
 public interface MsPaperQtypeQuestionMapper extends TkMapper<MsPaperQtypeQuestion> {
15
+
16
+    //保存客观题选项数量
17
+    int updateBatchQuestionOptionnum(@Param("list") List<MsPaperQtypeQuestion> list);
18
+
19
+    //保存客观题答案
20
+    int updateBatchQuestionAnswer(@Param("list") List<MsPaperQtypeQuestion> list);
21
+
22
+    //试卷中所有试题基础信息
23
+    List<MsPaperQtypeQuestion> listQuestionsByEpid(@Param("mpid")Integer mpid);
24
+
25
+
12 26
 }

+ 9
- 0
smarking/src/main/java/com/xhkjedu/smarking/model/paper/MsPaper.java View File

@@ -5,6 +5,8 @@ import lombok.Data;
5 5
 
6 6
 import javax.persistence.Id;
7 7
 import javax.persistence.Table;
8
+import javax.persistence.Transient;
9
+import java.util.List;
8 10
 
9 11
 /**
10 12
  * @Description 阅卷试卷表
@@ -45,4 +47,11 @@ public class MsPaper extends BaseBean {
45 47
     private Integer handleid;
46 48
     //修改时间
47 49
     private Integer handletime;
50
+
51
+    //题型
52
+    @Transient
53
+    private List<MsPaperQtype> qtypes;
54
+    //文件
55
+    @Transient
56
+    private List<MsPaperFile> pfiles;
48 57
 }

+ 1
- 1
smarking/src/main/java/com/xhkjedu/smarking/model/paper/MsPaperFile.java View File

@@ -24,7 +24,7 @@ public class MsPaperFile extends BaseBean {
24 24
     //原文档保存地址
25 25
     private String sourcepath;
26 26
     //文件类型1题干2答案
27
-    private Integer fileclass;
27
+    private Integer filetype;
28 28
     //文件顺序
29 29
     private Integer fileorder;
30 30
     //创建人ID

+ 6
- 0
smarking/src/main/java/com/xhkjedu/smarking/model/paper/MsPaperQtype.java View File

@@ -5,6 +5,8 @@ import lombok.Data;
5 5
 
6 6
 import javax.persistence.Id;
7 7
 import javax.persistence.Table;
8
+import javax.persistence.Transient;
9
+import java.util.List;
8 10
 
9 11
 /**
10 12
  * @Description 阅卷试卷题型表
@@ -31,4 +33,8 @@ public class MsPaperQtype extends BaseBean {
31 33
     private Integer createid;
32 34
     //创建时间
33 35
     private Integer createtime;
36
+
37
+    //题型下试题
38
+    @Transient
39
+    private List<MsPaperQtypeQuestion> questions;
34 40
 }

+ 731
- 13
smarking/src/main/java/com/xhkjedu/smarking/service/paper/MsPaperQtypeService.java View File

@@ -1,11 +1,19 @@
1 1
 package com.xhkjedu.smarking.service.paper;
2 2
 
3
-import com.xhkjedu.smarking.mapper.paper.MsPaperQtypeMapper;
4
-import com.xhkjedu.smarking.model.paper.MsPaperQtype;
3
+import com.alibaba.fastjson.JSON;
4
+import com.xhkjedu.smarking.mapper.paper.*;
5
+import com.xhkjedu.smarking.model.paper.*;
6
+import com.xhkjedu.smarking.utils.ConvertUtil;
7
+import com.xhkjedu.smarking.vo.paper.*;
8
+import com.xhkjedu.utils.N_Utils;
9
+import com.xhkjedu.vo.ResultVo;
10
+import org.springframework.beans.factory.annotation.Autowired;
5 11
 import org.springframework.stereotype.Service;
12
+import org.springframework.transaction.annotation.Transactional;
6 13
 
7 14
 import javax.annotation.Resource;
8
-import java.util.List;
15
+import java.util.*;
16
+import java.util.stream.Collectors;
9 17
 
10 18
 /**
11 19
  * @Description 阅卷试卷题型表 服务实现类
@@ -16,6 +24,16 @@ import java.util.List;
16 24
 public class MsPaperQtypeService {
17 25
     @Resource
18 26
     private MsPaperQtypeMapper msPaperQtypeMapper;
27
+    @Resource
28
+    private MsPaperAnalyzeMapper msPaperAnalyzeMapper;
29
+    @Resource
30
+    private MsPaperMapper msPaperMapper;
31
+    @Resource
32
+    private MsPaperQtypeQuestionMapper msPaperQtypeQuestionMapper;
33
+    @Resource
34
+    private MsPaperFileMapper msPaperFileMapper;
35
+    @Autowired
36
+    private ConvertUtil convertUtil;
19 37
 
20 38
     /**
21 39
      * @Description 新增
@@ -24,18 +42,718 @@ public class MsPaperQtypeService {
24 42
      * @Date 2024-10-15
25 43
      * @return void
26 44
      **/
27
-    public void add(MsPaperQtype msPaperQtype){
28
-        msPaperQtypeMapper.insert(msPaperQtype);
45
+    @Transactional(rollbackFor = Exception.class)
46
+    public ResultVo savePaperQtype(MsPaper paper){
47
+
48
+         //先删除原有题型、分析
49
+        msPaperQtypeMapper.deleteByMpId(paper.getMpid());
50
+        msPaperAnalyzeMapper.deleteByMpId(paper.getMpid());
51
+        //根据考试类型(线上和线下),是否删除文件
52
+
53
+        List<MsPaperQtype> qtypeList = paper.getQtypes();
54
+        List<MsPaperQtypeQuestion> questionList = new ArrayList<>();
55
+        int timestamp = N_Utils.getSecondTimestamp();
56
+        paper.setCreatetime(timestamp);
57
+        setPaperBaseDetail(paper,qtypeList);
58
+        msPaperMapper.updatePaperInfo(paper);
59
+        //保存题型试题信息
60
+        savePaperQtype(paper,qtypeList,questionList);
61
+
62
+        List<String> imgs = new ArrayList<>();
63
+        //保存试卷分析
64
+        if(paper.getPtype() == 1){
65
+            savePaperAnalyzeForQuestion(paper);
66
+        }else{
67
+            List<MsPaperFile> pfiles = paper.getPfiles();//附件
68
+            if(paper.getHasfile() == 1){
69
+                setPaperFiles(paper,pfiles,imgs);
70
+                msPaperFileMapper.insertList(pfiles);
71
+            }
72
+            savePaperAnalyzeForFj(paper,qtypeList,questionList);
73
+        }
74
+
75
+        //更加试卷状态是否需要生成分析和更改科目状态
76
+        // if(espVo.getExammode() == 1 && paper.getPstate()!=null && paper.getPstate() == 1){
77
+        //     //如果提交试卷,更改科目状态
78
+        //     eSubjectMapper.updateExamSubjectState(1,espVo.getEsid());
79
+        // }
80
+        //保存后清除试题栏中试题
81
+        // userBasketMapper.deleteByUseridAndSubjectid(paper.getCreateid(),espVo.getSubjectid());
82
+
83
+        if(N_Utils.isListNotEmpty(imgs) && imgs.size()>1){
84
+            //说明上传的附件文件为图片,并且图片为多张,调用mq合并
85
+            convertUtil.imgToPdf(imgs,paper.getMpid());
86
+        }
87
+
88
+        return new ResultVo(0,"保存成功",paper.getMpid());
29 89
     }
30 90
 
31
-    /**
32
-     * @Description 列表
33
-     * @Param [msPaperQtype]
34
-     * @Author auto
35
-     * @Date 2024-10-15
36
-     * @return java.util.List<com.xhkjedu.smarking.model.paper.MsPaperQtype>
91
+    //设置试卷基本信息
92
+    private void setPaperBaseDetail(MsPaper paper,List<MsPaperQtype> qtypelist){
93
+        if(paper.getPtype() == 2 && paper.getPfiles()!=null && paper.getPfiles().size()>0){
94
+            paper.setHasfile(1);
95
+        }else {
96
+            paper.setHasfile(0);
97
+        }
98
+        double pscore = qtypelist.stream().collect(Collectors.summingDouble(MsPaperQtype:: getMptscore));
99
+        int pnum = qtypelist.stream().collect(Collectors.summingInt(MsPaperQtype:: getMptnum));
100
+        paper.setPnum(pnum);
101
+        paper.setPscore(pscore);
102
+        paper.setHandletime(paper.getCreatetime());
103
+        paper.setHandleid(paper.getCreateid());
104
+        if(paper.getPtype() == 1){
105
+            paper.setAnswered(1);//题库已设置正确答案
106
+        }else{
107
+            paper.setAnswered(0);
108
+        }
109
+
110
+        if(paper.getHearnum() == null){
111
+            paper.setHearnum(0);
112
+        }
113
+    }
114
+
115
+    //保存试卷题型试题信息
116
+    private void savePaperQtype(MsPaper paper,List<MsPaperQtype> qtypelist,List<MsPaperQtypeQuestion> qtqs){
117
+        for(MsPaperQtype qtype : qtypelist){
118
+            qtype.setMpid(paper.getMpid());
119
+            qtype.setCreateid(paper.getHandleid());
120
+            qtype.setCreatetime(paper.getHandletime());
121
+            qtype.setMptname(N_Utils.strTrim(qtype.getMptname()));
122
+        }
123
+        msPaperQtypeMapper.insertList(qtypelist);//保存题型
124
+        for(MsPaperQtype qtype : qtypelist){
125
+            List<MsPaperQtypeQuestion> questions = qtype.getQuestions();
126
+            qtype.setMptid(qtype.getId());
127
+
128
+            for (int i = 0; i < questions.size(); i++) {
129
+                MsPaperQtypeQuestion q=questions.get(i);
130
+                q.setMpid(paper.getMpid());
131
+                q.setMptid(qtype.getId());
132
+                if(q.getOptionnum()==null){
133
+                    q.setOptionnum(0);
134
+                }
135
+                if(q.getLoseoption() == null){
136
+                    q.setLoseoption(0);
137
+                }
138
+                if(q.getScoreset() == null){
139
+                    q.setScoreset(0);
140
+                }
141
+
142
+                qtqs.add(q);
143
+            }
144
+        }
145
+        msPaperQtypeQuestionMapper.insertList(qtqs);
146
+    }
147
+
148
+    //保存试卷分析--题库
149
+    public void savePaperAnalyzeForQuestion(MsPaper paper){
150
+        //保存之前先清除之前的试卷分析
151
+        msPaperAnalyzeMapper.deleteByMpId(paper.getMpid());
152
+        //总体分析(主观题、客观题)
153
+        List<MsPaperQtypeVo> qtypelist = msPaperQtypeMapper.listPaperQtypeQuestions(paper.getMpid());//试卷题型试题信息
154
+        List<MsPaperQtypeQuestion> uplist = new ArrayList<>();//改试卷中客观题选项数量
155
+        List<QuestionOrderVo> pointqlist = new ArrayList<>();//用于存放知识点,以及知识点下得分
156
+        List<MsPaperQtypeQuestionVo> mquestions = new ArrayList<>();//存放单题和母题
157
+        List<QuestionOrderVo> complexitylist = new ArrayList<>();//试题难易度存放试题
158
+        List<QuestionPointVo> quesplist = new ArrayList<>();//单题、母题对应知识点
159
+
160
+        //处理出试卷中所有试题、知识点
161
+        List<Map> typelist = new ArrayList<>();//题型分析
162
+        for(MsPaperQtypeVo qt : qtypelist){
163
+            List<Integer> qorderlist = new ArrayList<>();
164
+            List<QuestionOrderVo> qtypeqlist = new ArrayList<>();
165
+            List<MsPaperQtypeQuestionVo> tqlist = qt.getQuestions();
166
+            int hasmt = 0;
167
+            for(MsPaperQtypeQuestionVo q : tqlist){
168
+                int ctype = q.getCtype();
169
+                //单题的pid设置为默认值,方便后续进行单题和母题处理
170
+                if(q.getQlevel()==1){
171
+                    q.setQuestionpid("000");
172
+                    //判断试题是否是客观题
173
+                    if(N_Utils.isObjectiveQuestion(ctype)){
174
+                        //判断试题是否是客观题,暂时用mctype进行标识
175
+                        q.setMctype(111);
176
+                    }else{
177
+                        q.setMctype(112);
178
+                    }
179
+                    //设置知识点
180
+                    mquestions.add(q);
181
+                    //获取试题知识点
182
+                    List<Map> qpoints = q.getPoints();
183
+                    //处理试题知识点占分
184
+                    setPaperQuestionPointScore(pointqlist,quesplist,qpoints,q,null,null);
185
+                    setPaperQuestionQtypeScore(qtypeqlist,q,null,null);//处理试题题型
186
+                    setPaperQuestionComplexityScore(complexitylist,q,null,null);//处理试题难易度
187
+                }
188
+                if(q.getQlevel() == 3){
189
+                    hasmt ++;
190
+                }
191
+                if(!qorderlist.contains(q.getQorder())){
192
+                    qorderlist.add(q.getQorder());
193
+                }
194
+                //为了处理客观题中选项数量
195
+                if(ctype == 1 || ctype ==2 || ctype==4 || ctype ==5 || ctype==6){
196
+                    List<String> strlst = JSON.parseArray(q.getQoption(), String.class);
197
+                    MsPaperQtypeQuestion eqq = new MsPaperQtypeQuestion();
198
+                    eqq.setMptqid(q.getMptqid());
199
+                    eqq.setOptionnum(strlst.size());
200
+                    uplist.add(eqq);
201
+                }
202
+            }
203
+
204
+            if(hasmt > 0){
205
+                //说明该题型下有复合题,复合体进行知识点、题型下试题题号及id进行操作
206
+                LinkedHashMap<String,List<MsPaperQtypeQuestionVo>> quesMap = tqlist.stream().collect(Collectors.groupingBy(MsPaperQtypeQuestionVo:: getQuestionpid, LinkedHashMap::new, Collectors.toList()));
207
+                for(Map.Entry<String, List<MsPaperQtypeQuestionVo>> entry : quesMap.entrySet()){
208
+                    if(!entry.getKey().equals("000")){
209
+                        //说明该题为母题,
210
+                        MsPaperQtypeQuestionVo mq = new MsPaperQtypeQuestionVo();
211
+                        List<MsPaperQtypeQuestionVo> sonques = entry.getValue();
212
+                        int k = 0;//如果k大于1说明有小题是主观题,则母题为主观题
213
+                        double score = 0;
214
+                        for(MsPaperQtypeQuestionVo sq : sonques){
215
+                            if(!N_Utils.isObjectiveQuestion(sq.getCtype())){
216
+                                k++;
217
+                            }
218
+                            score = N_Utils.getDoubleSum(score,sq.getScore());
219
+                        }
220
+
221
+                        MsPaperQtypeQuestionVo qobj = sonques.get(0);
222
+                        mq.setQuestionid(qobj.getQuestionpid());
223
+                        mq.setComplexity(qobj.getMcomplexity());
224
+                        mq.setQorder(qobj.getQorder());
225
+                        mq.setScore(score);
226
+                        mq.setQtypename(qobj.getMqtypename());
227
+                        if(k == 0){
228
+                            mq.setMctype(111);
229
+                        }else{
230
+                            mq.setMctype(112);
231
+                        }
232
+                        mq.setQlevel(qobj.getMqlevel());
233
+                        mq.setSonques(sonques);
234
+                        //获取所有子题的试题题号
235
+                        List<String> qns = sonques.stream().map(MsPaperQtypeQuestionVo::getQn).collect(Collectors.toList());
236
+                        String[] qnstr = qns.stream().toArray(String[]::new);
237
+                        //所有子题的试题eptqid
238
+                        List<Integer> eptqids = sonques.stream().map(MsPaperQtypeQuestionVo::getMptqid).collect(Collectors.toList());
239
+                        Integer[] eptqidstr = eptqids.stream().toArray(Integer[]::new);
240
+
241
+                        //获取知识点
242
+                        List<Map> qpoints = sonques.get(0).getPoints();
243
+                        mq.setPoints(qpoints);
244
+                        mquestions.add(mq);
245
+                        setPaperQuestionPointScore(pointqlist,quesplist,qpoints,mq,qnstr,eptqids);
246
+                        setPaperQuestionQtypeScore(qtypeqlist,mq,qnstr,eptqidstr);//处理试题题型
247
+                        setPaperQuestionComplexityScore(complexitylist,mq,qnstr,eptqidstr);//处理试题难易度
248
+                    }
249
+                }
250
+            }
251
+
252
+            //试题题型分析数据
253
+            double storerate = N_Utils.getDoubleDivideAndMulitiply(qt.getMptscore(), paper.getPscore());
254
+            Map tmap = new TreeMap();
255
+            tmap.put("qtid",qt.getMptid());
256
+            tmap.put("qtname",qt.getMptname());
257
+            tmap.put("num",qt.getMptnum());
258
+            tmap.put("score",qt.getMptscore());
259
+            tmap.put("srate",storerate);
260
+            qtypeqlist = qtypeqlist.stream().sorted(Comparator.comparing(QuestionOrderVo::getOrder)).collect(Collectors.toList());
261
+            tmap.put("ques",qtypeqlist);
262
+            typelist.add(tmap);
263
+        }
264
+
265
+        //进行客观题选项处理
266
+        if(N_Utils.isListNotEmpty(uplist)){
267
+            msPaperQtypeQuestionMapper.updateBatchQuestionOptionnum(uplist);
268
+        }
269
+
270
+        //试题总体分布进行分析
271
+        //客观题集合
272
+        List<MsPaperQtypeQuestionVo> ktlst = mquestions.stream().filter(q->q.getMctype() == 111).collect(Collectors.toList());
273
+        int ktnum = ktlst.size();
274
+        double ktscore = ktlst.stream().collect(Collectors.summingDouble(MsPaperQtypeQuestionVo:: getScore));
275
+        double ktnrate = N_Utils.getIntegerDivideAndMulitiply(ktnum, paper.getPnum());
276
+        double ktsrate = N_Utils.getDoubleDivideAndMulitiply(ktscore, paper.getPscore());
277
+        List<MsPaperQsVo> ranglist = new ArrayList<>();
278
+        ranglist.add(new MsPaperQsVo("客观题",ktscore,ktnum,ktsrate,ktnrate));
279
+        MsPaperQsVo zgq = new MsPaperQsVo();
280
+        zgq.setQtname("主观题");
281
+        zgq.setNum(paper.getPnum() - ktnum);
282
+        zgq.setNrate(N_Utils.getDoubleReduce(100.0,ktnrate));
283
+        zgq.setScore(N_Utils.getDoubleReduce(paper.getPscore(),ktscore));
284
+        zgq.setSrate(N_Utils.getDoubleReduce(100.0,ktsrate));
285
+        ranglist.add(zgq);
286
+        Map ztmap = new TreeMap<>();
287
+        ztmap.put("pnum", paper.getPnum());
288
+        ztmap.put("pscore",paper.getPscore());
289
+        ztmap.put("ranglist",ranglist);
290
+
291
+        //难易度
292
+        List<Map> clist = new ArrayList<>();
293
+        if(N_Utils.isListNotEmpty(complexitylist)){
294
+            Map<String,List<QuestionOrderVo>> complxmap =  complexitylist.stream().collect(Collectors.groupingBy(QuestionOrderVo:: getId, Collectors.toList()));
295
+            for(Map.Entry<String, List<QuestionOrderVo>> entry : complxmap.entrySet()){
296
+                List<QuestionOrderVo> pgblist = entry.getValue();
297
+                Double score = pgblist.stream().mapToDouble(s -> s.getScore()).sum();
298
+                score = N_Utils.formatDouble(score,1);
299
+                Double srate = N_Utils.getDoubleDivideAndMulitiply(score,paper.getPscore());
300
+                Map prate = new TreeMap();
301
+                prate.put("level",Integer.parseInt(entry.getKey()));
302
+                prate.put("score",score);
303
+                prate.put("srate",srate);
304
+                prate.put("ques",pgblist.stream().sorted(Comparator.comparing(QuestionOrderVo::getOrder)).collect(Collectors.toList()));
305
+                clist.add(prate);
306
+            }
307
+        }
308
+
309
+        //知识点分析
310
+        List<Map> pointlist = new ArrayList<>();
311
+        if(N_Utils.isListNotEmpty(pointqlist)){
312
+            LinkedHashMap<String,List<QuestionOrderVo>> pointmap = pointqlist.stream().collect(Collectors.groupingBy(QuestionOrderVo::getId,LinkedHashMap::new,Collectors.toList()));
313
+            //根据指示进行分组,
314
+            for(Map.Entry<String, List<QuestionOrderVo>> entry : pointmap.entrySet()){
315
+                List<QuestionOrderVo> pgblist = entry.getValue();
316
+                //获取知识点的总分
317
+                Double score = pgblist.stream().mapToDouble(s -> s.getScore()).sum();
318
+                score = N_Utils.formatDouble(score,1);
319
+                Double srate = N_Utils.getDoubleDivideAndMulitiply(score,paper.getPscore());
320
+                Map prate = new LinkedHashMap();
321
+                prate.put("pointid",entry.getKey());
322
+                prate.put("pointname",pgblist.get(0).getName());
323
+                prate.put("score",score);
324
+                prate.put("srate",srate);
325
+                prate.put("ques",pgblist.stream().sorted(Comparator.comparing(QuestionOrderVo::getOrder)).collect(Collectors.toList()));
326
+                pointlist.add(prate);
327
+            }
328
+        }
329
+
330
+        MsPaperAnalyze epa = new MsPaperAnalyze();
331
+        epa.setMpid(paper.getMpid());
332
+        epa.setAlljson(JSON.toJSONString(ztmap));//总体分析
333
+        epa.setQnumjson(JSON.toJSONString(typelist));//试题栏题型分析
334
+        epa.setComplexityjson(JSON.toJSONString(clist));//难易度分析
335
+        epa.setPointjson(JSON.toJSONString(pointlist));//知识点分析
336
+        epa.setQuespointjson(JSON.toJSONString(quesplist));
337
+        msPaperAnalyzeMapper.insertUseGeneratedKeys(epa);
338
+    }
339
+
340
+    //设置知识点
341
+    private void setPaperQuestionPointScore(List<QuestionOrderVo> pointqlist,List<QuestionPointVo> quesplist,List<Map> qpoints,MsPaperQtypeQuestionVo dq,String[] qnstr,List<Integer> mptqids){
342
+        if(N_Utils.isListNotEmpty(qpoints)){
343
+            if(qpoints.size() == 1){
344
+                //试题只有一个知识点
345
+                Map pointmap = qpoints.get(0);
346
+                QuestionOrderVo qpoint = new QuestionOrderVo();
347
+                qpoint.setId(pointmap.get("pointid").toString());
348
+                qpoint.setName(pointmap.get("pointname").toString());
349
+                qpoint.setOrder(dq.getQorder());
350
+                qpoint.setScore(dq.getScore());
351
+                qpoint.setQlevel(dq.getQlevel());
352
+                qpoint.setQuestionid(dq.getQuestionid());
353
+                if(qnstr == null){
354
+                    qpoint.setQns(new String[]{dq.getQn()});
355
+                }else{
356
+                    qpoint.setQns(qnstr);
357
+                }
358
+
359
+                if(N_Utils.isListEmpty(mptqids)){
360
+                    qpoint.setMptqids(new Integer[]{dq.getMptqid()});
361
+                }else{
362
+                    Integer[] mptqidstr = mptqids.stream().toArray(Integer[]::new);
363
+                    qpoint.setMptqids(mptqidstr);
364
+                }
365
+                pointqlist.add(qpoint);
366
+
367
+                //试题对应知识点
368
+                QuestionPointVo questionPointVo = new QuestionPointVo();
369
+                questionPointVo.setOrder(dq.getQorder());
370
+                questionPointVo.setScore(dq.getScore());
371
+                questionPointVo.setQlevel(dq.getQlevel());
372
+                questionPointVo.setQuestionid(dq.getQuestionid());
373
+                List<Map> pointids = new ArrayList<>();
374
+                Map map = new HashMap();
375
+                map.put("pointid",pointmap.get("pointid").toString());
376
+                map.put("score",dq.getScore());
377
+                pointids.add(map);
378
+                questionPointVo.setPointids(pointids);
379
+
380
+                if(N_Utils.isListEmpty(mptqids)){
381
+                    List<Integer> saveids = new ArrayList<>();
382
+                    saveids.add(dq.getMptqid());
383
+                    questionPointVo.setMptqids(saveids);
384
+                    questionPointVo.setMptqid(dq.getMptqid());
385
+                }else{
386
+                    questionPointVo.setMptqids(mptqids);
387
+                    questionPointVo.setMptqid(0);
388
+                }
389
+                quesplist.add(questionPointVo);
390
+            }else{
391
+
392
+                //试题有多个知识点
393
+                //计算每个知识点所占分值(平均分配)
394
+                Double[] avgps = N_Utils.getPointAvgScore(qpoints.size(),dq.getScore());
395
+                List<Map> pointids = new ArrayList<>();
396
+                for(int m = 0; m<qpoints.size();m++){
397
+                    Map pointmap = qpoints.get(m);
398
+                    QuestionOrderVo qpoint = new QuestionOrderVo();
399
+                    qpoint.setId(pointmap.get("pointid").toString());
400
+                    qpoint.setName(pointmap.get("pointname").toString());
401
+                    qpoint.setOrder(dq.getQorder());
402
+                    qpoint.setQlevel(dq.getQlevel());
403
+                    qpoint.setQuestionid(dq.getQuestionid());
404
+
405
+
406
+                    Map quesmap = new HashMap();//试题对应知识点
407
+                    quesmap.put("pointid",pointmap.get("pointid").toString());
408
+
409
+                    if(avgps.length == 2 && (m+1) == qpoints.size()){
410
+                        //不能整除分,并且是最后一个知识点
411
+                        qpoint.setScore(avgps[1]);
412
+                        quesmap.put("score",avgps[1]);
413
+                    }else{
414
+                        qpoint.setScore(avgps[0]);
415
+                        quesmap.put("score",avgps[0]);
416
+                    }
417
+                    pointids.add(quesmap);
418
+
419
+                    //题号集合
420
+                    if(qnstr == null){
421
+                        qpoint.setQns(new String[]{dq.getQn()});
422
+                    }else{
423
+                        qpoint.setQns(qnstr);
424
+                    }
425
+                    //试卷中试题id集合
426
+                    if(N_Utils.isListEmpty(mptqids)){
427
+                        qpoint.setMptqids(new Integer[]{dq.getMptqid()});
428
+                    }else{
429
+                        Integer[] eptqidstr = mptqids.stream().toArray(Integer[]::new);
430
+                        qpoint.setMptqids(eptqidstr);
431
+                    }
432
+                    pointqlist.add(qpoint);
433
+                }
434
+
435
+                //试题对应知识点
436
+                QuestionPointVo questionPointVo = new QuestionPointVo();
437
+                questionPointVo.setOrder(dq.getQorder());
438
+                questionPointVo.setScore(dq.getScore());
439
+                questionPointVo.setQlevel(dq.getQlevel());
440
+                questionPointVo.setQuestionid(dq.getQuestionid());
441
+                questionPointVo.setPointids(pointids);
442
+                if(N_Utils.isListEmpty(mptqids)){
443
+                    List<Integer> saveids = new ArrayList<>();
444
+                    saveids.add(dq.getMptqid());
445
+                    questionPointVo.setMptqids(saveids);
446
+                    questionPointVo.setMptqid(dq.getMptqid());
447
+
448
+                }else{
449
+                    questionPointVo.setMptqids(mptqids);
450
+                    questionPointVo.setMptqid(0);
451
+                }
452
+                quesplist.add(questionPointVo);
453
+
454
+            }
455
+        } else{
456
+            QuestionPointVo questionPointVo = new QuestionPointVo();
457
+            questionPointVo.setOrder(dq.getQorder());
458
+            questionPointVo.setScore(dq.getScore());
459
+            questionPointVo.setQlevel(dq.getQlevel());
460
+            questionPointVo.setQuestionid(dq.getQuestionid());
461
+            questionPointVo.setPointids(null);
462
+            if(N_Utils.isListEmpty(mptqids)){
463
+                List<Integer> saveids = new ArrayList<>();
464
+                saveids.add(dq.getMptqid());
465
+                questionPointVo.setMptqids(saveids);
466
+                questionPointVo.setMptqid(dq.getMptqid());
467
+            }else{
468
+                questionPointVo.setMptqids(mptqids);
469
+                questionPointVo.setMptqid(0);
470
+            }
471
+
472
+            quesplist.add(questionPointVo);
473
+        }
474
+    }
475
+
476
+    //设置题型下试题题号及分值
477
+    private void setPaperQuestionQtypeScore(List<QuestionOrderVo> qtypelist,MsPaperQtypeQuestionVo dq,String[] qnstr,Integer[] mptqidstr){
478
+        QuestionOrderVo qtype = new QuestionOrderVo();
479
+        qtype.setId(dq.getQtypeid());
480
+        qtype.setName(dq.getQtypename());
481
+        qtype.setComplexity(dq.getComplexity());
482
+        qtype.setOrder(dq.getQorder());
483
+        qtype.setScore(dq.getScore());
484
+        qtype.setQlevel(dq.getQlevel());
485
+        qtype.setQuestionid(dq.getQuestionid());
486
+        if(qnstr == null){
487
+            qtype.setQns(new String[]{dq.getQn()});
488
+        }else{
489
+            qtype.setQns(qnstr);
490
+        }
491
+        if(mptqidstr == null){
492
+            qtype.setMptqids(new Integer[]{dq.getMptqid()});
493
+        }else{
494
+            qtype.setMptqids(mptqidstr);
495
+        }
496
+
497
+        qtypelist.add(qtype);
498
+    }
499
+
500
+    //设置试题难易度
501
+    private void setPaperQuestionComplexityScore(List<QuestionOrderVo> complexityList,MsPaperQtypeQuestionVo dq,String[] qnstr,Integer[] mptqidstr){
502
+        QuestionOrderVo qtype = new QuestionOrderVo();
503
+        qtype.setId(dq.getComplexity().toString());//存放试题难易度
504
+        qtype.setOrder(dq.getQorder());
505
+        qtype.setScore(dq.getScore());
506
+        qtype.setQlevel(dq.getQlevel());
507
+        qtype.setQuestionid(dq.getQuestionid());
508
+        if(qnstr == null){
509
+            qtype.setQns(new String[]{dq.getQn()});
510
+        }else{
511
+            qtype.setQns(qnstr);
512
+        }
513
+        if(mptqidstr == null){
514
+            qtype.setMptqids(new Integer[]{dq.getMptqid()});
515
+        }else{
516
+            qtype.setMptqids(mptqidstr);
517
+        }
518
+
519
+        complexityList.add(qtype);
520
+    }
521
+
522
+    //设置附件
523
+    private void setPaperFiles(MsPaper paper,List<MsPaperFile> pfiles,List<String> imgs) {
524
+        if(paper.getHasfile() == 1){
525
+            int order= 1;
526
+            for(MsPaperFile pf :pfiles){
527
+                pf.setCreateid(paper.getCreateid());
528
+                pf.setCreatetime(paper.getHandletime());
529
+                pf.setMpid(paper.getMpid());
530
+                pf.setFileorder(order);
531
+                order ++;
532
+
533
+                if(isImgFormatForsourcepath(pf.getSourcepath()) && !imgs.contains(pf.getSourcepath())){
534
+                    imgs.add(pf.getSourcepath());
535
+                }
536
+            }
537
+        }
538
+    }
539
+
540
+    //根据地址判断是否图片
541
+    private boolean isImgFormatForsourcepath(String sourcepath){
542
+        if(sourcepath != null && sourcepath !=""){
543
+            if(sourcepath.contains(".jpg") || sourcepath.contains(".png") || sourcepath.contains(".jpeg")){
544
+                return true;
545
+            }else{
546
+                return false;
547
+            }
548
+        }else{
549
+            return false;
550
+        }
551
+
552
+    }
553
+
554
+    //保存试卷分析--附件
555
+    private void savePaperAnalyzeForFj(MsPaper paper,List<MsPaperQtype> fjtypelist,List<MsPaperQtypeQuestion> queslist){
556
+        //保存之前先清除之前的试卷分析
557
+        msPaperAnalyzeMapper.deleteByMpId(paper.getMpid());
558
+        //总体分析(主观题、客观题)
559
+        //处理题型1单选题2多选题3主观题4判断对错5判断√×6判断TF11综合题12听力13填空题
560
+        int znum = 0;
561
+        double zscore = 0;
562
+        int knum = 0;
563
+        double kscore = 0;
564
+        for(MsPaperQtypeQuestion q : queslist){
565
+            if(N_Utils.isObjectiveQuestion(q.getCtype())){
566
+                knum ++;
567
+                kscore = kscore + q.getQscore();
568
+            }else{
569
+                znum ++;
570
+                zscore = zscore + q.getQscore();
571
+            }
572
+        }
573
+
574
+        double znumrate = N_Utils.getIntegerDivideAndMulitiply(znum, paper.getPnum());
575
+        double knumrate = N_Utils.getDoubleReduce(100.0, znumrate);
576
+
577
+        double zscorerate = N_Utils.getDoubleDivideAndMulitiply(zscore, paper.getPscore());
578
+        double kscorerate = N_Utils.getDoubleReduce(100.0,zscorerate);
579
+
580
+        List<Map> ranglist = new ArrayList<>();
581
+        Map zgmap = new TreeMap();
582
+        zgmap.put("qtname","主观题");
583
+        zgmap.put("score",zscore);
584
+        zgmap.put("num",znum);
585
+        zgmap.put("srate",zscorerate);
586
+        zgmap.put("nrate",znumrate);
587
+        ranglist.add(zgmap);
588
+        Map kgmap = new TreeMap();
589
+        kgmap.put("qtname","客观题");
590
+        kgmap.put("score",kscore);
591
+        kgmap.put("num",knum);
592
+        kgmap.put("srate",kscorerate);
593
+        kgmap.put("nrate",knumrate);
594
+        ranglist.add(kgmap);
595
+
596
+        Map ztmap = new TreeMap<>();
597
+        ztmap.put("pnum", paper.getPnum());
598
+        ztmap.put("pscore",paper.getPscore());
599
+        ztmap.put("ranglist",ranglist);
600
+
601
+        //题型分布(按照名称进行分组)
602
+        fjtypelist.stream().sorted(Comparator.comparing(MsPaperQtype::getMptorder)).collect(Collectors.toList());
603
+        Map<String,List<MsPaperQtype>> qtypemap =  fjtypelist.stream().collect(Collectors.groupingBy(MsPaperQtype:: getMptname, Collectors.toList()));
604
+        List<Map> qtlist = new ArrayList<>();
605
+        for(Map.Entry<String, List<MsPaperQtype>> entry : qtypemap.entrySet()){
606
+            List<MsPaperQtype> aq = entry.getValue();
607
+            int num = aq.stream().mapToInt(MsPaperQtype::getMptnum).sum();
608
+            double tscore = aq.stream().collect(Collectors.summingDouble(MsPaperQtype:: getMptscore));
609
+            tscore = N_Utils.formatDouble(tscore,1);
610
+            double storerate = N_Utils.getDoubleDivideAndMulitiply(tscore, paper.getPscore());
611
+            //获取小题题号
612
+
613
+            List<Integer> orderlist = new ArrayList<>();
614
+            List<Integer> mptqidlist = new ArrayList<>();
615
+            List<String> qnlist = new ArrayList<>();
616
+            List<Double> scorelist = new ArrayList<>();
617
+            List<String> qnamelist = new ArrayList<>();
618
+            for(MsPaperQtype t : aq){
619
+                List<MsPaperQtypeQuestion> qlist = t.getQuestions();
620
+                for(MsPaperQtypeQuestion q : qlist){
621
+                    orderlist.add(q.getQorder());
622
+                    mptqidlist.add(q.getId());
623
+                    qnlist.add(q.getQn());
624
+                    scorelist.add(q.getQscore());
625
+                    qnamelist.add(q.getQtypename());
626
+                }
627
+            }
628
+
629
+            Map anmap = new TreeMap();//题型对应试题的id和基础信息
630
+            anmap.put("orders",orderlist);
631
+            anmap.put("mptqids",mptqidlist);
632
+            anmap.put("qns",qnlist);
633
+            anmap.put("scores",scorelist);
634
+            anmap.put("qtypenames",qnamelist);
635
+
636
+            Map tmap = new TreeMap();
637
+            tmap.put("qtname",entry.getKey());
638
+            tmap.put("qtid",aq.get(0).getMptid());
639
+            tmap.put("num",num);
640
+            tmap.put("score",tscore);
641
+            tmap.put("srate",storerate);
642
+            tmap.put("ques",anmap);
643
+            qtlist.add(tmap);
644
+        }
645
+
646
+        MsPaperAnalyze epa = new MsPaperAnalyze();
647
+        epa.setMpid(paper.getMpid());
648
+        epa.setAlljson(JSON.toJSONString(ztmap));
649
+        epa.setQnumjson(JSON.toJSONString(qtlist));
650
+        msPaperAnalyzeMapper.insertUseGeneratedKeys(epa);
651
+    }
652
+
653
+
654
+    /*
655
+     * @Description 试卷答案-客观题
656
+     * @Date 2024/10/17 15:38:44
657
+     * @Author WN
658
+     * @Param [qtype]
659
+     * @Return com.xhkjedu.vo.ResultVo
660
+     **/
661
+    public ResultVo answerObjective(MsPaperQtype qtype){
662
+        Integer mpid = qtype.getMpid();
663
+        List<MsPaperQtypeQuestion> questions = qtype.getQuestions();
664
+        for(MsPaperQtypeQuestion question : questions) {
665
+            if(N_Utils.isTrueInteger(question.getMptqid()) || N_Utils.isEmpty(question.getQanswer())){
666
+                return new ResultVo(1,"请设置对应试题答案");
667
+            }
668
+        }
669
+
670
+        //获取试题答案改变的试题
671
+        List<MsPaperQtypeQuestion> changelist = listChangeQuestionAnswer(questions,mpid);
672
+
673
+        //获取考试状态
674
+        int examstate = 0;
675
+
676
+
677
+        saveQuestionAnswerObjective(mpid,changelist);
678
+
679
+        //判断考试状态是开始,已提交学生重新批阅
680
+
681
+        return new ResultVo(0,"保存成功");
682
+    }
683
+    //获取答案改变的试题
684
+    private List<MsPaperQtypeQuestion> listChangeQuestionAnswer(List<MsPaperQtypeQuestion> questions,Integer mpid){
685
+        List<MsPaperQtypeQuestion> changelist = new ArrayList<>();
686
+        //获取试卷中原试题答案
687
+        List<MsPaperQtypeQuestion> yqlist = msPaperQtypeQuestionMapper.listQuestionsByEpid(mpid);
688
+        for(MsPaperQtypeQuestion tq : questions){
689
+            if(N_Utils.isTrueInteger(tq.getMptqid()) && N_Utils.isNotEmpty(tq.getQanswer())){
690
+                MsPaperQtypeQuestion yq = yqlist.stream().filter(y -> y.getMptqid().equals(tq.getMptqid())).findFirst().orElse(null);
691
+
692
+                if(N_Utils.isObjectiveQuestion(yq.getCtype())){
693
+                    if(!tq.getQanswer().equalsIgnoreCase(yq.getQanswer()) || !tq.getLoseoption().equals(yq.getLoseoption()) || !tq.getScoreset().equals(yq.getScoreset())){
694
+                        //说明新设置的答案和原设置的答案不一致,则为变动的
695
+                        tq.setCtype(yq.getCtype());
696
+                        tq.setQscore(yq.getQscore());
697
+                        changelist.add(tq);
698
+                    }
699
+                }
700
+            }
701
+        }
702
+        return changelist;
703
+    }
704
+
705
+    @Transactional(rollbackFor = Exception.class)
706
+    public void saveQuestionAnswerObjective(Integer mpid,List<MsPaperQtypeQuestion> questions){
707
+        //更新试卷设置答案状态
708
+        msPaperMapper.updatePaperAnswer(1,mpid);
709
+        //保存试卷答案
710
+        msPaperQtypeQuestionMapper.updateBatchQuestionAnswer(questions);
711
+        //保存试卷模版题块中试题答案
712
+
713
+        //如果考试一开始,标记需要批改的学生试题
714
+    }
715
+
716
+    /*
717
+     * @Description 试卷答案-主观题
718
+     * @Date 2024/10/17 15:40:47
719
+     * @Author WN
720
+     * @Param [qtype]
721
+     * @Return com.xhkjedu.vo.ResultVo
722
+     **/
723
+    @Transactional(rollbackFor = Exception.class)
724
+    public void answerSubjective(MsPaper paper){
725
+        Integer mpid = paper.getMpid();
726
+        List<MsPaperFile> pfiles = paper.getPfiles();
727
+        //删除已保存主观题答案图片
728
+        msPaperFileMapper.deleteByMpidAndType(mpid,2);
729
+        Integer timestamp = N_Utils.getSecondTimestamp();
730
+        for (MsPaperFile pf : pfiles) {
731
+            pf.setMpid(mpid);
732
+            pf.setCreatetime(timestamp);
733
+            pf.setFiletype(2);
734
+        }
735
+        msPaperFileMapper.insertList(pfiles);
736
+    }
737
+
738
+    /*
739
+     * @Description 试卷题干文件
740
+     * @Date 2024/10/17 16:00:37
741
+     * @Author WN
742
+     * @Param [paper]
743
+     * @Return com.xhkjedu.vo.ResultVo
37 744
      **/
38
-    public List<MsPaperQtype> queryList(MsPaperQtype msPaperQtype){
39
-        return msPaperQtypeMapper.selectAll();
745
+    @Transactional(rollbackFor = Exception.class)
746
+    public void savePaperFiles(MsPaper paper){
747
+        Integer mpid = paper.getMpid();
748
+        List<MsPaperFile> pfiles = paper.getPfiles();
749
+        //删除已保存主观题答案图片
750
+        msPaperFileMapper.deleteByMpidAndType(mpid,1);
751
+        Integer timestamp = N_Utils.getSecondTimestamp();
752
+        for (MsPaperFile pf : pfiles) {
753
+            pf.setMpid(mpid);
754
+            pf.setCreatetime(timestamp);
755
+            pf.setFiletype(1);
756
+        }
757
+        msPaperFileMapper.insertList(pfiles);
40 758
     }
41 759
 }

+ 77
- 0
smarking/src/main/java/com/xhkjedu/smarking/utils/ConvertUtil.java View File

@@ -0,0 +1,77 @@
1
+package com.xhkjedu.smarking.utils;
2
+
3
+import com.xhkjedu.smarking.listener.MessageSender;
4
+import org.springframework.beans.factory.annotation.Autowired;
5
+import org.springframework.stereotype.Component;
6
+
7
+import java.util.List;
8
+import java.util.Map;
9
+
10
+/**
11
+ * @author ywx
12
+ * @classname ConvertUtil
13
+ * @description 文件转换工具类
14
+ * @date 2021/10/27 9:32
15
+ **/
16
+@Component
17
+public class ConvertUtil {
18
+    @Autowired
19
+    private MessageSender messageSender;
20
+
21
+    /**
22
+     * 学生答案图片合并mq
23
+     *
24
+     * @param [psqid, userAnswers, device(1线上线下2答题卡)]
25
+     * @return void
26
+     * @author ywx
27
+     * @date 2022/5/9 14:57
28
+     */
29
+    public void imgMerge(Integer psqid, List<String> userAnswers, Integer device) {
30
+        messageSender.imgMerge(psqid, userAnswers, device);
31
+    }
32
+
33
+    /**
34
+     * @Description  答题卡base64转pdf
35
+     * @Param [epid, examid, imgs, savefolder]
36
+     * @Return void
37
+     * @Author wn
38
+     * @Date 2022/8/2 17:20
39
+     **/
40
+    public void baseToPdf(Integer examid,Integer epid, List<String> imgs, String savefolder,Integer scantroncol,String filename) {
41
+        messageSender.base64ToPdf(examid,epid,imgs, savefolder,scantroncol,filename);
42
+    }
43
+
44
+    /**
45
+     * @Description 扫码出错答题卡base64转图片
46
+     * @Param [serrorid, imgs, savefolder]
47
+     * @Return void
48
+     * @Author wn
49
+     * @Date 2022/9/19 9:50
50
+     **/
51
+    public void baseToScanImg(Integer serrorid,Integer examid, List<String> imgs, String savefolder) {
52
+        messageSender.base64ToScanImg(serrorid,examid,imgs,savefolder);
53
+    }
54
+
55
+
56
+    /** 考试附件试卷图片转pdf
57
+     * @Description
58
+     * @Param [imgs, epid]
59
+     * @Return void
60
+     * @Author wn
61
+     * @Date 2022/11/1 10:51
62
+     **/
63
+    public void imgToPdf(List<String> imgs,Integer epid){
64
+        messageSender.ImgToPdf(imgs,epid);
65
+    }
66
+
67
+    /**
68
+     * @Description 考试报告html转pdf
69
+     * @Param [map]
70
+     * @Author ywx
71
+     * @Date 2023/2/14 11:24
72
+     * @return void
73
+     **/
74
+    public void htmlToPdf(Map map) {
75
+        messageSender.htmlToPdf(map);
76
+    }
77
+}

+ 35
- 0
smarking/src/main/java/com/xhkjedu/smarking/vo/paper/MsPaperQsVo.java View File

@@ -0,0 +1,35 @@
1
+package com.xhkjedu.smarking.vo.paper;
2
+
3
+import lombok.Data;
4
+
5
+/**
6
+ * @Description 试卷分值分析
7
+ * @Author WN
8
+ * Date 2022/7/18 15:45
9
+ **/
10
+@Data
11
+public class MsPaperQsVo {
12
+
13
+    private String qtname;//名称
14
+
15
+    private double score;//分值
16
+
17
+    private int num;//数量
18
+
19
+    private double srate;//分值占比
20
+
21
+    private double nrate;//数量占比
22
+
23
+    private String qns;//小题题号
24
+    public MsPaperQsVo(){
25
+
26
+    }
27
+
28
+    public MsPaperQsVo(String qtname, double score, int num, double srate, double nrate){
29
+        this.qtname = qtname;
30
+        this.score = score;
31
+        this.num = num;
32
+        this.srate = srate;
33
+        this.nrate = nrate;
34
+    }
35
+}

+ 122
- 0
smarking/src/main/java/com/xhkjedu/smarking/vo/paper/MsPaperQtypeQuestionVo.java View File

@@ -0,0 +1,122 @@
1
+package com.xhkjedu.smarking.vo.paper;
2
+
3
+import lombok.Data;
4
+
5
+import java.util.ArrayList;
6
+import java.util.List;
7
+import java.util.Map;
8
+
9
+/**
10
+ * @Description 题库试卷题型下试题集合
11
+ * @Author WN
12
+ * Date 2022/7/20 14:17
13
+ **/
14
+@Data
15
+public class MsPaperQtypeQuestionVo {
16
+    //考试试卷题型试题表
17
+    private Integer mptqid;
18
+
19
+    //考试试卷题型表id
20
+    private Integer mptid;
21
+
22
+    //试题id
23
+    private String questionid;
24
+
25
+    //试题分值
26
+    private Double score;
27
+
28
+    //题号
29
+    private String qn;
30
+
31
+    //试题排序
32
+    private Integer qorder;
33
+
34
+    //试题处理题型
35
+    private Integer ctype;
36
+
37
+    //试题题型
38
+    private String qtypeid;
39
+
40
+    //试题题型名称
41
+    private String qtypename;
42
+
43
+    //试卷中设置的答案
44
+    private String answer;
45
+
46
+    //试题题干
47
+    private String qstem;
48
+
49
+    private String qstemtxt;
50
+
51
+    //试题选项
52
+    private String qoption;
53
+
54
+    //试题答案
55
+    private String qanswer;
56
+
57
+    //试题分析
58
+    private String qanalyze;
59
+
60
+    //难易度
61
+    private Integer complexity;
62
+
63
+    //是否是听力题0不是1是
64
+    private Integer hashear;
65
+
66
+    //听力题文件地址
67
+    private String hearfile;
68
+
69
+    //试题类型1单体2母题3子题
70
+    private Integer qlevel;
71
+
72
+    //子题排序
73
+    private Integer sorder;
74
+
75
+    //母题id
76
+    private String questionpid;
77
+
78
+    //子题数量
79
+    private Integer snum;
80
+
81
+    //母题主键
82
+    private String mquestionid;
83
+
84
+    //母题难易度
85
+    private Integer  mcomplexity;
86
+
87
+    //母题题干
88
+    private String  mqstem;
89
+
90
+    //母题题型id
91
+    private String mqtypeid;
92
+
93
+    //母题名称
94
+    private String mqtypename;
95
+
96
+    //母题处理题型
97
+    private Integer mctype;
98
+
99
+    //母题答案
100
+    private String mqanswer;
101
+
102
+    //母题解析
103
+    private String mqanalyze;
104
+
105
+    //母题类型
106
+    private Integer mqlevel;
107
+
108
+    //是否是听力题0不是1是
109
+    private Integer mhashear;
110
+
111
+    //听力题文件地址
112
+    private String mhearfile;
113
+
114
+    //试题关联知识点
115
+    private List<Map> points = new ArrayList<>();
116
+
117
+    //子题集合
118
+    private List<MsPaperQtypeQuestionVo> sonques;
119
+
120
+    private Integer mqspid;
121
+
122
+}

+ 36
- 0
smarking/src/main/java/com/xhkjedu/smarking/vo/paper/MsPaperQtypeVo.java View File

@@ -0,0 +1,36 @@
1
+package com.xhkjedu.smarking.vo.paper;
2
+
3
+import lombok.Data;
4
+
5
+import java.util.ArrayList;
6
+import java.util.List;
7
+
8
+/**
9
+ * @Description 题库试卷题型
10
+ * @Author WN
11
+ * Date 2022/7/20 14:17
12
+ **/
13
+@Data
14
+public class MsPaperQtypeVo {
15
+    private Integer mpid;
16
+    //考试试卷题型表
17
+    private Integer mptid;
18
+
19
+    // //处理题型
20
+    // private Integer ctype;
21
+
22
+    //题型名称
23
+    private String mptname;
24
+
25
+    //题型下试题数量
26
+    private Integer mptnum;
27
+
28
+    //题型排序
29
+    private Integer mptorder;
30
+
31
+    //题型下试题总分值
32
+    private Double mptscore;
33
+
34
+    //试题集合
35
+    private List<MsPaperQtypeQuestionVo> questions = new ArrayList<>();
36
+}

+ 33
- 0
smarking/src/main/java/com/xhkjedu/smarking/vo/paper/QuestionOrderVo.java View File

@@ -0,0 +1,33 @@
1
+package com.xhkjedu.smarking.vo.paper;
2
+
3
+import lombok.Data;
4
+
5
+/**
6
+ * @Description 试题题号
7
+ * @Author WN
8
+ **/
9
+@Data
10
+public class QuestionOrderVo {
11
+
12
+    private String id;
13
+
14
+    private String name;
15
+
16
+    private Integer complexity;
17
+
18
+    //试题类型1单题2母题
19
+    private Integer qlevel;
20
+
21
+    private Double score;//试题分值
22
+
23
+    //试题题号(单题、母题) (子题不占题号)
24
+    private Integer order;
25
+
26
+    private String questionid;//试题id(单题id,母题id)
27
+
28
+    //试题题号对应子题在试卷中id(单题时数组长度为1)
29
+    private Integer[] mptqids;
30
+
31
+    //试题题号对应子题在试卷中显示的题号(单题时数组长度为1)
32
+    private String[] qns;
33
+}

+ 21
- 0
smarking/src/main/java/com/xhkjedu/smarking/vo/paper/QuestionPointVo.java View File

@@ -0,0 +1,21 @@
1
+package com.xhkjedu.smarking.vo.paper;
2
+
3
+import lombok.Data;
4
+
5
+import java.util.List;
6
+import java.util.Map;
7
+
8
+/**
9
+ * @Description 试题知识点
10
+ * @Author WN
11
+ **/
12
+@Data
13
+public class QuestionPointVo {
14
+    private Integer order;//试题排序
15
+    private Integer mptqid;//试卷中试题id单题,母子题的情况下为0
16
+    private String questionid;//试题id
17
+    private Double score;//单题、母题分值
18
+    private Integer qlevel;//试题级别1单题2母题
19
+    private List<Integer> mptqids;//试题子题集合
20
+    private List<Map> pointids;//知识点id和知识点分值
21
+}

+ 20
- 1
smarking/src/main/resources/application.properties View File

@@ -36,10 +36,29 @@ mapper.mappers=com.xhkjedu.base.TkMapper
36 36
 mybatis.configuration.call-setters-on-nulls=true
37 37
 mapper.identity=MYSQL
38 38
 #\u6253\u5370\u65E5\u5FD7
39
-logging.level.com.xhkjedu.sclass.mapper=debug
39
+logging.level.com.xhkjedu.smarking.mapper=debug
40 40
 #\u5206\u9875
41 41
 spring.pagehelper.helper-dialect=mysql
42 42
 
43
+#\u6D88\u8D39\u8005mq
44
+rabbitmq.markingImgMergeHandleQueue=xhkjedutest.xhschool.markingImgMergeHandleQueue_dev
45
+rabbitmq.markingBaseToPdfHandleQueue=xhkjedutest.xhschool.markingBaseToPdfHandleQueue_dev
46
+rabbitmq.markingBaseToScanImgHandleQueue=xhkjedutest.xhschool.markingBaseToScanImgHandleQueue_dev
47
+rabbitmq.markingImgToPdfHandleQueue=xhkjedutest.xhschool.markingImgToPdfHandleQueue_dev
48
+rabbitmq.markingHtmlToPdfHandleQueue=xhkjedutest.xhschool.markingHtmlToPdfHandleQueue_dev
49
+#\u8D1F\u8F7D\u4E00\u751F\u4EA7\u8005mq
50
+rabbitmq.markingImgMergeQueue=xhkjedutest.xhschool.markingImgMergeQueue_node01_dev
51
+rabbitmq.markingBaseToPdfQueue=xhkjedutest.xhschool.markingBaseToPdfQueue_node01_dev
52
+rabbitmq.markingBaseToScanImgQueue=xhkjedutest.xhschool.markingBaseToScanImgQueue_node01_dev
53
+rabbitmq.markingImgToPdfQueue=xhkjedutest.xhschool.markingImgToPdfQueue_node01_dev
54
+rabbitmq.markingHtmlToPdfQueue=xhkjedutest.xhschool.markingHtmlToPdfQueue_node01_dev
55
+#\u8D1F\u8F7D\u4E8C\u751F\u4EA7\u8005mq
56
+rabbitmq.markingImgMergeQueue2=xhkjedutest.xhschool.markingImgMergeQueue_node02_dev
57
+rabbitmq.markingBaseToPdfQueue2=xhkjedutest.xhschool.markingBaseToPdfQueue_node02_dev
58
+rabbitmq.markingBaseToScanImgQueue2=xhkjedutest.xhschool.markingBaseToScanImgQueue_node02_dev
59
+rabbitmq.markingImgToPdfQueue2=xhkjedutest.xhschool.markingImgToPdfQueue_node02_dev
60
+
61
+
43 62
 #\u6D88\u8D39\u8005mq
44 63
 rabbitmq.handleLogQueue=xhkjedutest.xhschool.handleLogQueue_dev
45 64
 spring.rabbitmq.host=49.4.26.249

+ 5
- 0
smarking/src/main/resources/mapper/paper/MsPaperAnalyzeMapper.xml View File

@@ -1,4 +1,9 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
 <mapper namespace="com.xhkjedu.smarking.mapper.paper.MsPaperAnalyzeMapper">
4
+
5
+    <!--根据试卷ID删除试卷分析-->
6
+    <delete id="deleteByMpId">
7
+        DELETE FROM ms_paper_analyze WHERE mpid = #{mpId}
8
+    </delete>
4 9
 </mapper>

+ 5
- 0
smarking/src/main/resources/mapper/paper/MsPaperFileMapper.xml View File

@@ -1,4 +1,9 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
 <mapper namespace="com.xhkjedu.smarking.mapper.paper.MsPaperFileMapper">
4
+
5
+    <!--删除试卷指定类型附件-->
6
+    <delete id="deleteByMpidAndType">
7
+        delete from ms_paper_file where mpid=#{mpid} and filetype=#{filetype}
8
+    </delete>
4 9
 </mapper>

+ 12
- 0
smarking/src/main/resources/mapper/paper/MsPaperMapper.xml View File

@@ -1,4 +1,16 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
 <mapper namespace="com.xhkjedu.smarking.mapper.paper.MsPaperMapper">
4
+
5
+    <!--设置试卷中试题数量-->
6
+    <update id="updatePaperInfo">
7
+        update ms_paper set ptype=#{paper.ptype},pnum=#{paper.pnum},pscore=#{paper.pscore},answered=#{paper.answered},
8
+        hearfile=#{paper.hearfile},hearnum=#{paper.hearnum},hasfile=#{paper.hasfile},handleid=#{paper.handleid},
9
+        handletime=#{paper.handletime} where epid=#{paper.epid}
10
+    </update>
11
+
12
+    <!--更新试卷答案设置状态-->
13
+    <update id="updatePaperAnswer">
14
+        update ms_paper set answered=#{answered} where mpid=#{mpid}
15
+    </update>
4 16
 </mapper>

+ 110
- 0
smarking/src/main/resources/mapper/paper/MsPaperQtypeMapper.xml View File

@@ -1,4 +1,114 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
 <mapper namespace="com.xhkjedu.smarking.mapper.paper.MsPaperQtypeMapper">
4
+
5
+    <!--根据试卷ID删除试卷结构-->
6
+    <delete id="deleteByMpId">
7
+        DELETE FROM ms_paper_qtype WHERE mpid = #{mpId}
8
+    </delete>
9
+
10
+    <!--试卷题型试题信息(题库)-->
11
+    <resultMap id="qtypesQuestion" type="com.xhkjedu.smarking.vo.paper.MsPaperQtypeVo">
12
+        <result column="mptid" property="mptid"></result>
13
+        <result column="mpid" property="mpid"></result>
14
+        <result column="ctype" property="ctype"></result>
15
+        <result column="mptname" property="mptname"></result>
16
+        <result column="mptnum" property="mptnum"></result>
17
+        <result column="mptorder" property="mptorder"></result>
18
+        <result column="mptscore" property="mptscore"></result>
19
+        <collection property="questions" ofType="com.xhkjedu.smarking.vo.paper.MsPaperQtypeQuestionVo" javaType="java.util.List">
20
+            <result column="mptqid" property="mptqid"></result>
21
+            <result column="mptid" property="mptid"></result>
22
+            <result column="questionid" property="questionid"></result>
23
+            <result column="score" property="score"></result>
24
+            <result column="qorder" property="qorder"></result>
25
+            <result column="qn" property="qn"></result>
26
+            <result column="qtypeid" property="qtypeid"></result>
27
+            <result column="qtypename" property="qtypename"></result>
28
+            <result column="answer" property="answer"></result>
29
+            <result column="qctype" property="ctype"></result>
30
+            <result column="qstem" property="qstem"></result>
31
+            <result column="qstemtxt" property="qstemtxt"></result>
32
+            <result column="qoption" property="qoption"></result>
33
+            <result column="qanswer" property="qanswer"></result>
34
+            <result column="qanalyze" property="qanalyze"></result>
35
+            <result column="complexity" property="complexity"></result>
36
+            <result column="hashear" property="hashear"></result>
37
+            <result column="hearfile" property="hearfile"></result>
38
+            <result column="qlevel" property="qlevel"></result>
39
+            <result column="sorder" property="sorder"></result>
40
+            <result column="snum" property="snum"></result>
41
+            <result column="questionpid" property="questionpid"></result>
42
+            <result column="mqstem" property="mqstem"></result>
43
+            <result column="mcomplexity" property="mcomplexity"></result>
44
+            <result column="mqtypeid" property="mqtypeid"></result>
45
+            <result column="mqtypename" property="mqtypename"></result>
46
+            <result column="mctype" property="mctype"></result>
47
+            <result column="mqanswer" property="mqanswer"></result>
48
+            <result column="mqanalyze" property="mqanalyze"></result>
49
+            <result column="mqlevel" property="mqlevel"></result>
50
+            <result column="mhashear" property="mhashear"></result>
51
+            <result column="mhearfile" property="mhearfile"></result>
52
+            <collection property="points" ofType="java.util.Map" javaType="java.util.List"
53
+                        column="{questionid=questionid,qlevel=qlevel,questionpid=questionpid" select="listQuestionPoints">
54
+                <result column="pointid" property="pointid"></result>
55
+                <result column="pointname" property="pointname"></result>
56
+            </collection>
57
+        </collection>
58
+    </resultMap>
59
+    <!--试题详情-->
60
+    <select id="listPaperQtypeQuestions" resultMap="qtypesQuestion">
61
+        select t.mptid,t.mpid,t.ctype,t.mptname,t.mptnum,t.mptorder,t.mptscore,tq.mptqid,tq.questionid,tq.answer,
62
+        tq.score,tq.qn,tq.qorder,q.qtypeid,q.qtypename,q.ctype qctype,q.qstem,q.qstemtxt,q.qoption,q.qanswer,q.qanalyze,
63
+        q.complexity,q.hashear,q.hearfile,q.qlevel,q.sorder,q.questionpid,fq.snum,fq.qstem as mqstem,fq.complexity as mcomplexity,
64
+        fq.qtypeid mqtypeid,fq.qtypename mqtypename,fq.ctype mctype,fq.qanalyze mqanalyze,fq.qanswer mqanswer,
65
+        fq.qlevel mqlevel,fq.hashear mhashear,fq.hearfile mhearfile
66
+        from ms_paper_qtype_question tq left join ms_paper_qtype t on t.mptid=tq.mptid
67
+        left join t_question q on tq.questionid=q.questionid
68
+        left JOIN t_question fq on q.questionpid=fq.questionid
69
+        where t.mpid=#{mpid} order by t.mptorder,tq.qorder,q.sorder
70
+    </select>
71
+    <!--试题关联知识点-->
72
+    <select id="listQuestionPoints" resultType="java.util.Map">
73
+        select qp.pointid,p.pointname from t_question_point qp
74
+        left join t_point p on qp.pointid=p.pointid
75
+        where
76
+        <if test="qlevel==1">
77
+            qp.questionid=#{questionid}
78
+        </if>
79
+        <if test="qlevel==3">
80
+            qp.questionid=#{questionpid}
81
+        </if>
82
+        group by qp.pointid order by p.pointorder,p.pointid
83
+    </select>
84
+
85
+    <!--试卷题型试题信息(附件)-->
86
+    <resultMap id="fjtypeQuestions" type="com.xhkjedu.smarking.vo.paper.MsPaperQtypeVo">
87
+        <result column="mptid" property="mptid"></result>
88
+        <result column="mpid" property="mpid"></result>
89
+        <result column="ctype" property="ctype"></result>
90
+        <result column="mptname" property="mptname"></result>
91
+        <result column="mptnum" property="mptnum"></result>
92
+        <result column="mptorder" property="mptorder"></result>
93
+        <result column="mptscore" property="mptscore"></result>
94
+        <collection property="questions" ofType="java.util.Map" javaType="java.util.List">
95
+            <result column="mptqid" property="mptqid"></result>
96
+            <result column="mptid" property="mptid"></result>
97
+            <result column="score" property="score"></result>
98
+            <result column="qorder" property="qorder"></result>
99
+            <result column="qtypeid" property="qtypeid"></result>
100
+            <result column="qtypename" property="qtypename"></result>
101
+            <result column="qctype" property="ctype"></result>
102
+            <result column="qn" property="qn"></result>
103
+            <result column="optionnum" property="optionnum"></result>
104
+            <result column="answer" property="answer"></result>
105
+        </collection>
106
+    </resultMap>
107
+    <select id="listPaperQtypeQuesitonsForFj" resultMap="fjtypeQuestions">
108
+        select  t.mptid,t.mpid,t.ctype,t.mptname,t.mptnum,t.mptorder,t.mptscore,
109
+                q.mptqid,q.qtypeid,q.qtypename,q.ctype qctype,q.score,q.qn,q.qorder,q.answer,q.optionnum
110
+        from ms_paper_qtype_question q left join ms_paper_qtype t on t.mptid=q.mptid
111
+        where t.mpid=#{mpid} order by t.mptorder,q.qorder
112
+    </select>
113
+
4 114
 </mapper>

+ 49
- 0
smarking/src/main/resources/mapper/paper/MsPaperQtypeQuestionMapper.xml View File

@@ -1,4 +1,53 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2 2
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
 <mapper namespace="com.xhkjedu.smarking.mapper.paper.MsPaperQtypeQuestionMapper">
4
+
5
+    <!--保存主观题选项数量-->
6
+    <update id="updateBatchQuestionOptionnum">
7
+        update ms_paper_qtype_question
8
+        <trim prefix="set" suffixOverrides=",">
9
+            <trim prefix="optionnum = case" suffix="end,">
10
+                <foreach collection="list" item="q">
11
+                    when mptqid=#{q.mptqid} then #{q.optionnum}
12
+                </foreach>
13
+            </trim>
14
+        </trim>
15
+        <where>
16
+            <foreach collection="list" item="q" separator="or">
17
+                mptqid=#{q.mptqid}
18
+            </foreach>
19
+        </where>
20
+    </update>
21
+
22
+    <!--保存客观题答案-->
23
+    <update id="updateBatchQuestionAnswer">
24
+        update ms_paper_qtype_question
25
+        <trim prefix="set" suffixOverrides=",">
26
+            <trim prefix="qanswer = case" suffix="end,">
27
+                <foreach collection="list" item="q">
28
+                    when mptqid=#{q.mptqid} then #{q.qanswer}
29
+                </foreach>
30
+            </trim>
31
+            <trim prefix="loseoption = case" suffix="end,">
32
+                <foreach collection="list" item="q">
33
+                    when mptqid=#{q.mptqid} then #{q.loseoption}
34
+                </foreach>
35
+            </trim>
36
+            <trim prefix="scoreset = case" suffix="end,">
37
+                <foreach collection="list" item="q">
38
+                    when mptqid=#{q.mptqid} then #{q.scoreset}
39
+                </foreach>
40
+            </trim>
41
+        </trim>
42
+        <where>
43
+            <foreach collection="list" item="q" separator="or">
44
+                mptqid=#{q.mptqid}
45
+            </foreach>
46
+        </where>
47
+    </update>
48
+
49
+    <!--试卷中所有试题基础信息-->
50
+    <select id="listQuestionsByEpid" resultType="com.xhkjedu.smarking.model.paper.MsPaperQtypeQuestion">
51
+        select * from ms_paper_qtype_question where epid=#{epid} order by qorder
52
+    </select>
4 53
 </mapper>

Loading…
Cancel
Save