Selaa lähdekoodia

增加组合题

yangfeng 1 vuosi sitten
vanhempi
commit
05f0c5bf97
76 muutettua tiedostoa jossa 1466 lisäystä ja 925 poistoa
  1. 2 2
      web/pom.xml
  2. 8 8
      web/src/main/java/com/ynfy/Application.java
  3. 26 25
      web/src/main/java/com/ynfy/app/api/v1/annoation/ApiLog.java
  4. 0 1
      web/src/main/java/com/ynfy/app/api/v1/annoation/IgnoreAuth.java
  5. 13 13
      web/src/main/java/com/ynfy/app/api/v1/controller/ApiCommonController.java
  6. 22 23
      web/src/main/java/com/ynfy/app/api/v1/util/HttpDownUtils.java
  7. 21 18
      web/src/main/java/com/ynfy/buss/course/category/mapper/xml/CourseCategoryMapper.xml
  8. 0 2
      web/src/main/java/com/ynfy/buss/course/course/controller/CourseController.java
  9. 1 1
      web/src/main/java/com/ynfy/buss/course/course/mapper/CourseMapper.java
  10. 53 55
      web/src/main/java/com/ynfy/buss/course/course/mapper/xml/CourseMapper.xml
  11. 9 9
      web/src/main/java/com/ynfy/buss/course/coursecatalog/enums/CatalogType.java
  12. 9 9
      web/src/main/java/com/ynfy/buss/course/coursecatalog/enums/ResourceType.java
  13. 1 1
      web/src/main/java/com/ynfy/buss/course/coursecatalog/mapper/CourseCatalogMapper.java
  14. 0 1
      web/src/main/java/com/ynfy/buss/course/coursecatalog/service/impl/CourseCatalogServiceImpl.java
  15. 0 1
      web/src/main/java/com/ynfy/buss/course/usercoursecatalog/mapper/UserCourseCatalogMapper.java
  16. 6 6
      web/src/main/java/com/ynfy/buss/course/usercoursecatalog/mapper/xml/UserCourseCatalogMapper.xml
  17. 1 2
      web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/IUserCourseCatalogService.java
  18. 1 4
      web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/impl/UserCourseCatalogServiceImpl.java
  19. 0 2
      web/src/main/java/com/ynfy/buss/exam/exam/entity/Exam.java
  20. 3 2
      web/src/main/java/com/ynfy/buss/exam/exam/enums/ExamState.java
  21. 3 2
      web/src/main/java/com/ynfy/buss/exam/exam/enums/PaperState.java
  22. 0 1
      web/src/main/java/com/ynfy/buss/exam/exam/mapper/ExamMapper.java
  23. 98 100
      web/src/main/java/com/ynfy/buss/exam/exam/mapper/xml/ExamMapper.xml
  24. 254 91
      web/src/main/java/com/ynfy/buss/exam/exam/service/impl/ExamServiceImpl.java
  25. 37 51
      web/src/main/java/com/ynfy/buss/exam/examreview/mapper/xml/ExamReviewMapper.xml
  26. 40 54
      web/src/main/java/com/ynfy/buss/exam/examstatistics/mapper/xml/ExamStatisticsMapper.xml
  27. 1 1
      web/src/main/java/com/ynfy/buss/exam/examuserstatistics/mapper/ExamUserStatisticsMapper.java
  28. 1 1
      web/src/main/java/com/ynfy/buss/exam/examuserstatistics/service/IExamUserStatisticsService.java
  29. 2 4
      web/src/main/java/com/ynfy/buss/exam/job/BreakExamJob.java
  30. 2 4
      web/src/main/java/com/ynfy/buss/exam/job/MakeupSubmitExamJob.java
  31. 9 9
      web/src/main/java/com/ynfy/buss/exam/paper/enmus/JoinType.java
  32. 9 9
      web/src/main/java/com/ynfy/buss/exam/paper/enmus/LevelType.java
  33. 1 1
      web/src/main/java/com/ynfy/buss/exam/paper/mapper/PaperMapper.java
  34. 15 0
      web/src/main/java/com/ynfy/buss/exam/paper/service/IPaperService.java
  35. 82 22
      web/src/main/java/com/ynfy/buss/exam/paper/service/impl/PaperServiceImpl.java
  36. 14 0
      web/src/main/java/com/ynfy/buss/exam/paperquestion/entity/PaperQuestion.java
  37. 1 1
      web/src/main/java/com/ynfy/buss/exam/paperquestion/mapper/PaperQuestionMapper.java
  38. 6 9
      web/src/main/java/com/ynfy/buss/exam/paperquestion/mapper/xml/PaperQuestionMapper.xml
  39. 2 0
      web/src/main/java/com/ynfy/buss/exam/paperquestion/service/IPaperQuestionService.java
  40. 31 3
      web/src/main/java/com/ynfy/buss/exam/paperquestion/service/impl/PaperQuestionServiceImpl.java
  41. 4 4
      web/src/main/java/com/ynfy/buss/exam/paperquestionanswer/entity/PaperQuestionAnswer.java
  42. 1 1
      web/src/main/java/com/ynfy/buss/exam/paperquestionanswer/mapper/PaperQuestionAnswerMapper.java
  43. 1 1
      web/src/main/java/com/ynfy/buss/exam/paperruledetail/mapper/PaperRuleDetailMapper.java
  44. 4 11
      web/src/main/java/com/ynfy/buss/exam/paperrulegroup/entity/PaperRuleGroup.java
  45. 28 37
      web/src/main/java/com/ynfy/buss/exam/paperrulegroup/mapper/xml/PaperRuleGroupMapper.xml
  46. 1 1
      web/src/main/java/com/ynfy/buss/exam/paperrulegroup/service/impl/PaperRuleGroupServiceImpl.java
  47. 12 6
      web/src/main/java/com/ynfy/buss/exam/question/controller/QuestionController.java
  48. 6 2
      web/src/main/java/com/ynfy/buss/exam/question/dto/QuestionConditionDTO.java
  49. 25 0
      web/src/main/java/com/ynfy/buss/exam/question/entity/Question.java
  50. 11 10
      web/src/main/java/com/ynfy/buss/exam/question/enums/QuestionType.java
  51. 132 115
      web/src/main/java/com/ynfy/buss/exam/question/mapper/xml/QuestionMapper.xml
  52. 42 0
      web/src/main/java/com/ynfy/buss/exam/question/service/IQuestionService.java
  53. 205 2
      web/src/main/java/com/ynfy/buss/exam/question/service/impl/QuestionServiceImpl.java
  54. 1 1
      web/src/main/java/com/ynfy/buss/exam/questionanswer/mapper/QuestionAnswerMapper.java
  55. 7 0
      web/src/main/java/com/ynfy/buss/exam/questionanswer/service/IQuestionAnswerService.java
  56. 11 0
      web/src/main/java/com/ynfy/buss/exam/questionanswer/service/impl/QuestionAnswerServiceImpl.java
  57. 6 2
      web/src/main/java/com/ynfy/buss/exam/repository/entity/Repository.java
  58. 1 1
      web/src/main/java/com/ynfy/buss/exam/repository/mapper/RepositoryMapper.java
  59. 1 1
      web/src/main/java/com/ynfy/buss/exam/repository/service/IRepositoryService.java
  60. 1 1
      web/src/main/java/com/ynfy/buss/exam/repository/service/impl/RepositoryServiceImpl.java
  61. 48 47
      web/src/main/java/com/ynfy/buss/exam/userexam/mapper/xml/UserExamMapper.xml
  62. 18 0
      web/src/main/java/com/ynfy/buss/exam/userexamquestion/entity/UserExamQuestion.java
  63. 1 1
      web/src/main/java/com/ynfy/buss/exam/userexamquestion/mapper/UserExamQuestionMapper.java
  64. 1 1
      web/src/main/java/com/ynfy/buss/exam/userexamquestionanswer/mapper/UserExamQuestionAnswerMapper.java
  65. 1 1
      web/src/main/java/com/ynfy/buss/exam/userexamquestionanswer/service/IUserExamQuestionAnswerService.java
  66. 14 14
      web/src/main/java/com/ynfy/buss/exam/userexamresult/mapper/xml/UserExamResultMapper.xml
  67. 9 9
      web/src/main/java/com/ynfy/buss/practice/userpractice/enums/PracticeMode.java
  68. 3 3
      web/src/main/java/com/ynfy/buss/practice/userpractice/mapper/xml/UserPracticeMapper.xml
  69. 0 1
      web/src/main/java/com/ynfy/buss/practice/userpractice/service/IUserPracticeService.java
  70. 3 1
      web/src/main/java/com/ynfy/common/utils/CronUtils.java
  71. 3 0
      web/src/main/java/com/ynfy/common/utils/SM3Utils.java
  72. 5 15
      web/src/main/java/com/ynfy/common/utils/SM4Utils.java
  73. 4 8
      web/src/main/java/com/ynfy/common/utils/ThreadPoolUtil.java
  74. 4 5
      web/src/main/java/com/ynfy/config/WebMvcConfiguration.java
  75. 1 8
      web/src/main/resources/jeecg/jeecg_config.properties
  76. 67 67
      web/src/main/resources/logback-spring.xml

+ 2 - 2
web/pom.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
         <groupId>com.ynfy</groupId>

+ 8 - 8
web/src/main/java/com/ynfy/Application.java

@@ -14,20 +14,15 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 
 /**
-* 单体启动类
-* 报错提醒: 未集成mongo报错,可以打开启动类上面的注释 exclude={MongoAutoConfiguration.class}
-*/
+ * 单体启动类
+ * 报错提醒: 未集成mongo报错,可以打开启动类上面的注释 exclude={MongoAutoConfiguration.class}
+ */
 @Slf4j
 @SpringBootApplication
 @ComponentScan(basePackages = {"org.jeecg", "com.ynfy"})
 //@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
 public class Application extends SpringBootServletInitializer {
 
-    @Override
-    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
-        return application.sources(Application.class);
-    }
-
     public static void main(String[] args) throws UnknownHostException {
         ConfigurableApplicationContext application = SpringApplication.run(Application.class, args);
         Environment env = application.getEnvironment();
@@ -43,4 +38,9 @@ public class Application extends SpringBootServletInitializer {
 
     }
 
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+        return application.sources(Application.class);
+    }
+
 }

+ 26 - 25
web/src/main/java/com/ynfy/app/api/v1/annoation/ApiLog.java

@@ -7,7 +7,7 @@ import java.lang.annotation.*;
 
 /**
  * 系统日志注解
- * 
+ *
  * @Author scott
  * @email jeecgos@163.com
  * @Date 2019年1月14日
@@ -17,30 +17,31 @@ import java.lang.annotation.*;
 @Documented
 public @interface ApiLog {
 
-	/**
-	 * 日志内容
-	 * 
-	 * @return
-	 */
-	String value() default "";
+    /**
+     * 日志内容
+     *
+     * @return
+     */
+    String value() default "";
 
-	/**
-	 * 日志类型
-	 * 
-	 * @return 0:操作日志;1:登录日志;2:定时任务;
-	 */
-	int logType() default CommonConstant.LOG_TYPE_2;
-	
-	/**
-	 * 操作日志类型
-	 * 
-	 * @return (1查询,2添加,3修改,4删除)
-	 */
-	int operateType() default 0;
+    /**
+     * 日志类型
+     *
+     * @return 0:操作日志;1:登录日志;2:定时任务;
+     */
+    int logType() default CommonConstant.LOG_TYPE_2;
 
-	/**
-	 * 模块类型 默认为common
-	 * @return
-	 */
-	ModuleType module() default ModuleType.COMMON;
+    /**
+     * 操作日志类型
+     *
+     * @return (1查询,2添加,3修改,4删除)
+     */
+    int operateType() default 0;
+
+    /**
+     * 模块类型 默认为common
+     *
+     * @return
+     */
+    ModuleType module() default ModuleType.COMMON;
 }

+ 0 - 1
web/src/main/java/com/ynfy/app/api/v1/annoation/IgnoreAuth.java

@@ -5,7 +5,6 @@ import java.lang.annotation.*;
 
 /**
  * 忽略Token验证
- *
  */
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)

+ 13 - 13
web/src/main/java/com/ynfy/app/api/v1/controller/ApiCommonController.java

@@ -61,6 +61,19 @@ public class ApiCommonController {
     @Autowired
     private IFileInfoService fileInfoService;
 
+    /**
+     * 把指定URL后的字符串全部截断当成参数
+     * 这么做是为了防止URL中包含中文或者特殊字符(/等)时,匹配不了的问题
+     *
+     * @param request
+     * @return
+     */
+    private static String extractPathFromPattern(final HttpServletRequest request) {
+        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
+        String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
+        return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
+    }
+
     /**
      * 预览图片&下载文件
      * 请求地址:http://localhost:8080/common/static/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
@@ -182,19 +195,6 @@ public class ApiCommonController {
         }
     }
 
-    /**
-     * 把指定URL后的字符串全部截断当成参数
-     * 这么做是为了防止URL中包含中文或者特殊字符(/等)时,匹配不了的问题
-     *
-     * @param request
-     * @return
-     */
-    private static String extractPathFromPattern(final HttpServletRequest request) {
-        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
-        String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
-        return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
-    }
-
     /**
      * 文件上传统一方法
      *

+ 22 - 23
web/src/main/java/com/ynfy/app/api/v1/util/HttpDownUtils.java

@@ -64,6 +64,28 @@ public class HttpDownUtils {
         return bos.toByteArray();
     }
 
+    /**
+     * 从网络下载文件到本地,然后上传到minio
+     *
+     * @param url
+     * @param fileName
+     * @param tmpPath
+     * @return
+     */
+    public static String downFormUrlAndUpload(String url, String fileName, String tmpPath) {
+        try {
+            HttpDownUtils.downLoadFromUrl(url, fileName, tmpPath);
+            String destFilePath = tmpPath + File.separator + fileName;
+            File destFile = new File(destFilePath);
+            InputStream inputStream = new BufferedInputStream(new FileInputStream(destFile));
+            String objectName = MinioUtil.uploadFile(inputStream, fileName);
+            destFile.delete();
+            return objectName;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
 
     /**
      * 从网络url下载excel
@@ -109,27 +131,4 @@ public class HttpDownUtils {
         }
     }
 
-
-    /**
-     * 从网络下载文件到本地,然后上传到minio
-     * @param url
-     * @param fileName
-     * @param tmpPath
-     * @return
-     */
-    public static String downFormUrlAndUpload(String url, String fileName, String tmpPath) {
-        try {
-            HttpDownUtils.downLoadFromUrl(url, fileName, tmpPath);
-            String destFilePath = tmpPath + File.separator + fileName;
-            File destFile = new File(destFilePath);
-            InputStream inputStream = new BufferedInputStream(new FileInputStream(destFile));
-            String objectName = MinioUtil.uploadFile(inputStream, fileName);
-            destFile.delete();
-            return objectName;
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
 }

+ 21 - 18
web/src/main/java/com/ynfy/buss/course/category/mapper/xml/CourseCategoryMapper.xml

@@ -2,24 +2,27 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ynfy.buss.course.category.mapper.CourseCategoryMapper">
 
-	<update id="updateTreeNodeStatus" parameterType="java.lang.String">
-		update course_category set has_child = #{status} where id = #{id}
-	</update>
+    <update id="updateTreeNodeStatus" parameterType="java.lang.String">
+        update course_category
+        set has_child = #{status}
+        where id = #{id}
+    </update>
 
-  	<!-- 【vue3专用】 -->
-	<select id="queryListByPid" parameterType="java.lang.Object" resultType="org.jeecg.common.system.vo.SelectTreeModel">
-		select
-		  id as "key",
-		  name as "title",
-		  (case when has_child = '1' then 0 else 1 end) as isLeaf,
-		  pid as parentId
-		from course_category
-		where pid = #{pid}
-		<if test="query != null">
-			<foreach collection="query.entrySet()" item="value" index="key">
-				and ${key} = #{value}
-			</foreach>
-		</if>
-	</select>
+    <!-- 【vue3专用】 -->
+    <select id="queryListByPid" parameterType="java.lang.Object"
+            resultType="org.jeecg.common.system.vo.SelectTreeModel">
+        select
+        id as "key",
+        name as "title",
+        (case when has_child = '1' then 0 else 1 end) as isLeaf,
+        pid as parentId
+        from course_category
+        where pid = #{pid}
+        <if test="query != null">
+            <foreach collection="query.entrySet()" item="value" index="key">
+                and ${key} = #{value}
+            </foreach>
+        </if>
+    </select>
 
 </mapper>

+ 0 - 2
web/src/main/java/com/ynfy/buss/course/course/controller/CourseController.java

@@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ynfy.buss.course.course.entity.Course;
 import com.ynfy.buss.course.course.service.ICourseService;
 import com.ynfy.buss.course.coursecatalog.service.ICourseCatalogService;
-import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
 import com.ynfy.buss.course.usercoursecatalog.service.IUserCourseCatalogService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -30,7 +29,6 @@ import javax.servlet.http.HttpServletResponse;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * @Description: course

+ 1 - 1
web/src/main/java/com/ynfy/buss/course/course/mapper/CourseMapper.java

@@ -11,7 +11,7 @@ import java.util.List;
 /**
  * @Description: course
  * @Author: jeecg-boot
- * @Date:   2023-10-24
+ * @Date: 2023-10-24
  * @Version: V1.0
  */
 public interface CourseMapper extends BaseMapper<Course> {

+ 53 - 55
web/src/main/java/com/ynfy/buss/course/course/mapper/xml/CourseMapper.xml

@@ -1,52 +1,52 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ynfy.buss.course.course.mapper.CourseMapper">
-    <select id="selectCourseList"  resultType="com.ynfy.buss.course.course.entity.Course">
+    <select id="selectCourseList" resultType="com.ynfy.buss.course.course.entity.Course">
         SELECT
-                tmp1.*,
-                u.realname AS teacherName,
-                tmp2.learner_number
+        tmp1.*,
+        u.realname AS teacherName,
+        tmp2.learner_number
         FROM
         (
-            SELECT
-                *
-            FROM course
-            WHERE
-                open_type = 1
+        SELECT
+        *
+        FROM course
+        WHERE
+        open_type = 1
         <choose>
             <when test="user!=null and user.orgCode!=null and user.orgCode!=''">
-            UNION
+                UNION
                 SELECT
-                    *
+                *
                 FROM course
                 WHERE
-                    open_type = 2
-                    AND learner like concat('%', #{user.orgCode}, '%')
+                open_type = 2
+                AND learner like concat('%', #{user.orgCode}, '%')
             </when>
 
             <when test="user!=null and user.username!=null and user.username!=''">
-            UNION
+                UNION
                 SELECT
-                    *
+                *
                 FROM course
                 WHERE
-                    open_type = 3
-                    AND learner like concat('%', #{user.username}, '%')
+                open_type = 3
+                AND learner like concat('%', #{user.username}, '%')
             </when>
         </choose>
         ) tmp1
         LEFT JOIN
         (
-            SELECT
-                cc.course_id,
-                count(
-                DISTINCT (ucc.user_id)) AS learner_number
-            FROM
-                user_course_catalog ucc
-            LEFT JOIN
-                course_catalog cc ON ucc.course_catalog_id = cc.id
-            GROUP BY
-                    cc.course_id
+        SELECT
+        cc.course_id,
+        count(
+        DISTINCT (ucc.user_id)) AS learner_number
+        FROM
+        user_course_catalog ucc
+        LEFT JOIN
+        course_catalog cc ON ucc.course_catalog_id = cc.id
+        GROUP BY
+        cc.course_id
         ) tmp2
         ON tmp1.id = tmp2.course_id
         LEFT JOIN sys_user u on tmp1.teacher_id=u.id
@@ -62,42 +62,40 @@
             </if>
         </where>
     </select>
-    <select id="queryById"  resultType="com.ynfy.buss.course.course.entity.Course">
-        SELECT
-            c.*,
-            e.title AS examTitle,
-            u.realname AS teacherName,
-            u.avatar as teacherAvatar,
-            u.teacher_introduce as teacherIntroduce,
-            u.post as teacherPost
-        FROM
-            course c
-                LEFT JOIN exam e ON c.exam_id = e.id
-                LEFT JOIN sys_user u ON c.teacher_id = u.id
+    <select id="queryById" resultType="com.ynfy.buss.course.course.entity.Course">
+        SELECT c.*,
+               e.title             AS examTitle,
+               u.realname          AS teacherName,
+               u.avatar            as teacherAvatar,
+               u.teacher_introduce as teacherIntroduce,
+               u.post              as teacherPost
+        FROM course c
+                 LEFT JOIN exam e ON c.exam_id = e.id
+                 LEFT JOIN sys_user u ON c.teacher_id = u.id
         where c.id = #{id}
     </select>
 
     <select id="listHotCourse" resultType="com.ynfy.buss.course.course.entity.Course">
         SELECT
-            c.*,
-            tmp.learn_number
+        c.*,
+        tmp.learn_number
         FROM
-            course c
+        course c
         LEFT JOIN
-            (
-                SELECT
-                    cc.course_id,
-                    count(
-                            DISTINCT ( ucc.user_id )) AS learn_number
-                FROM
-                    user_course_catalog ucc
-                        LEFT JOIN course_catalog cc ON ucc.course_catalog_id = cc.id
-                GROUP BY
-                    cc.course_id
-            ) tmp
-            ON c.id = tmp.course_id
+        (
+        SELECT
+        cc.course_id,
+        count(
+        DISTINCT ( ucc.user_id )) AS learn_number
+        FROM
+        user_course_catalog ucc
+        LEFT JOIN course_catalog cc ON ucc.course_catalog_id = cc.id
+        GROUP BY
+        cc.course_id
+        ) tmp
+        ON c.id = tmp.course_id
         ORDER BY
-                tmp.learn_number DESC
+        tmp.learn_number DESC
         <if test="limit !=null">
             LIMIT #{limit}
         </if>

+ 9 - 9
web/src/main/java/com/ynfy/buss/course/coursecatalog/enums/CatalogType.java

@@ -17,6 +17,15 @@ public enum CatalogType {
         this.value = value;
     }
 
+    public static CatalogType getByCode(Integer code) {
+        for (CatalogType catalogType : values()) {
+            if (catalogType.getCode().equals(code)) {
+                return catalogType;
+            }
+        }
+        return null;
+    }
+
     public Integer getCode() {
         return code;
     }
@@ -33,13 +42,4 @@ public enum CatalogType {
         this.value = value;
     }
 
-    public static CatalogType getByCode(Integer code) {
-        for (CatalogType catalogType : values()) {
-            if (catalogType.getCode().equals(code)) {
-                return catalogType;
-            }
-        }
-        return null;
-    }
-
 }

+ 9 - 9
web/src/main/java/com/ynfy/buss/course/coursecatalog/enums/ResourceType.java

@@ -17,6 +17,15 @@ public enum ResourceType {
         this.value = value;
     }
 
+    public static ResourceType getByCode(Integer code) {
+        for (ResourceType resourceType : values()) {
+            if (resourceType.getCode().equals(code)) {
+                return resourceType;
+            }
+        }
+        return null;
+    }
+
     public Integer getCode() {
         return code;
     }
@@ -33,13 +42,4 @@ public enum ResourceType {
         this.value = value;
     }
 
-    public static ResourceType getByCode(Integer code) {
-        for (ResourceType resourceType : values()) {
-            if (resourceType.getCode().equals(code)) {
-                return resourceType;
-            }
-        }
-        return null;
-    }
-
 }

+ 1 - 1
web/src/main/java/com/ynfy/buss/course/coursecatalog/mapper/CourseCatalogMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.course.coursecatalog.entity.CourseCatalog;
 /**
  * @Description: course_catalog
  * @Author: jeecg-boot
- * @Date:   2023-10-25
+ * @Date: 2023-10-25
  * @Version: V1.0
  */
 public interface CourseCatalogMapper extends BaseMapper<CourseCatalog> {

+ 0 - 1
web/src/main/java/com/ynfy/buss/course/coursecatalog/service/impl/CourseCatalogServiceImpl.java

@@ -11,7 +11,6 @@ import com.ynfy.buss.course.coursecatalog.service.ICourseCatalogService;
 import com.ynfy.buss.course.usercoursecatalog.entity.UserCourseCatalog;
 import com.ynfy.buss.course.usercoursecatalog.service.IUserCourseCatalogService;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.SecurityUtils;
 import org.jeecg.common.system.vo.LoginUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;

+ 0 - 1
web/src/main/java/com/ynfy/buss/course/usercoursecatalog/mapper/UserCourseCatalogMapper.java

@@ -4,7 +4,6 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.ynfy.buss.course.usercoursecatalog.entity.UserCourseCatalog;
 import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
-import com.ynfy.buss.practice.userpractice.entity.UserPractice;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;

+ 6 - 6
web/src/main/java/com/ynfy/buss/course/usercoursecatalog/mapper/xml/UserCourseCatalogMapper.xml

@@ -14,21 +14,21 @@
             count(*) AS task_num,
             sum(
                 CASE
-                    WHEN ucc.learn_process IS NOT NULL
+                WHEN ucc.learn_process IS NOT NULL
                 AND ucc.learn_process >= 1 THEN
                 1 ELSE 0
                 END
             ) AS finish_num,
             sum(
                 CASE
-                    WHEN ucc.learn_process IS NOT NULL
+                WHEN ucc.learn_process IS NOT NULL
                 THEN
-                    1 ELSE 0 END
-                ) as study_num
+                1 ELSE 0 END
+            ) as study_num
         FROM
             course_catalog cc
         LEFT JOIN user_course_catalog ucc ON cc.id = ucc.course_catalog_id
-                AND ucc.user_id = #{userId}
+        AND ucc.user_id = #{userId}
         WHERE
             cc.course_id IN
             <foreach collection="courseIds" index="index" item="id" open="(" separator="," close=")">
@@ -58,7 +58,7 @@
             <if test="userCourseCatalog.userId != null and userCourseCatalog.userId != ''">
                 AND ucc.user_id = #{userCourseCatalog.userId}
             </if>
-        ORDER by lastLearnTime desc
+            ORDER by lastLearnTime desc
         </where>
     </select>
 </mapper>

+ 1 - 2
web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/IUserCourseCatalogService.java

@@ -2,10 +2,9 @@ package com.ynfy.buss.course.usercoursecatalog.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
 import com.ynfy.buss.course.usercoursecatalog.entity.UserCourseCatalog;
 import com.ynfy.buss.course.usercoursecatalog.entity.vo.CatalogTimeVO;
-import com.ynfy.buss.practice.userpractice.entity.UserPractice;
+import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
 
 import java.util.List;
 

+ 1 - 4
web/src/main/java/com/ynfy/buss/course/usercoursecatalog/service/impl/UserCourseCatalogServiceImpl.java

@@ -6,17 +6,14 @@ import cn.hutool.core.util.NumberUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
 import com.ynfy.buss.course.coursecatalog.entity.CourseCatalog;
 import com.ynfy.buss.course.coursecatalog.service.ICourseCatalogService;
 import com.ynfy.buss.course.usercoursecatalog.entity.UserCourseCatalog;
 import com.ynfy.buss.course.usercoursecatalog.entity.vo.CatalogTimeVO;
+import com.ynfy.buss.course.usercoursecatalog.entity.vo.UserCourseStudyVO;
 import com.ynfy.buss.course.usercoursecatalog.mapper.UserCourseCatalogMapper;
 import com.ynfy.buss.course.usercoursecatalog.service.IUserCourseCatalogService;
-import com.ynfy.buss.practice.userpractice.entity.UserPractice;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.SecurityUtils;
-import org.jeecg.common.system.vo.LoginUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;

+ 0 - 2
web/src/main/java/com/ynfy/buss/exam/exam/entity/Exam.java

@@ -3,7 +3,6 @@ package com.ynfy.buss.exam.exam.entity;
 import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ynfy.buss.exam.exam.dto.QuestionTypeCountDTO;
-import com.ynfy.buss.exam.exam.enums.ExamState;
 import com.ynfy.buss.exam.paper.entity.Paper;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -17,7 +16,6 @@ import org.springframework.format.annotation.DateTimeFormat;
 import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
-import java.util.Objects;
 
 /**
  * @Description: exam

+ 3 - 2
web/src/main/java/com/ynfy/buss/exam/exam/enums/ExamState.java

@@ -3,7 +3,8 @@ package com.ynfy.buss.exam.exam.enums;
 
 /**
  * 考试状态
- * @author bool 
+ *
+ * @author bool
  * @date 2019-10-30 13:11
  */
 public interface ExamState {
@@ -29,5 +30,5 @@ public interface ExamState {
      */
     Integer OVERDUE = 3;
 
-    
+
 }

+ 3 - 2
web/src/main/java/com/ynfy/buss/exam/exam/enums/PaperState.java

@@ -3,7 +3,8 @@ package com.ynfy.buss.exam.exam.enums;
 
 /**
  * 试卷状态
- * @author bool 
+ *
+ * @author bool
  * @date 2019-10-30 13:11
  */
 public interface PaperState {
@@ -29,5 +30,5 @@ public interface PaperState {
      */
     Integer BREAK = 3;
 
-    
+
 }

+ 0 - 1
web/src/main/java/com/ynfy/buss/exam/exam/mapper/ExamMapper.java

@@ -3,7 +3,6 @@ package com.ynfy.buss.exam.exam.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.ynfy.buss.exam.exam.entity.Exam;
-import io.swagger.models.auth.In;
 import org.apache.ibatis.annotations.Param;
 import org.jeecg.common.system.vo.LoginUser;
 

+ 98 - 100
web/src/main/java/com/ynfy/buss/exam/exam/mapper/xml/ExamMapper.xml

@@ -3,36 +3,36 @@
 <mapper namespace="com.ynfy.buss.exam.exam.mapper.ExamMapper">
     <select id="detail" resultMap="examMap">
         SELECT
-                e.id,
-                e.title,
-                e.open_type,
-                e.start_time,
-                e.end_time,
-                e.total_score as exam_total_score,
-                e.qualify_score,
-                e.total_time,
-                e.leave_on,
-                e.total_leave_times,
-                e.leave_time,
-                p.id as exam_paper_id,
-                e.limit_count,
-                e.examiner,
-                e.reviewer,
-                e.reviewer_type,
-                e.question_disorder,
-                e.answer_disorder,
-                e.image,
-                e.exam_result_showtype,
-                e.show_deadline,
-                p.id as paper_id,
-                p.title as paperName,
-                p.total_score,
-                p.join_type,
-                p.create_by,
-                p.create_time,
-                p.question_count,
-                p.has_subjective,
-                <include refid="stateCondition" />
+        e.id,
+        e.title,
+        e.open_type,
+        e.start_time,
+        e.end_time,
+        e.total_score as exam_total_score,
+        e.qualify_score,
+        e.total_time,
+        e.leave_on,
+        e.total_leave_times,
+        e.leave_time,
+        p.id as exam_paper_id,
+        e.limit_count,
+        e.examiner,
+        e.reviewer,
+        e.reviewer_type,
+        e.question_disorder,
+        e.answer_disorder,
+        e.image,
+        e.exam_result_showtype,
+        e.show_deadline,
+        p.id as paper_id,
+        p.title as paperName,
+        p.total_score,
+        p.join_type,
+        p.create_by,
+        p.create_time,
+        p.question_count,
+        p.has_subjective,
+        <include refid="stateCondition"/>
         FROM exam e
         LEFT JOIN paper p on e.paper_id = p.id
         <where>
@@ -78,59 +78,59 @@
         </association>
     </resultMap>
 
-    <select id="selectExamList"  resultType="com.ynfy.buss.exam.exam.entity.Exam">
+    <select id="selectExamList" resultType="com.ynfy.buss.exam.exam.entity.Exam">
         SELECT
-            tmp1.*
-            <choose>
-                <when test="user!=null and user.id!=null and user.id!=''">
-                       ,
-                    tmp2.try_count,
-                    u.passed
-                </when>
-            </choose>
+        tmp1.*
+        <choose>
+            <when test="user!=null and user.id!=null and user.id!=''">
+                ,
+                tmp2.try_count,
+                u.passed
+            </when>
+        </choose>
         FROM
         (
-            SELECT
-                    *,
-                    <include refid="stateCondition" />
-            FROM exam
-            WHERE
-                open_type = 1
-            <choose>
-                <when test="user!=null and user.orgCode!=null and user.orgCode!=''">
+        SELECT
+        *,
+        <include refid="stateCondition"/>
+        FROM exam
+        WHERE
+        open_type = 1
+        <choose>
+            <when test="user!=null and user.orgCode!=null and user.orgCode!=''">
                 UNION
-                    SELECT
-                            *,
-                            <include refid="stateCondition" />
-                    FROM exam
-                    WHERE
-                        open_type = 2
-                        AND examiner like concat('%', #{user.orgCode}, '%')
-                </when>
-                <when test="user!=null and user.username!=null and user.username!=''">
+                SELECT
+                *,
+                <include refid="stateCondition"/>
+                FROM exam
+                WHERE
+                open_type = 2
+                AND examiner like concat('%', #{user.orgCode}, '%')
+            </when>
+            <when test="user!=null and user.username!=null and user.username!=''">
                 UNION
-                    SELECT
-                            *,
-                            <include refid="stateCondition" />
-                    FROM exam
-                    WHERE
-                        open_type = 3
-                        AND examiner like concat('%', #{user.username}, '%')
-                </when>
-            </choose>
+                SELECT
+                *,
+                <include refid="stateCondition"/>
+                FROM exam
+                WHERE
+                open_type = 3
+                AND examiner like concat('%', #{user.username}, '%')
+            </when>
+        </choose>
         ) tmp1
         <choose>
             <when test="user!=null and user.id!=null and user.id!=''">
                 LEFT JOIN (
-                    SELECT
-                        exam_id,
-                        COUNT(*) AS try_count
-                    FROM
-                        user_exam uer
-                    WHERE
-                        uer.user_id = #{user.id}
-                    GROUP BY
-                        exam_id
+                SELECT
+                exam_id,
+                COUNT(*) AS try_count
+                FROM
+                user_exam uer
+                WHERE
+                uer.user_id = #{user.id}
+                GROUP BY
+                exam_id
                 ) tmp2
                 ON tmp1.ID = tmp2.exam_id
                 LEFT JOIN user_exam_result u on u.exam_id = tmp2.exam_id and u.user_id = #{user.id}
@@ -150,43 +150,40 @@
     </select>
 
     <select id="listExamIng" resultType="String">
-        SELECT
-            id
-        FROM
-            exam
-        WHERE
-            start_time &lt; now()
-          AND now() &lt; DATE_ADD(end_time , INTERVAL 6 HOUR)
+        SELECT id
+        FROM exam
+        WHERE start_time &lt; now()
+          AND now() &lt; DATE_ADD(end_time, INTERVAL 6 HOUR)
     </select>
 
     <select id="listLatestExam" resultType="com.ynfy.buss.exam.exam.entity.Exam">
         SELECT
-            e.*,
-            IFNULL( update_time, create_time ) AS time_sort,
-            <include refid="stateCondition" />
+        e.*,
+        IFNULL( update_time, create_time ) AS time_sort,
+        <include refid="stateCondition"/>
         <choose>
             <when test="userId !=null and userId != ''">
-               ,tmp.try_count, tmp.passed
+                ,tmp.try_count, tmp.passed
             </when>
         </choose>
         FROM
-            exam e
+        exam e
         <choose>
-           <when test="userId !=null and userId != ''">
-              LEFT JOIN (
-                   SELECT
-                       exam_id,
-                       try_count,
-                       passed
-                   FROM
-                    user_exam_result uer
-                   WHERE
-                    uer.user_id = #{userId}
-              ) tmp ON e.id = tmp.exam_id
-           </when>
+            <when test="userId !=null and userId != ''">
+                LEFT JOIN (
+                SELECT
+                exam_id,
+                try_count,
+                passed
+                FROM
+                user_exam_result uer
+                WHERE
+                uer.user_id = #{userId}
+                ) tmp ON e.id = tmp.exam_id
+            </when>
         </choose>
         ORDER BY
-            time_sort DESC
+        time_sort DESC
         <if test="limit !=null">
             LIMIT #{limit}
         </if>
@@ -197,6 +194,7 @@
         WHEN NOW() &gt; end_TIME THEN 3
         WHEN NOW() &gt; START_TIME AND NOW() &lt; end_TIME THEN 0
         ELSE NULL
-        END AS state
+        END
+        AS state
     </sql>
 </mapper>

+ 254 - 91
web/src/main/java/com/ynfy/buss/exam/exam/service/impl/ExamServiceImpl.java

@@ -67,30 +67,22 @@ import static java.util.stream.Collectors.toList;
 @Service
 public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IExamService {
 
+    @Autowired
+    public RedisUtil redisUtil;
     @Autowired
     private ExamMapper examMapper;
-
     @Autowired
     private IPaperService paperService;
-
     @Autowired
     private IUserExamService userExamService;
-
     @Autowired
     private IPaperQuestionService paperQuestionService;
-
     @Autowired
     private IUserExamQuestionService userExamQuestionService;
-
     @Autowired
     private IQuestionService questionService;
-
     @Autowired
     private IUserExamResultService userExamResultService;
-
-    @Autowired
-    public RedisUtil redisUtil;
-
     @Autowired
     private IQuartzJobService quartzJobService;
 
@@ -210,9 +202,9 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
 
         //保存考试关联的试题列表
         if (!CollectionUtils.isEmpty(paperQuestionList)) {
-            this.savePaperQuestion(userExam.getId(), paperQuestionList);
+            this.savePaperQuestion(paper.getId(), userExam.getId(), paperQuestionList);
             //保存用户考试的题目答案
-            this.saveQuestionAnswer(userExam.getId(), exam, paperQuestionList);
+            this.saveQuestionAnswer(paper.getId(), userExam.getId(), exam, paperQuestionList);
         }
 
         // 截止时间
@@ -235,42 +227,53 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
      * @param userExamId
      * @param paperQuestionList
      */
-    public void saveQuestionAnswer(String userExamId, Exam exam, List<PaperQuestion> paperQuestionList) {
-        //获取题目和答案
-        List<Question> questionList = listQuestion(paperQuestionList);
+    public void saveQuestionAnswer(String paperId, String userExamId, Exam exam, List<PaperQuestion> paperQuestionList) {
+        //组装试卷试题,子题目,答案
+        List<Question> questionList = paperService.assemblePaperQuestion(paperId, paperQuestionList, null, null);
         if (!CollectionUtils.isEmpty(questionList)) {
             List<UserExamQuestionAnswer> userExamQuestionAnswerList = new ArrayList<>();
-            questionList.stream().forEach(question -> {
-                List<QuestionAnswer> answerList = question.getAnswerList();
-                if (!CollectionUtils.isEmpty(answerList)) {
-                    //答案乱序(支持单选题,多选题)
-                    if (!Objects.isNull(exam.getAnswerDisorder()) && exam.getAnswerDisorder() && (QuestionType.RADIO.getCode().equals(question.getType()) || QuestionType.MULTI.getCode().equals(question.getType()))) {
-                        //答案洗牌
-                        Collections.shuffle(answerList);
-                    }
-                    int sort = 1;
-                    for (QuestionAnswer questionAnswer : answerList) {
-                        UserExamQuestionAnswer userExamQuestionAnswer = new UserExamQuestionAnswer();
-                        userExamQuestionAnswer.setUserExamId(userExamId);
-                        userExamQuestionAnswer.setQuestionId(question.getId());
-                        userExamQuestionAnswer.setAnswerId(questionAnswer.getId());
-                        userExamQuestionAnswer.setIsRight(questionAnswer.getIsRight());
-                        userExamQuestionAnswer.setSort(sort);
-                        //填空题不再生成tag
-                        if (QuestionType.BLANK.getCode().equals(question.getType())) {
-                            userExamQuestionAnswer.setTag(questionAnswer.getTag());
-                        } else {
-                            userExamQuestionAnswer.setTag(CharUtil.getZm(sort - 1));
-                        }
-                        userExamQuestionAnswerList.add(userExamQuestionAnswer);
-                        sort++;
+            questionList.forEach(question -> {
+                if (QuestionType.COMBINATION.getCode().equals(question.getType())) {//组合题
+                    List<Question> subQuestionList = question.getSubQuestionList();
+                    if (!CollectionUtils.isEmpty(subQuestionList)) {
+                        subQuestionList.forEach(subQuestion -> genQuestionAnswer(userExamId, exam, subQuestion, userExamQuestionAnswerList));
                     }
+                } else {
+                    genQuestionAnswer(userExamId, exam, question, userExamQuestionAnswerList);
                 }
             });
             userExamQuestionAnswerService.saveBatch(userExamQuestionAnswerList);
         }
     }
 
+    public void genQuestionAnswer(String userExamId, Exam exam, Question question, List<UserExamQuestionAnswer> userExamQuestionAnswerList) {
+        List<QuestionAnswer> answerList = question.getAnswerList();
+        if (!CollectionUtils.isEmpty(answerList)) {
+            //答案乱序(支持单选题,多选题)
+            if (!Objects.isNull(exam.getAnswerDisorder()) && exam.getAnswerDisorder() && (QuestionType.RADIO.getCode().equals(question.getType()) || QuestionType.MULTI.getCode().equals(question.getType()))) {
+                //答案洗牌
+                Collections.shuffle(answerList);
+            }
+            int sort = 1;
+            for (QuestionAnswer questionAnswer : answerList) {
+                UserExamQuestionAnswer userExamQuestionAnswer = new UserExamQuestionAnswer();
+                userExamQuestionAnswer.setUserExamId(userExamId);
+                userExamQuestionAnswer.setQuestionId(question.getId());
+                userExamQuestionAnswer.setAnswerId(questionAnswer.getId());
+                userExamQuestionAnswer.setIsRight(questionAnswer.getIsRight());
+                userExamQuestionAnswer.setSort(sort);
+                //填空题不再生成tag
+                if (QuestionType.BLANK.getCode().equals(question.getType())) {
+                    userExamQuestionAnswer.setTag(questionAnswer.getTag());
+                } else {
+                    userExamQuestionAnswer.setTag(CharUtil.getZm(sort - 1));
+                }
+                userExamQuestionAnswerList.add(userExamQuestionAnswer);
+                sort++;
+            }
+        }
+    }
+
 
     /**
      * 创建定时任务
@@ -306,35 +309,97 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
     /**
      * 保存试卷试题列表
      */
-    private void savePaperQuestion(String userExamId, List<PaperQuestion> paperQuestionList) {
-        paperQuestionList = paperQuestionList.stream().sorted(Comparator.comparing(PaperQuestion::getQuestionType).thenComparing(PaperQuestion::getSort)).collect(Collectors.toList());
+    private void savePaperQuestion(String paperId, String userExamId, List<PaperQuestion> paperQuestionList) {
+        paperQuestionList = paperQuestionList.stream().sorted(Comparator.comparing(PaperQuestion::getQuestionType)
+                .thenComparing(PaperQuestion::getSort)).collect(Collectors.toList());
         //获取规则组map
         Map<String, PaperRuleGroup> groupMap = getGroupMap(paperQuestionList);
 
         List<UserExamQuestion> userExamQuestionList = new ArrayList<>();
         int index = 1;
         for (PaperQuestion item : paperQuestionList) {
-            UserExamQuestion userExamQuestion = new UserExamQuestion();
-            BeanUtils.copyProperties(item, userExamQuestion);
-
-            if (!Objects.isNull(groupMap)) {
-                PaperRuleGroup ruleGroup = groupMap.get(item.getGroupId());
-                if (!Objects.isNull(ruleGroup)) {
-                    //设置题目是否允许漏选
-                    userExamQuestion.setCanMissOption(ruleGroup.getCanMissOption());
-                    //设置题目是否按空给分
-                    userExamQuestion.setCanBlankOption(ruleGroup.getCanBlankOption());
-                }
-            }
-            userExamQuestion.setId(null);
-            userExamQuestion.setQuestionIndex(index);
-            userExamQuestion.setUserExamId(userExamId);
-            userExamQuestionList.add(userExamQuestion);
+            //配置用户考试试题
+            configUserExamQuestion(userExamId, item, index, groupMap, userExamQuestionList);
             index++;
         }
+
+        //组合题
+        List<String> combinationIdList = paperQuestionList.stream().filter(r -> QuestionType.COMBINATION.getCode().equals(r.getQuestionType()))
+                .map(PaperQuestion::getQuestionId).collect(Collectors.toList());
+        List<PaperQuestion> paperSubQuestionList = paperQuestionService.listPaperSubQuestionByIds(paperId, combinationIdList);
+        //配置子题目
+        configUserExamSubQuestion(userExamId, paperSubQuestionList, groupMap, userExamQuestionList);
+
         userExamQuestionService.saveBatch(userExamQuestionList);
     }
 
+    /**
+     * 配置用户考试试题
+     *
+     * @param userExamId
+     * @param item
+     * @param index
+     * @param groupMap
+     * @param userExamQuestionList
+     */
+    public void configUserExamQuestion(String userExamId, PaperQuestion item, int index,
+                                       Map<String, PaperRuleGroup> groupMap, List<UserExamQuestion> userExamQuestionList) {
+        UserExamQuestion userExamQuestion = new UserExamQuestion();
+        BeanUtils.copyProperties(item, userExamQuestion);
+
+        //根据规则组设置题目
+        setQuestionByGroup(item, userExamQuestion, groupMap);
+
+        userExamQuestion.setId(null);
+        userExamQuestion.setQuestionIndex(index);
+        userExamQuestion.setUserExamId(userExamId);
+        userExamQuestionList.add(userExamQuestion);
+    }
+
+    /**
+     * 根据规则组设置题目
+     *
+     * @param item
+     * @param userExamQuestion
+     * @param groupMap
+     */
+    public void setQuestionByGroup(PaperQuestion item, UserExamQuestion userExamQuestion, Map<String, PaperRuleGroup> groupMap) {
+        if (!Objects.isNull(groupMap)) {
+            PaperRuleGroup ruleGroup = groupMap.get(item.getGroupId());
+            if (!Objects.isNull(ruleGroup)) {
+                //设置题目是否允许漏选
+                userExamQuestion.setCanMissOption(ruleGroup.getCanMissOption());
+                //设置题目是否按空给分
+                userExamQuestion.setCanBlankOption(ruleGroup.getCanBlankOption());
+            }
+        }
+    }
+
+    /**
+     * 配置用户考试试题子题目
+     *
+     * @param userExamId
+     * @param groupMap
+     * @param userExamQuestionList
+     */
+    public void configUserExamSubQuestion(String userExamId, List<PaperQuestion> paperSubQuestionList,
+                                          Map<String, PaperRuleGroup> groupMap, List<UserExamQuestion> userExamQuestionList) {
+        if (!CollectionUtils.isEmpty(paperSubQuestionList)) {
+            paperSubQuestionList.forEach(subQuestion -> {
+                UserExamQuestion userExamQuestion = new UserExamQuestion();
+                BeanUtils.copyProperties(subQuestion, userExamQuestion);
+                //根据规则组设置题目
+                setQuestionByGroup(subQuestion, userExamQuestion, groupMap);
+                userExamQuestion.setId(null);
+                userExamQuestion.setChild(true);
+                userExamQuestion.setParentQuestionId(subQuestion.getParentQuestionId());
+                userExamQuestion.setQuestionIndex(subQuestion.getSort());
+                userExamQuestion.setUserExamId(userExamId);
+                userExamQuestionList.add(userExamQuestion);
+            });
+        }
+    }
+
 
     /**
      * 在线考试试卷详情,包括答题卡,试题(不包含正确答案和解析)
@@ -357,14 +422,21 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
         List<UserExamQuestion> userExamQuestionList = dto.getUserExamQuestionList();
         List<Map<String, AnswerCardDTO>> answerCardList = new ArrayList<>();
         if (!CollectionUtils.isEmpty(userExamQuestionList)) {
-            //获取题目和答案
-            Map<String, Question> questionMap = getUserExamQuestionMap(userExamId, userExamQuestionList, needAnswerFlag, needAnalysis, false, true, needClearBlankContent);
-
-            //设置题目
-            setUserExamQuestion(questionMap, userExamQuestionList, needRenderBlank);
+            List<UserExamQuestion> rootQuestionList = userExamQuestionList.stream().filter(r -> !Objects.isNull(r.getChild()) && !r.getChild())
+                    .sorted(Comparator.comparing(UserExamQuestion::getQuestionIndex)).collect(Collectors.toList());
+            List<UserExamQuestion> childQuestionList = userExamQuestionList.stream().filter(r -> !Objects.isNull(r.getChild()) && r.getChild())
+                    .sorted(Comparator.comparing(UserExamQuestion::getQuestionIndex)).collect(Collectors.toList());
+            if (!CollectionUtils.isEmpty(rootQuestionList)) {
+                //获取题目和答案
+                getUserExamQuestionMap(userExamId, rootQuestionList, childQuestionList, needAnswerFlag, needAnalysis, false, true, needClearBlankContent);
+
+                //设置题目
+                setUserExamQuestion(rootQuestionList, needRenderBlank);
+
+                //设置答题卡
+                setAnswerCard(rootQuestionList, answerCardList);
+            }
 
-            //设置答题卡
-            setAnswerCard(userExamQuestionList, answerCardList);
         }
         dto.setAnswerCardList(answerCardList);
         return dto;
@@ -427,21 +499,36 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
     /**
      * 设置题目
      *
-     * @param questionMap
      * @param userExamQuestionList
      */
-    public void setUserExamQuestion(Map<String, Question> questionMap, List<UserExamQuestion> userExamQuestionList, boolean needRenderBlank) {
+    public void setUserExamQuestion(List<UserExamQuestion> userExamQuestionList, boolean needRenderBlank) {
         //与问题关联
         for (UserExamQuestion userExamQuestion : userExamQuestionList) {
-            if (!Objects.isNull(questionMap) && !Objects.isNull(questionMap.get(userExamQuestion.getQuestionId()))) {
-                Question question = questionMap.get(userExamQuestion.getQuestionId());
-                //渲染填空题的题干,填充进答案
-                if (needRenderBlank && QuestionType.BLANK.getCode().equals(userExamQuestion.getQuestionType())) {
-                    renderBlank(question, userExamQuestion.getAnswer());
-                }
-                userExamQuestion.setQuestion(question);
-                userExamQuestion.setQuestionTypeName(QuestionType.getByCode(userExamQuestion.getQuestionType()).getValue());
+            Question question = userExamQuestion.getQuestion();
+            //渲染填空题的题干,填充进答案
+            if (QuestionType.COMBINATION.getCode().equals(question.getType())) {//组合题
+                userExamQuestion.getSubQuestionList().forEach(subQuestion -> {
+                    rendBlank(needRenderBlank, userExamQuestion, subQuestion.getQuestion());
+                    subQuestion.setQuestionTypeName(QuestionType.getByCode(subQuestion.getQuestionType()).getValue());
+                });
+            } else {
+                rendBlank(needRenderBlank, userExamQuestion, question);
             }
+            userExamQuestion.setQuestion(question);
+            userExamQuestion.setQuestionTypeName(QuestionType.getByCode(userExamQuestion.getQuestionType()).getValue());
+        }
+    }
+
+    /**
+     * 渲染填空题
+     *
+     * @param needRenderBlank
+     * @param userExamQuestion
+     * @param question
+     */
+    public void rendBlank(boolean needRenderBlank, UserExamQuestion userExamQuestion, Question question) {
+        if (needRenderBlank && QuestionType.BLANK.getCode().equals(userExamQuestion.getQuestionType())) {
+            renderBlank(question, userExamQuestion.getAnswer());
         }
     }
 
@@ -520,10 +607,10 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
             Map<Integer, UserExamQuestion> userExamQuestionMap = userExamQuestionList.stream().collect(Collectors.toMap(UserExamQuestion::getQuestionIndex, a -> a, (k1, k2) -> k1));
 
             //获取题目和答案
-            Map<String, Question> questionMap = getUserExamQuestionMap(dto.getUserExamId(), userExamQuestionList, true, false, true, false, false);
+            getUserExamQuestionMap(dto.getUserExamId(), userExamQuestionList, null, true, false, true, false, false);
 
             //计算客观题得分
-            int score = calcObjectiveScore(examAnswers, userExamQuestionMap, questionMap, userExamQuestionList);
+            int score = calcObjectiveScore(examAnswers, userExamQuestionMap, null, userExamQuestionList);
             //更新用户考试信息和成绩
             updateUserExamAndResult(score, userExam);
         }
@@ -552,28 +639,104 @@ public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IE
     }
 
     //获取题目和答案
-    public Map<String, Question> getUserExamQuestionMap(String userExamId, List<UserExamQuestion> userExamQuestionList, boolean needAnswerFlag, boolean needAnalysis, boolean needPathScore, boolean needSubjective, boolean needClearBlankContent) {
-        List<String> subjectiveList = null;
+    public void getUserExamQuestionMap(String userExamId, List<UserExamQuestion> rootExamQuestionList,
+                                       List<UserExamQuestion> childQuestionList, boolean needAnswerFlag,
+                                       boolean needAnalysis, boolean needPathScore, boolean needSubjective,
+                                       boolean needClearBlankContent) {
+        List<String> subjectiveList = new ArrayList<>();
         if (needSubjective) {//是否需要主观题
-            subjectiveList = userExamQuestionList.stream().filter(o -> o.getQuestionType().equals(QuestionType.SIMPLE.getCode())).map(UserExamQuestion::getQuestionId).collect(Collectors.toList());
+            List<String> rootSubjectList = getSubjectList(rootExamQuestionList);
+            List<String> childSubjectList = getSubjectList(childQuestionList);
+            if (!CollectionUtils.isEmpty(rootSubjectList)) {
+                subjectiveList.addAll(rootSubjectList);
+            }
+            if (!CollectionUtils.isEmpty(childSubjectList)) {
+                subjectiveList.addAll(childSubjectList);
+            }
         }
 
         //客观题
-        List<String> objectiveList = userExamQuestionList.stream().filter(o -> !o.getQuestionType().equals(QuestionType.SIMPLE.getCode())).map(UserExamQuestion::getQuestionId).collect(Collectors.toList());
-        List<Question> questionList = questionService.listUserExamQuestionAnswer(userExamId, objectiveList, subjectiveList, needAnswerFlag, needAnalysis, needPathScore, needSubjective);
-        //题目答案重新排序
+        List<String> objectiveList = new ArrayList<>();
+        List<String> rootObjectiveList = getObjectiveList(rootExamQuestionList);
+        List<String> childObjectiveList = getObjectiveList(childQuestionList);
+        if (!CollectionUtils.isEmpty(rootObjectiveList)) {
+            objectiveList.addAll(rootObjectiveList);
+        }
+        if (!CollectionUtils.isEmpty(childObjectiveList)) {
+            objectiveList.addAll(childObjectiveList);
+        }
+        List<Question> resultList = new ArrayList<>();
+        List<Question> questionList = questionService.listUserExamQuestionAnswer(userExamId, objectiveList,
+                subjectiveList, needAnswerFlag, needAnalysis, needPathScore, needSubjective);
+
+        List<String> combinationIdList = rootExamQuestionList.stream().filter(o -> o.getQuestionType()
+                .equals(QuestionType.COMBINATION.getCode())).map(UserExamQuestion::getQuestionId).collect(toList());
+        List<Question> combinationQuestionList = questionService.listByIds(combinationIdList);
         if (!CollectionUtils.isEmpty(questionList)) {
-            questionList.stream().forEach(question -> {
-                List<QuestionAnswer> answerList = question.getAnswerList();
-                if (!CollectionUtils.isEmpty(answerList)) {
-                    if (QuestionType.BLANK.getCode().equals(question.getType()) && needClearBlankContent) {//填空题是否清空答案内容
-                        answerList.stream().forEach(o -> o.setContent(null));
-                    }
-                    question.setAnswerList(answerList.stream().sorted(Comparator.comparing(QuestionAnswer::getSort)).collect(Collectors.toList()));
+            resultList.addAll(questionList);
+        }
+        if (!CollectionUtils.isEmpty(combinationQuestionList)) {
+            resultList.addAll(combinationQuestionList);
+        }
+        //清空填空题答案并重新排序
+        if (!CollectionUtils.isEmpty(resultList)) {
+            resultList.forEach(question -> clearBlankAnswer(question, needClearBlankContent));
+        }
+        rootExamQuestionList.forEach(o -> {
+            Question question = resultList.stream().filter(r -> r.getId().equals(o.getQuestionId())).findFirst().orElse(null);
+            if (QuestionType.COMBINATION.getCode().equals(o.getQuestionType())) {//组合题
+                List<UserExamQuestion> subQuestionList = childQuestionList.stream().filter(c -> StringUtils.isNotBlank(c.getParentQuestionId())
+                                && c.getParentQuestionId().equals(o.getQuestionId())).sorted(Comparator.comparing(UserExamQuestion::getSort))
+                        .collect(Collectors.toList());
+                if (!Objects.isNull(question) && !CollectionUtils.isEmpty(subQuestionList)) {
+                    subQuestionList.forEach(subQuestion -> {
+                        Question tmp = resultList.stream().filter(r -> r.getId().equals(subQuestion.getQuestionId())).findFirst().orElse(null);
+                        if (!Objects.isNull(tmp)) {
+                            subQuestion.setQuestion(tmp);
+                        }
+                    });
+                    o.setSubQuestionList(subQuestionList);
                 }
-            });
+                o.setQuestion(question);
+            } else {
+                if (!Objects.isNull(question)) {
+                    o.setQuestion(question);
+                }
+            }
+        });
+    }
+
+    /**
+     * 获取主观题
+     *
+     * @param userExamQuestionList
+     * @return
+     */
+    public List<String> getSubjectList(List<UserExamQuestion> userExamQuestionList) {
+        return userExamQuestionList.stream().filter(o -> o.getQuestionType().equals(QuestionType.SIMPLE.getCode()))
+                .map(UserExamQuestion::getQuestionId).collect(Collectors.toList());
+    }
+
+    public List<String> getObjectiveList(List<UserExamQuestion> userExamQuestionList) {
+        return userExamQuestionList.stream().filter(o -> !o.getQuestionType().equals(QuestionType.SIMPLE.getCode())
+                        && !o.getQuestionType().equals(QuestionType.COMBINATION.getCode())).map(UserExamQuestion::getQuestionId)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 清空填空题答案
+     *
+     * @param question
+     * @param needClearBlankContent
+     */
+    public void clearBlankAnswer(Question question, boolean needClearBlankContent) {
+        List<QuestionAnswer> answerList = question.getAnswerList();
+        if (!CollectionUtils.isEmpty(answerList)) {
+            if (QuestionType.BLANK.getCode().equals(question.getType()) && needClearBlankContent) {//填空题是否清空答案内容
+                answerList.forEach(o -> o.setContent(null));
+            }
+            question.setAnswerList(answerList.stream().sorted(Comparator.comparing(QuestionAnswer::getSort)).collect(Collectors.toList()));
         }
-        return questionList.stream().collect(Collectors.toMap(Question::getId, a -> a, (k1, k2) -> k1));
     }
 
     //获取题目和答案

+ 37 - 51
web/src/main/java/com/ynfy/buss/exam/examreview/mapper/xml/ExamReviewMapper.xml

@@ -1,25 +1,25 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ynfy.buss.exam.examreview.mapper.ExamReviewMapper">
-    <select id="selectPageList"  resultType="com.ynfy.buss.exam.examreview.entity.ExamReview">
+    <select id="selectPageList" resultType="com.ynfy.buss.exam.examreview.entity.ExamReview">
         SELECT
-                er.*
+        er.*
         FROM
         (
 
-            SELECT
-                    *
-            FROM exam
-            WHERE
-                    reviewer_type = 1
-                    AND reviewer like concat('%', #{user.orgCode}, '%')
-            UNION
-            SELECT
-                    *
-            FROM exam
-            WHERE
-                    reviewer_type = 2
-                    AND reviewer like concat('%', #{user.username}, '%')
+        SELECT
+        *
+        FROM exam
+        WHERE
+        reviewer_type = 1
+        AND reviewer like concat('%', #{user.orgCode}, '%')
+        UNION
+        SELECT
+        *
+        FROM exam
+        WHERE
+        reviewer_type = 2
+        AND reviewer like concat('%', #{user.username}, '%')
         ) tmp
         INNER JOIN exam_review er on tmp.id=er.id
         <where>
@@ -30,41 +30,27 @@
     </select>
 
     <select id="calcReview" resultType="com.ynfy.buss.exam.examreview.entity.ExamReview">
-        SELECT
-            em.ID as id,
-            em.title,
-            em.open_type,
-            em.start_time,
-            em.end_time,
-            tmp1.exam_user_count,
-            tmp1.exam_review_times
-        FROM
-            (
-                SELECT
-                    tmp.ID,
-                    COUNT ( * ) AS exam_user_count,
-                    SUM ( exam_review_times ) AS exam_review_times
-                FROM
-                    (
-                        SELECT
-                            e.ID,
-                            ue.user_id,
-                            COUNT ( * ) AS exam_review_times
-                        FROM
-                            exam e
-                                LEFT JOIN user_exam ue ON ue.exam_id = e.ID
-                        WHERE
-                            ue.STATE = 1
-                        GROUP BY
-                            e.ID,
-                            ue.user_id
-                        ORDER BY
-                            e.ID,
-                            ue.user_id
-                    ) tmp
-                GROUP BY
-                    tmp.ID
-            ) tmp1
-                LEFT JOIN exam em ON tmp1.ID = em.ID
+        SELECT em.ID as id,
+               em.title,
+               em.open_type,
+               em.start_time,
+               em.end_time,
+               tmp1.exam_user_count,
+               tmp1.exam_review_times
+        FROM (SELECT tmp.ID,
+                     COUNT(*)               AS exam_user_count,
+                     SUM(exam_review_times) AS exam_review_times
+              FROM (SELECT e.ID,
+                           ue.user_id,
+                           COUNT(*) AS exam_review_times
+                    FROM exam e
+                             LEFT JOIN user_exam ue ON ue.exam_id = e.ID
+                    WHERE ue.STATE = 1
+                    GROUP BY e.ID,
+                             ue.user_id
+                    ORDER BY e.ID,
+                             ue.user_id) tmp
+              GROUP BY tmp.ID) tmp1
+                 LEFT JOIN exam em ON tmp1.ID = em.ID
     </select>
 </mapper>

+ 40 - 54
web/src/main/java/com/ynfy/buss/exam/examstatistics/mapper/xml/ExamStatisticsMapper.xml

@@ -20,60 +20,46 @@
     </resultMap>
 
     <select id="calcExamDimension" resultMap="examDimensionMap">
-        SELECT
-            uea.id,
-            uea.user_id,
-            u.username,
-            u.realname,
-            uea.exam_id,
-            e.title,
-            e.open_type,
-            uea.total_score,
-            uea.qualify_score,
-            uea.user_time,
-            uea.user_score,
-            uea.commit_time,
-            uea.create_time,
-            uea.passed,
-            uer.try_count
-        FROM
-            user_exam uea
-                INNER JOIN (
-                SELECT
-                    tmp.user_id,
-                    tmp.exam_id,
-                    tmp.user_score,
-                    MAX ( update_time ) AS update_time
-                FROM
-                    user_exam ue
-                        INNER JOIN (
-                        SELECT
-                            user_id,
-                            exam_id,
-                            MAX ( user_score ) AS user_score
-                        FROM
-                            user_exam
-                        WHERE
-                            exam_id = #{examId}
-                        GROUP BY
-                            user_id,
-                            exam_id
-                    ) tmp ON tmp.user_id = ue.user_id
-                        AND tmp.exam_id = ue.exam_id
-                        AND tmp.user_score = ue.user_score
-                GROUP BY
-                    tmp.user_id,
-                    tmp.exam_id,
-                    tmp.user_score
-            ) tmp1 ON tmp1.user_id = uea.user_id
-                AND tmp1.exam_id = uea.exam_id
-                AND tmp1.user_score = uea.user_score
-                AND tmp1.update_time = uea.update_time
-                LEFT JOIN sys_user u ON u.ID = uea.user_id
-                LEFT JOIN exam e ON e.ID = uea.exam_id
-        LEFT JOIN user_exam_result uer on uer.user_id=uea.user_id and uer.exam_id=uea.exam_id
-        WHERE
-            uea.STATE = 2
+        SELECT uea.id,
+               uea.user_id,
+               u.username,
+               u.realname,
+               uea.exam_id,
+               e.title,
+               e.open_type,
+               uea.total_score,
+               uea.qualify_score,
+               uea.user_time,
+               uea.user_score,
+               uea.commit_time,
+               uea.create_time,
+               uea.passed,
+               uer.try_count
+        FROM user_exam uea
+                 INNER JOIN (SELECT tmp.user_id,
+                                    tmp.exam_id,
+                                    tmp.user_score,
+                                    MAX(update_time) AS update_time
+                             FROM user_exam ue
+                                      INNER JOIN (SELECT user_id,
+                                                         exam_id,
+                                                         MAX(user_score) AS user_score
+                                                  FROM user_exam
+                                                  WHERE exam_id = #{examId}
+                                                  GROUP BY user_id,
+                                                           exam_id) tmp ON tmp.user_id = ue.user_id
+                                 AND tmp.exam_id = ue.exam_id
+                                 AND tmp.user_score = ue.user_score
+                             GROUP BY tmp.user_id,
+                                      tmp.exam_id,
+                                      tmp.user_score) tmp1 ON tmp1.user_id = uea.user_id
+            AND tmp1.exam_id = uea.exam_id
+            AND tmp1.user_score = uea.user_score
+            AND tmp1.update_time = uea.update_time
+                 LEFT JOIN sys_user u ON u.ID = uea.user_id
+                 LEFT JOIN exam e ON e.ID = uea.exam_id
+                 LEFT JOIN user_exam_result uer on uer.user_id = uea.user_id and uer.exam_id = uea.exam_id
+        WHERE uea.STATE = 2
         ORDER BY uea.user_score desc
     </select>
 

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/examuserstatistics/mapper/ExamUserStatisticsMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.examuserstatistics.entity.ExamUserStatistics;
 /**
  * @Description: 考试用户统计
  * @Author: jeecg-boot
- * @Date:   2023-03-20
+ * @Date: 2023-03-20
  * @Version: V1.0
  */
 public interface ExamUserStatisticsMapper extends BaseMapper<ExamUserStatistics> {

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/examuserstatistics/service/IExamUserStatisticsService.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.examuserstatistics.entity.ExamUserStatistics;
 /**
  * @Description: 考试用户统计
  * @Author: jeecg-boot
- * @Date:   2023-03-20
+ * @Date: 2023-03-20
  * @Version: V1.0
  */
 public interface IExamUserStatisticsService extends IService<ExamUserStatistics> {

+ 2 - 4
web/src/main/java/com/ynfy/buss/exam/job/BreakExamJob.java

@@ -24,15 +24,13 @@ import java.util.Objects;
 @Slf4j
 public class BreakExamJob implements Job {
 
+    @Autowired
+    public RedisUtil redisUtil;
     @Autowired
     private IExamService examService;
-
     @Autowired
     private IUserExamService userExamService;
 
-    @Autowired
-    public RedisUtil redisUtil;
-
     @Override
     public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
         JobDetail detail = jobExecutionContext.getJobDetail();

+ 2 - 4
web/src/main/java/com/ynfy/buss/exam/job/MakeupSubmitExamJob.java

@@ -24,15 +24,13 @@ import java.util.Objects;
 @Slf4j
 public class MakeupSubmitExamJob implements Job {
 
+    @Autowired
+    public RedisUtil redisUtil;
     @Autowired
     private IExamService examService;
-
     @Autowired
     private IUserExamService userExamService;
 
-    @Autowired
-    public RedisUtil redisUtil;
-
     @Override
     public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
         log.info("考试交卷补偿任务开始执行:处理已经过期了,但是状态还在进行中的考试...");

+ 9 - 9
web/src/main/java/com/ynfy/buss/exam/paper/enmus/JoinType.java

@@ -17,6 +17,15 @@ public enum JoinType {
         this.value = value;
     }
 
+    public static JoinType getByCode(Integer code) {
+        for (JoinType joinType : values()) {
+            if (joinType.getCode().equals(code)) {
+                return joinType;
+            }
+        }
+        return null;
+    }
+
     public Integer getCode() {
         return code;
     }
@@ -33,13 +42,4 @@ public enum JoinType {
         this.value = value;
     }
 
-    public static JoinType getByCode(Integer code) {
-        for (JoinType joinType : values()) {
-            if (joinType.getCode().equals(code)) {
-                return joinType;
-            }
-        }
-        return null;
-    }
-
 }

+ 9 - 9
web/src/main/java/com/ynfy/buss/exam/paper/enmus/LevelType.java

@@ -18,6 +18,15 @@ public enum LevelType {
         this.value = value;
     }
 
+    public static LevelType getByCode(Integer code) {
+        for (LevelType levelType : values()) {
+            if (levelType.getCode().equals(code)) {
+                return levelType;
+            }
+        }
+        return null;
+    }
+
     public Integer getCode() {
         return code;
     }
@@ -34,13 +43,4 @@ public enum LevelType {
         this.value = value;
     }
 
-    public static LevelType getByCode(Integer code) {
-        for (LevelType levelType : values()) {
-            if (levelType.getCode().equals(code)) {
-                return levelType;
-            }
-        }
-        return null;
-    }
-
 }

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/paper/mapper/PaperMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.paper.entity.Paper;
 /**
  * @Description: paper
  * @Author: jeecg-boot
- * @Date:   2023-02-22
+ * @Date: 2023-02-22
  * @Version: V1.0
  */
 public interface PaperMapper extends BaseMapper<Paper> {

+ 15 - 0
web/src/main/java/com/ynfy/buss/exam/paper/service/IPaperService.java

@@ -4,9 +4,12 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.ynfy.buss.exam.paper.dto.PaperDTO;
 import com.ynfy.buss.exam.paper.entity.Paper;
 import com.ynfy.buss.exam.paperquestion.entity.PaperQuestion;
+import com.ynfy.buss.exam.paperquestionanswer.entity.PaperQuestionAnswer;
+import com.ynfy.buss.exam.paperrulegroup.entity.PaperRuleGroup;
 import com.ynfy.buss.exam.question.entity.Question;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @Description: paper
@@ -49,4 +52,16 @@ public interface IPaperService extends IService<Paper> {
      * @return
      */
     List<Question> extractPartQuestion(String ruleDetail, String questionIds);
+
+
+    /**
+     * 组装试卷试题,子题目,答案
+     *
+     * @param paperQuestionList
+     * @param answerMap
+     * @param g
+     * @return
+     */
+    List<Question> assemblePaperQuestion(String paperId, List<PaperQuestion> paperQuestionList,
+                                         Map<String, PaperQuestionAnswer> answerMap, PaperRuleGroup g);
 }

+ 82 - 22
web/src/main/java/com/ynfy/buss/exam/paper/service/impl/PaperServiceImpl.java

@@ -185,7 +185,7 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements
     public List<PaperRuleDetail> listRuleDetail(List<PaperRuleGroup> ruleGroups) {
         List<PaperRuleDetail> ruleDetails = new ArrayList<>();
         if (!CollectionUtils.isEmpty(ruleGroups)) {
-            ruleGroups.stream().forEach(g -> ruleDetails.addAll(g.getRuleList()));
+            ruleGroups.forEach(g -> ruleDetails.addAll(g.getRuleList()));
         }
         return ruleDetails;
     }
@@ -342,24 +342,84 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements
         for (PaperRuleGroup g : groups) {
             List<PaperQuestion> paperQuestionList = listMap.get(g.getId()).stream().sorted(Comparator
                     .comparing(PaperQuestion::getSort)).collect(Collectors.toList());
-            List<String> questionIdList = paperQuestionList.stream().map(PaperQuestion::getQuestionId).collect(Collectors.toList());
-            List<Question> questions = questionService.selectQuestionList(questionIdList, true, false);
-
-            List<Question> sortQuestions = new ArrayList<>();
-            paperQuestionList.stream().forEach(paperQuestion -> {
-                List<Question> list = questions.stream().filter(question -> question.getId().equals(paperQuestion.getQuestionId())).collect(Collectors.toList());
-                if (!CollectionUtils.isEmpty(list)) {
-                    Question question = list.get(0);
+            //组装试卷试题,子题目,答案
+            g.setQuestionList(assemblePaperQuestion(paperId, paperQuestionList, answerMap, g));
+        }
+    }
+
+
+    /**
+     * 组装试卷试题,子题目,答案
+     *
+     * @param paperQuestionList
+     * @param answerMap
+     * @param g
+     * @return
+     */
+    @Override
+    public List<Question> assemblePaperQuestion(String paperId, List<PaperQuestion> paperQuestionList,
+                                                Map<String, PaperQuestionAnswer> answerMap, PaperRuleGroup g) {
+        //非组合题
+        List<String> commonIdList = paperQuestionList.stream().filter(r -> !QuestionType.COMBINATION.getCode().equals(r.getQuestionType()))
+                .map(PaperQuestion::getQuestionId).collect(Collectors.toList());
+        //组合题
+        List<String> combinationIdList = paperQuestionList.stream().filter(r -> QuestionType.COMBINATION.getCode().equals(r.getQuestionType()))
+                .map(PaperQuestion::getQuestionId).collect(Collectors.toList());
+
+        List<Question> questionList = questionService.searchQuestionWithAnswer(commonIdList, combinationIdList);
+        //试卷中保存的子题目信息
+        List<PaperQuestion> paperSubQuestionList;
+        if (!CollectionUtils.isEmpty(combinationIdList)) {
+            paperSubQuestionList = paperQuestionService.listPaperSubQuestionByIds(paperId, combinationIdList);
+        } else {
+            paperSubQuestionList = new ArrayList<>();
+        }
+
+        List<Question> sortQuestions = new ArrayList<>();
+        paperQuestionList.forEach(paperQuestion -> {
+            Question question = questionList.stream().filter(c -> c.getId().equals(paperQuestion.getQuestionId())).findFirst().orElse(null);
+            if (QuestionType.COMBINATION.getCode().equals(paperQuestion.getQuestionType())) {//组合题
+                if (!Objects.isNull(question)) {
+                    List<Question> subQuestionList = questionList.stream().filter(c -> StringUtils.isNotBlank(c.getParentId()) && c.getParentId()
+                            .equals(paperQuestion.getQuestionId())).sorted(Comparator.comparing(Question::getSort)).collect(Collectors.toList());
+                    if (!CollectionUtils.isEmpty(subQuestionList)) {
+                        //设置子题目相关信息
+                        setSubQuestionInfo(subQuestionList, paperSubQuestionList);
+                    }
+                    question.setSubQuestionList(subQuestionList);
+                    sortQuestions.add(question);
+                }
+            } else {
+                if (!Objects.isNull(question)) {
                     question.setScore(paperQuestion.getQuestionScore());
-                    //多选题回填漏选选项分值
-                    fillMissOptionScore(g, question, answerMap);
-                    //填空题按空设置分值
-                    fillBlankOptionScore(g, question, answerMap);
+                    if (!Objects.isNull(answerMap) && !Objects.isNull(g)) {
+                        //多选题回填漏选选项分值
+                        fillMissOptionScore(g, question, answerMap);
+                        //填空题按空设置分值
+                        fillBlankOptionScore(g, question, answerMap);
+                    }
                     sortQuestions.add(question);
                 }
-            });
-            g.setQuestionList(sortQuestions);
-        }
+            }
+        });
+        return sortQuestions;
+    }
+
+    /**
+     * 设置子题目相关信息
+     *
+     * @param subQuestionList
+     * @param paperSubQuestionList
+     */
+    public void setSubQuestionInfo(List<Question> subQuestionList, List<PaperQuestion> paperSubQuestionList) {
+        //设置子题目分值
+        subQuestionList.forEach(subQuestion -> {
+            PaperQuestion paperSubQuestion = paperSubQuestionList.stream().filter(psq -> psq.getQuestionId()
+                    .equals(subQuestion.getId())).findFirst().orElse(null);
+            if (!Objects.isNull(paperSubQuestion)) {
+                subQuestion.setScore(paperSubQuestion.getQuestionScore());
+            }
+        });
     }
 
 
@@ -399,7 +459,7 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements
     public void setPathScore(Question question, Map<String, PaperQuestionAnswer> answerMap) {
         List<QuestionAnswer> answerList = question.getAnswerList();
         if (!CollectionUtils.isEmpty(answerList) && !Objects.isNull(answerMap) && answerMap.size() > 0) {
-            answerList.stream().forEach(a -> {
+            answerList.forEach(a -> {
                 PaperQuestionAnswer pqa = answerMap.get(a.getId());
                 if (!Objects.isNull(pqa)) {
                     a.setPathScore(pqa.getPathScore());
@@ -417,7 +477,7 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements
         for (PaperRuleGroup g : groups) {
             //组内规则明细排序
             List<PaperRuleDetail> details = g.getRuleList().stream().sorted(Comparator.comparing(PaperRuleDetail::getSort)).collect(Collectors.toList());
-            details.stream().forEach(detail -> {
+            details.forEach(detail -> {
                 detail.setRepositoryName(repositoryService.getById(detail.getRepositoryId()).getTitle());
                 detail.setLevel_dictText(LevelType.getByCode(detail.getLevel()).getValue());
             });
@@ -456,9 +516,9 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements
      */
     @Override
     public List<Question> extractPartQuestion(String ruleDetail, String questionIds) {
-        List<QuestionDTO> questionDTOS = JSONArray.parseArray(ruleDetail, QuestionDTO.class);
+        List<QuestionDTO> questionDtoList = JSONArray.parseArray(ruleDetail, QuestionDTO.class);
         List<PaperRuleDetail> ruleDetailList = new ArrayList<>();
-        questionDTOS.forEach(dto -> {
+        questionDtoList.forEach(dto -> {
             PaperRuleDetail detail = new PaperRuleDetail();
             BeanUtils.copyProperties(dto, detail);
             detail.setQuestionType(dto.getType());
@@ -480,8 +540,8 @@ public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements
             }
         }
         if (!CollectionUtils.isEmpty(questionList)) {
-            List<String> idList = questionList.stream().map(Question::getId).collect(Collectors.toList());
-            return questionService.selectQuestionList(idList, true, false);
+            //组装题目,子题目,答案
+            return questionService.assembleQuestion(questionList);
         }
         return null;
     }

+ 14 - 0
web/src/main/java/com/ynfy/buss/exam/paperquestion/entity/PaperQuestion.java

@@ -69,4 +69,18 @@ public class PaperQuestion implements Serializable {
     @Excel(name = "所属组id", width = 15)
     @ApiModelProperty(value = "所属组id")
     private String groupId;
+
+    /**
+     * 是否子题目
+     */
+    @Excel(name = "是否子题目", width = 15)
+    @ApiModelProperty(value = "是否子题目")
+    private Boolean child;
+
+    /**
+     * 父题目id
+     */
+    @Excel(name = "父题目id", width = 15)
+    @ApiModelProperty(value = "父题目id")
+    private String parentQuestionId;
 }

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/paperquestion/mapper/PaperQuestionMapper.java

@@ -8,7 +8,7 @@ import java.util.List;
 /**
  * @Description: paper_question
  * @Author: jeecg-boot
- * @Date:   2023-02-23
+ * @Date: 2023-02-23
  * @Version: V1.0
  */
 public interface PaperQuestionMapper extends BaseMapper<PaperQuestion> {

+ 6 - 9
web/src/main/java/com/ynfy/buss/exam/paperquestion/mapper/xml/PaperQuestionMapper.xml

@@ -2,14 +2,11 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ynfy.buss.exam.paperquestion.mapper.PaperQuestionMapper">
     <select id="listByPaperId" resultType="com.ynfy.buss.exam.paperquestion.entity.PaperQuestion">
-        SELECT
-            pq.*
-        FROM
-            paper_question pq
-                LEFT JOIN paper_rule_group prg ON pq.group_id = prg.ID
-        WHERE
-            pq.paper_id = #{paperId}
-        ORDER BY
-            prg.sort
+        SELECT pq.*
+        FROM paper_question pq
+                 LEFT JOIN paper_rule_group prg ON pq.group_id = prg.ID
+        WHERE pq.paper_id = #{paperId}
+          AND pq.child = 0
+        ORDER BY prg.sort
     </select>
 </mapper>

+ 2 - 0
web/src/main/java/com/ynfy/buss/exam/paperquestion/service/IPaperQuestionService.java

@@ -46,4 +46,6 @@ public interface IPaperQuestionService extends IService<PaperQuestion> {
      * @return
      */
     boolean checkIsUsed(String[] questionIds);
+
+    List<PaperQuestion> listPaperSubQuestionByIds(String paperId, List<String> combinationIdList);
 }

+ 31 - 3
web/src/main/java/com/ynfy/buss/exam/paperquestion/service/impl/PaperQuestionServiceImpl.java

@@ -71,15 +71,16 @@ public class PaperQuestionServiceImpl extends ServiceImpl<PaperQuestionMapper, P
     @Override
     public void saveAll(String paperId, List<PaperRuleGroup> groupList) {
         if (!CollectionUtils.isEmpty(groupList)) {
-            groupList.stream().forEach(g -> {
+            groupList.forEach(g -> {
                 List<PaperQuestion> paperQuestionList = new ArrayList<>();
                 List<Question> questionList = g.getQuestionList();
                 int sort = 1;
                 if (!CollectionUtils.isEmpty(questionList)) {
                     for (Question q : questionList) {
-                        //保存漏选选项分值
+                        //多选题漏选选项分值(漏选也给分)
                         if (QuestionType.MULTI.getCode().equals(q.getType()) && !Objects.isNull(g.getCanMissOption()) && g.getCanMissOption()) {
                             paperQuestionAnswerService.save(paperId, q);
+                            //填空题按空给分
                         } else if (QuestionType.BLANK.getCode().equals(q.getType()) && !Objects.isNull(g.getCanBlankOption()) && g.getCanBlankOption()) {
                             paperQuestionAnswerService.save(paperId, q);
                         }
@@ -88,7 +89,26 @@ public class PaperQuestionServiceImpl extends ServiceImpl<PaperQuestionMapper, P
                         paperQuestion.setPaperId(paperId);
                         paperQuestion.setQuestionId(q.getId());
                         paperQuestion.setQuestionType(q.getType());
-                        paperQuestion.setQuestionScore(q.getScore());
+                        paperQuestion.setChild(q.getChild());
+                        if (QuestionType.COMBINATION.getCode().equals(q.getType())) {//组合题
+                            paperQuestion.setQuestionScore((int) q.getSubQuestionList().stream().mapToDouble(Question::getScore).sum());
+
+                            //子题目
+                            q.getSubQuestionList().forEach(sub -> {
+                                PaperQuestion paperSubQuestion = new PaperQuestion();
+                                paperSubQuestion.setGroupId(g.getId());
+                                paperSubQuestion.setPaperId(paperId);
+                                paperSubQuestion.setQuestionId(sub.getId());
+                                paperSubQuestion.setQuestionType(sub.getType());
+                                paperSubQuestion.setChild(sub.getChild());
+                                paperSubQuestion.setQuestionScore(sub.getScore());
+                                paperSubQuestion.setSort(sub.getSort());
+                                paperSubQuestion.setParentQuestionId(q.getId());
+                                paperQuestionList.add(paperSubQuestion);
+                            });
+                        } else {
+                            paperQuestion.setQuestionScore(q.getScore());
+                        }
                         paperQuestion.setSort(sort);
                         paperQuestionList.add(paperQuestion);
                         sort++;
@@ -135,4 +155,12 @@ public class PaperQuestionServiceImpl extends ServiceImpl<PaperQuestionMapper, P
     public boolean checkIsUsed(String[] questionIds) {
         return checkIsUsedInPaper(questionIds) || userExamQuestionService.checkIsUsedInExam(questionIds);
     }
+
+    @Override
+    public List<PaperQuestion> listPaperSubQuestionByIds(String paperId, List<String> combinationIdList) {
+        LambdaQueryWrapper<PaperQuestion> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(PaperQuestion::getPaperId, paperId)
+                .in(PaperQuestion::getParentQuestionId, combinationIdList).eq(PaperQuestion::getChild, true);
+        return list(wrapper);
+    }
 }

+ 4 - 4
web/src/main/java/com/ynfy/buss/exam/paperquestionanswer/entity/PaperQuestionAnswer.java

@@ -1,16 +1,16 @@
 package com.ynfy.buss.exam.paperquestionanswer.entity;
 
-import java.io.Serializable;
-
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import org.jeecgframework.poi.excel.annotation.Excel;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+
+import java.io.Serializable;
 
 /**
  * @Description: 试卷试题答案

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/paperquestionanswer/mapper/PaperQuestionAnswerMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.paperquestionanswer.entity.PaperQuestionAnswer;
 /**
  * @Description: 试卷试题答案
  * @Author: jeecg-boot
- * @Date:   2023-04-05
+ * @Date: 2023-04-05
  * @Version: V1.0
  */
 public interface PaperQuestionAnswerMapper extends BaseMapper<PaperQuestionAnswer> {

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/paperruledetail/mapper/PaperRuleDetailMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.paperruledetail.entity.PaperRuleDetail;
 /**
  * @Description: paper_rule_detail
  * @Author: jeecg-boot
- * @Date:   2023-02-22
+ * @Date: 2023-02-22
  * @Version: V1.0
  */
 public interface PaperRuleDetailMapper extends BaseMapper<PaperRuleDetail> {

+ 4 - 11
web/src/main/java/com/ynfy/buss/exam/paperrulegroup/entity/PaperRuleGroup.java

@@ -30,21 +30,22 @@ import java.util.List;
 @ApiModel(value = "paper_rule_group对象", description = "paper_rule_group")
 public class PaperRuleGroup implements Serializable {
     private static final long serialVersionUID = 1L;
-
+    @TableField(exist = false)
+    public List<PaperRuleDetail> ruleList;
+    @TableField(exist = false)
+    public List<Question> questionList;
     /**
      * id
      */
     @TableId(type = IdType.ASSIGN_ID)
     @ApiModelProperty(value = "id")
     private String id;
-
     /**
      * 组名
      */
     @Excel(name = "组名", width = 15)
     @ApiModelProperty(value = "组名")
     private String title;
-
     /**
      * 试卷id
      */
@@ -82,24 +83,16 @@ public class PaperRuleGroup implements Serializable {
     @Excel(name = "排序", width = 15)
     @ApiModelProperty(value = "排序")
     private Integer sort;
-
     /**
      * 漏选也给分
      */
     @Excel(name = "漏选也给分", width = 15)
     @ApiModelProperty(value = "漏选也给分")
     private Boolean canMissOption;
-
     /**
      * 按空给分
      */
     @Excel(name = "按空给分", width = 15)
     @ApiModelProperty(value = "按空给分")
     private Boolean canBlankOption;
-
-    @TableField(exist = false)
-    public List<PaperRuleDetail> ruleList;
-
-    @TableField(exist = false)
-    public List<Question> questionList;
 }

+ 28 - 37
web/src/main/java/com/ynfy/buss/exam/paperrulegroup/mapper/xml/PaperRuleGroupMapper.xml

@@ -26,45 +26,36 @@
         </collection>
     </resultMap>
     <select id="listGroupAndDetail" resultMap="groupMap">
-        SELECT
-            prg.id,
-            prg.title,
-            prg.paper_id,
-            prg.question_type,
-            prg.per_score,
-            prg.question_count,
-            prg.total_score,
-            prg.sort,
-            prg.can_miss_option,
-            prg.can_blank_option,
-            prd.id AS prd_id,
-            prd.repository_id,
-            prd.level,
-            prd.group_id,
-            prd.paper_id AS prd_paper_id,
-            prd.num,
-            prd.sort AS prd_sort,
-            prd.question_type AS prd_question_type
-        FROM
-            paper_rule_group prg
-        LEFT JOIN paper_rule_detail prd ON prg.ID = prd.group_id
-        WHERE
-            prg.paper_id =  #{paperId}
-        ORDER BY
-            prg.sort ASC
+        SELECT prg.id,
+               prg.title,
+               prg.paper_id,
+               prg.question_type,
+               prg.per_score,
+               prg.question_count,
+               prg.total_score,
+               prg.sort,
+               prg.can_miss_option,
+               prg.can_blank_option,
+               prd.id            AS prd_id,
+               prd.repository_id,
+               prd.level,
+               prd.group_id,
+               prd.paper_id      AS prd_paper_id,
+               prd.num,
+               prd.sort          AS prd_sort,
+               prd.question_type AS prd_question_type
+        FROM paper_rule_group prg
+                 LEFT JOIN paper_rule_detail prd ON prg.ID = prd.group_id
+        WHERE prg.paper_id = #{paperId}
+        ORDER BY prg.sort ASC
     </select>
 
     <select id="sumQuestionCount" resultType="com.ynfy.buss.exam.exam.dto.QuestionTypeCountDTO">
-        SELECT
-            question_type,
-            SUM ( question_count ) as question_count
-        FROM
-            paper_rule_group
-        WHERE
-            paper_id = #{paperId}
-        GROUP BY
-            question_type
-        ORDER BY
-            question_type
+        SELECT question_type,
+               SUM(question_count) as question_count
+        FROM paper_rule_group
+        WHERE paper_id = #{paperId}
+        GROUP BY question_type
+        ORDER BY question_type
     </select>
 </mapper>

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/paperrulegroup/service/impl/PaperRuleGroupServiceImpl.java

@@ -59,7 +59,7 @@ public class PaperRuleGroupServiceImpl extends ServiceImpl<PaperRuleGroupMapper,
         paperRuleDetailService.remove(paper.getId());
         //移除试卷关联的试题
         paperQuestionService.remove(paper.getId());
-        //删除多选题漏选分值
+        //删除试卷答案
         paperQuestionAnswerService.remove(paper.getId());
         if (!CollectionUtils.isEmpty(groupList)) {
             if (paper.getJoinType().equals(JoinType.SJ.getCode())) {//随机组卷

+ 12 - 6
web/src/main/java/com/ynfy/buss/exam/question/controller/QuestionController.java

@@ -11,6 +11,7 @@ import com.ynfy.buss.exam.paperruledetail.dto.RuleDetailDTO;
 import com.ynfy.buss.exam.paperruledetail.entity.PaperRuleDetail;
 import com.ynfy.buss.exam.question.dto.QuestionDTO;
 import com.ynfy.buss.exam.question.entity.Question;
+import com.ynfy.buss.exam.question.enums.QuestionType;
 import com.ynfy.buss.exam.question.service.IQuestionService;
 import com.ynfy.buss.exam.questionanswer.entity.QuestionAnswer;
 import com.ynfy.buss.exam.questionanswer.service.IQuestionAnswerService;
@@ -35,7 +36,6 @@ import javax.servlet.http.HttpServletResponse;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
-import java.util.stream.Collectors;
 
 /**
  * @Description: question
@@ -77,6 +77,7 @@ public class QuestionController extends JeecgController<Question, IQuestionServi
                                                  @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                                  HttpServletRequest req) {
         Page<Question> page = new Page<Question>(pageNo, pageSize);
+        question.setChild(false);
         IPage<Question> pageList = questionService.selectPageList(page, question);
         return Result.OK(pageList);
     }
@@ -150,8 +151,13 @@ public class QuestionController extends JeecgController<Question, IQuestionServi
         if (question == null) {
             return Result.error("未找到对应数据");
         }
-        List<QuestionAnswer> answers = questionAnswerService.listAnswerByQuestionId(id);
-        question.setAnswerList(answers);
+        if (QuestionType.COMBINATION.getCode().equals(question.getType())) {//组合题
+            //查询组合题子题目和答案
+            question.setSubQuestionList(questionService.listSubQuestionList(question.getId()));
+        } else {
+            List<QuestionAnswer> answers = questionAnswerService.listAnswerByQuestionId(id);
+            question.setAnswerList(answers);
+        }
         return Result.OK(question);
     }
 
@@ -184,14 +190,14 @@ public class QuestionController extends JeecgController<Question, IQuestionServi
         if (StringUtils.isNotBlank(excludeQIds)) {
             queryWrapper.notIn("id", Arrays.asList(excludeQIds.split(",")));
         }
+        queryWrapper.eq("child", false);
         Page<Question> page = new Page<>(pageNo, pageSize);
         IPage<Question> pageList = questionService.page(page, queryWrapper);
         if (!Objects.isNull(pageList)) {
             List<Question> records = pageList.getRecords();
             if (!CollectionUtils.isEmpty(records)) {
-                List<String> idList = records.stream().map(Question::getId).collect(Collectors.toList());
-                List<Question> questionList = questionService.selectQuestionList(idList, true, false);
-                pageList.setRecords(questionList);
+                //组装题目,子题目,答案
+                pageList.setRecords(questionService.assembleQuestion(records));
             }
         }
         return Result.OK(pageList);

+ 6 - 2
web/src/main/java/com/ynfy/buss/exam/question/dto/QuestionConditionDTO.java

@@ -27,8 +27,12 @@ public class QuestionConditionDTO implements Serializable {
 
     @Override
     public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return true;
+        }
         QuestionConditionDTO dto = (QuestionConditionDTO) o;
         return Objects.equals(repositoryId, dto.repositoryId) && Objects.equals(questionType, dto.questionType) && Objects.equals(level, dto.level);
     }

+ 25 - 0
web/src/main/java/com/ynfy/buss/exam/question/entity/Question.java

@@ -96,6 +96,25 @@ public class Question implements Serializable {
     @ApiModelProperty(value = "按序作答")
     private Boolean answerOrder;
 
+    /**
+     * 是否子题目
+     */
+    @Excel(name = "是否子题目", width = 15)
+    @ApiModelProperty(value = "是否子题目")
+    private Boolean child;
+
+    /**
+     * 父题目id
+     */
+    @Excel(name = "父题目id", width = 15)
+    @ApiModelProperty(value = "父题目id")
+    private String parentId;
+
+    /**
+     * 排序
+     */
+    private Integer sort;
+
     /**
      * 题库名称
      */
@@ -119,6 +138,12 @@ public class Question implements Serializable {
      */
     @TableField(exist = false)
     private Integer questionNum;
+
+    /**
+     * 子题目列表
+     */
+    @TableField(exist = false)
+    private List<Question> subQuestionList;
 }
 
 

+ 11 - 10
web/src/main/java/com/ynfy/buss/exam/question/enums/QuestionType.java

@@ -13,7 +13,8 @@ public enum QuestionType {
     MULTI(2, "多选题"),
     JUDGE(3, "判断题"),
     SIMPLE(4, "简答题"),
-    BLANK(5, "填空题");
+    BLANK(5, "填空题"),
+    COMBINATION(6, "组合题");
 
     private Integer code;
 
@@ -24,6 +25,15 @@ public enum QuestionType {
         this.value = value;
     }
 
+    public static QuestionType getByCode(Integer code) {
+        for (QuestionType questionType : values()) {
+            if (questionType.getCode().equals(code)) {
+                return questionType;
+            }
+        }
+        return null;
+    }
+
     public Integer getCode() {
         return code;
     }
@@ -40,13 +50,4 @@ public enum QuestionType {
         this.value = value;
     }
 
-    public static QuestionType getByCode(Integer code) {
-        for (QuestionType questionType : values()) {
-            if (questionType.getCode().equals(code)) {
-                return questionType;
-            }
-        }
-        return null;
-    }
-
 }

+ 132 - 115
web/src/main/java/com/ynfy/buss/exam/question/mapper/xml/QuestionMapper.xml

@@ -4,8 +4,8 @@
 
     <select id="selectPageList" resultType="com.ynfy.buss.exam.question.entity.Question">
         SELECT
-               q.*,
-               p.title as repositoryName
+        q.*,
+        p.title as repositoryName
         FROM question q
         LEFT JOIN repository p on q.repository_id = p.id
         <where>
@@ -21,6 +21,9 @@
             <if test="question.level != null">
                 AND q.level = #{question.level}
             </if>
+            <if test="question.child != null">
+                AND q.child = #{question.child}
+            </if>
         </where>
         ORDER BY q.create_time DESC
     </select>
@@ -34,6 +37,9 @@
         <result column="repository_name" property="repositoryName" jdbcType="VARCHAR"/>
         <result column="analysis" property="analysis" jdbcType="VARCHAR"/>
         <result column="answer_order" property="answerOrder" jdbcType="BOOLEAN"/>
+        <result column="child" property="child" jdbcType="BOOLEAN"/>
+        <result column="parent_id" property="parentId" jdbcType="VARCHAR"/>
+        <result column="sort" property="sort" jdbcType="INTEGER"/>
 
         <collection property="answerList" ofType="com.ynfy.buss.exam.questionanswer.entity.QuestionAnswer">
             <id column="qa_id" property="id" jdbcType="VARCHAR"/>
@@ -48,27 +54,31 @@
 
     <select id="selectQuestionList" resultMap="QuestionMap">
         SELECT
-                q.id,
-                q.type,
-                q.level,
-                q.content,
-                q.repository_id,
-                r.title as repository_name,
-                qa.id as qa_id,
-                qa.content as qa_content ,
-                <choose>
-                    <when test="needAnalysis">
-                        q.analysis,
-                        qa.analysis as qa_analysis,
-                    </when>
-                </choose>
-                <choose>
-                    <when test="needAnswerFlag">
-                        qa.is_right,
-                    </when>
-                </choose>
-                qa.tag,
-                qa.sort as qa_sort
+            q.id,
+            q.type,
+            q.level,
+            q.content,
+            q.repository_id,
+            q.answer_order,
+            q.child,
+            q.parent_id,
+            q.sort,
+            r.title as repository_name,
+            qa.id as qa_id,
+            qa.content as qa_content ,
+            <choose>
+                <when test="needAnalysis">
+                    q.analysis,
+                    qa.analysis as qa_analysis,
+                </when>
+            </choose>
+            <choose>
+                <when test="needAnswerFlag">
+                    qa.is_right,
+                </when>
+            </choose>
+            qa.tag,
+            qa.sort as qa_sort
         FROM question q
         LEFT JOIN repository r on q.repository_id=r.id
         LEFT JOIN question_answer qa on q.id=qa.question_id
@@ -91,6 +101,8 @@
             q.content,
             q.repository_id,
             q.answer_order,
+            q.child,
+            q.parent_id,
             qa.id as qa_id,
             qa.content as qa_content ,
             <choose>
@@ -112,15 +124,15 @@
             </choose>
             ueqa.sort as qa_sort
         FROM question q
-            LEFT JOIN user_exam_question_answer ueqa on q.id = ueqa.question_id
-            LEFT JOIN question_answer qa on ueqa.answer_id = qa.id
-            <choose>
-                <when test="needPathScore">
-                    LEFT JOIN user_exam ue ON ueqa.user_exam_id = ue.id
-                    LEFT JOIN paper_question_answer pqa
-                              ON pqa.paper_id = ue.paper_id AND pqa.question_id = q.id AND pqa.answer_id = qa.id
-                </when>
-            </choose>
+        LEFT JOIN user_exam_question_answer ueqa on q.id = ueqa.question_id
+        LEFT JOIN question_answer qa on ueqa.answer_id = qa.id
+        <choose>
+            <when test="needPathScore">
+                LEFT JOIN user_exam ue ON ueqa.user_exam_id = ue.id
+                LEFT JOIN paper_question_answer pqa
+                ON pqa.paper_id = ue.paper_id AND pqa.question_id = q.id AND pqa.answer_id = qa.id
+            </when>
+        </choose>
         <where>
             <if test="userExamId !=null and userExamId !=''">
                 AND ueqa.user_exam_id = #{userExamId}
@@ -136,92 +148,95 @@
             <when test="needSubjective and subjectiveList !=null and subjectiveList.size()>0">
                 UNION ALL
                 SELECT
-                        q.ID,
-                        q.TYPE,
-                        q.LEVEL,
-                        q.CONTENT,
-                        q.repository_id,
-                        q.answer_order,
-                        null as qa_id,
-                        null as qa_content ,
-                        <choose>
-                            <when test="needAnalysis">
-                                null as analysis,
-                                null as qa_analysis,
-                            </when>
-                        </choose>
-                        <choose>
-                            <when test="needAnswerFlag">
-                                null as is_right,
-                            </when>
-                        </choose>
-                        null as tag,
-                        <choose>
-                            <when test="needPathScore">
-                                null as path_score,
-                            </when>
-                        </choose>
-                        null as qa_sort
+                    q.ID,
+                    q.TYPE,
+                    q.LEVEL,
+                    q.CONTENT,
+                    q.repository_id,
+                    q.answer_order,
+                    q.child,
+                    q.parent_id,
+                    null as qa_id,
+                    null as qa_content ,
+                    <choose>
+                        <when test="needAnalysis">
+                            null as analysis,
+                            null as qa_analysis,
+                        </when>
+                    </choose>
+                    <choose>
+                        <when test="needAnswerFlag">
+                            null as is_right,
+                        </when>
+                    </choose>
+                    null as tag,
+                    <choose>
+                        <when test="needPathScore">
+                            null as path_score,
+                        </when>
+                    </choose>
+                    null as qa_sort
                 FROM
-                        question q
-                <where>
-                    <if test="subjectiveList !=null and subjectiveList.size()>0">
-                        AND q.id IN
-                        <foreach collection="subjectiveList" item="item" open="(" separator="," close=")">
-                            #{item}
-                        </foreach>
-                    </if>
-                </where>
+                    question q
+                    <where>
+                        <if test="subjectiveList !=null and subjectiveList.size()>0">
+                            AND q.id IN
+                            <foreach collection="subjectiveList" item="item" open="(" separator="," close=")">
+                                #{item}
+                            </foreach>
+                        </if>
+                    </where>
             </when>
         </choose>
     </select>
 
     <select id="groupLevelById" resultType="com.ynfy.buss.exam.question.dto.QuestionDTO">
         SELECT
-                    tmp.*,
-                    r.title as repositoryName
+            tmp.*,
+            r.title as repositoryName
         FROM (
-                SELECT
-                       repository_id,
-                       type,
-                       level,
-                       COUNT ( * ) AS categoryCount
-                FROM
-                       question
-                <where>
-                        <if test="repositoryIds !=null and repositoryIds.size()>0">
-                            AND repository_id IN
-                            <foreach collection="repositoryIds" item="item" open="(" separator="," close=")">
-                                #{item}
-                            </foreach>
-                        </if>
-                        <if test="type !=null">
-                            AND type = #{type,jdbcType=INTEGER}
-                        </if>
-                        <if test="questionIds !=null and questionIds.size()>0">
-                            AND id NOT IN
-                            <foreach collection="questionIds" item="item" open="(" separator="," close=")">
-                                #{item}
-                            </foreach>
-                        </if>
-                </where>
-                GROUP BY
-                    repository_id,
-                    type,
-                    level
+            SELECT
+                repository_id,
+                type,
+                level,
+                COUNT ( * ) AS categoryCount
+            FROM
+                question
+            <where>
+                <if test="repositoryIds !=null and repositoryIds.size()>0">
+                    AND repository_id IN
+                    <foreach collection="repositoryIds" item="item" open="(" separator="," close=")">
+                        #{item}
+                    </foreach>
+                </if>
+                <if test="type !=null">
+                    AND type = #{type,jdbcType=INTEGER}
+                </if>
+                <if test="questionIds !=null and questionIds.size()>0">
+                    AND id NOT IN
+                    <foreach collection="questionIds" item="item" open="(" separator="," close=")">
+                        #{item}
+                    </foreach>
+                </if>
+                AND child = 0
+            </where>
+            GROUP BY
+                repository_id,
+                type,
+                level
         ) tmp
-       LEFT JOIN repository r on tmp.repository_id=r.id
-       order by tmp.repository_id,tmp.level
+        LEFT JOIN repository r on tmp.repository_id=r.id
+        order by tmp.repository_id,tmp.level
     </select>
 
     <select id="listByPaperId" resultType="com.ynfy.buss.exam.question.entity.Question">
         SELECT
-             q.*
+            q.*
         FROM question q
         LEFT JOIN paper_question pq on q.id=pq.question_id
         <where>
             <if test="paperId !=null and paperId!=''">
-                 pq.paper_id = #{paperId}
+                pq.paper_id = #{paperId}
             </if>
         </where>
     </select>
@@ -255,15 +270,16 @@
             LEVEL
     </select>
 
-    <select id="listQuestionByCondition" parameterType="java.util.List" resultType="com.ynfy.buss.exam.question.entity.Question">
+    <select id="listQuestionByCondition" parameterType="java.util.List"
+            resultType="com.ynfy.buss.exam.question.entity.Question">
         <foreach collection="list" item="item" index="index" separator="union">
             SELECT
-                    *
+                *
             FROM
-                    question
+                question
             <where>
                 <if test="item.repositoryId !=null and item.repositoryId!='' ">
-                    AND repository_id  = #{item.repositoryId}
+                    AND repository_id = #{item.repositoryId}
                 </if>
                 <if test="item.questionType !=null">
                     AND type = #{item.questionType,jdbcType=INTEGER}
@@ -271,6 +287,7 @@
                 <if test="item.level !=null">
                     AND level = #{item.level,jdbcType=INTEGER}
                 </if>
+                AND child = 0
             </where>
         </foreach>
     </select>
@@ -289,27 +306,27 @@
             </if>
         </where>
         GROUP BY
-                repository_id
+            repository_id
     </select>
 
     <select id="countQuestionNumByType" resultType="com.ynfy.buss.exam.question.dto.QuestionDTO">
         SELECT
-            type,
-            COUNT(*) as num
+        type,
+        COUNT(*) as num
         FROM
-            question
+        question
         <where>
             repository_id = #{repositoryId}
         </where>
         GROUP BY
-            type
+        type
     </select>
 
     <select id="countQuestionType" resultType="com.ynfy.buss.exam.question.dto.QuestionDTO">
         SELECT
-            repository_id as repositoryId,
-            type,
-            COUNT(*) as num
+        repository_id as repositoryId,
+        type,
+        COUNT(*) as num
         FROM
         question
         <where>
@@ -321,7 +338,7 @@
             </if>
         </where>
         GROUP BY
-          repository_id,
-          type
+        repository_id,
+        type
     </select>
 </mapper>

+ 42 - 0
web/src/main/java/com/ynfy/buss/exam/question/service/IQuestionService.java

@@ -141,4 +141,46 @@ public interface IQuestionService extends IService<Question> {
      * @return
      */
     List<QuestionDTO> countQuestionType(List<String> repositoryIds);
+
+
+    /**
+     * 查询组合题子题目和答案
+     *
+     * @param questionId
+     * @return
+     */
+    List<Question> listSubQuestionList(String questionId);
+
+    /**
+     * 查询组合题子题目列表和答案
+     *
+     * @param questionIdList
+     * @return
+     */
+    List<Question> listSubQuestionByIds(List<String> questionIdList);
+
+    /**
+     * 组装题目,子题目,答案
+     *
+     * @param records
+     * @return
+     */
+    List<Question> assembleQuestion(List<Question> records);
+
+    /**
+     * 查询组合题子题目列表
+     *
+     * @param questionIdList
+     * @return
+     */
+    List<Question> listSubQuestionNoAnswerByIds(List<String> questionIdList);
+
+    /**
+     * 查询题目和答案
+     *
+     * @param commonIdList
+     * @param combinationIdList
+     * @return
+     */
+    List<Question> searchQuestionWithAnswer(List<String> commonIdList, List<String> combinationIdList);
 }

+ 205 - 2
web/src/main/java/com/ynfy/buss/exam/question/service/impl/QuestionServiceImpl.java

@@ -65,12 +65,88 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> i
     public void saveQuestion(Question question) {
         // 校验数据
         this.checkData(question);
+        question.setChild(false);
         // 更新
         this.saveOrUpdate(question);
-        // 保存题目答案选项
-        questionAnswerService.saveAll(question.getId(), question.getType(), question.getAnswerList());
+        if (QuestionType.COMBINATION.getCode().equals(question.getType())) {//组合题
+            if (!CollectionUtils.isEmpty(question.getSubQuestionList())) {
+                List<Question> subQuestionList = question.getSubQuestionList();
+                //保存组合题子题目
+                saveSubQuestionList(subQuestionList, question);
+                //保存子题目答案
+                saveSubQuestionAnswerList(subQuestionList);
+            }
+        } else {
+            // 保存题目答案选项
+            questionAnswerService.saveAll(question.getId(), question.getType(), question.getAnswerList());
+        }
+    }
+
+    /**
+     * 保存组合题子题目
+     *
+     * @param subQuestionList
+     * @param question
+     */
+    public void saveSubQuestionList(List<Question> subQuestionList, Question question) {
+        //删除组合题的子题目
+        deleteSubQuestionById(question.getId());
+
+        int sort = 1;
+        for (Question subQuestion : subQuestionList) {
+            subQuestion.setId(null);
+            subQuestion.setParentId(question.getId());
+            subQuestion.setChild(true);
+            subQuestion.setRepositoryId(question.getRepositoryId());
+            subQuestion.setSort(sort);
+            sort++;
+        }
+        saveBatch(subQuestionList);
+    }
+
+    /**
+     * 删除组合题的子题目
+     *
+     * @param questionId
+     */
+    public void deleteSubQuestionById(String questionId) {
+        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(Question::getParentId, questionId).eq(Question::getChild, true);
+        remove(queryWrapper);
     }
 
+    /**
+     * 保存子题目答案
+     *
+     * @param subQuestionList
+     */
+    public void saveSubQuestionAnswerList(List<Question> subQuestionList) {
+        List<String> questionIds = subQuestionList.stream().map(Question::getId).collect(Collectors.toList());
+        //删除组合题子题目的答案
+        questionAnswerService.deleteSubQuestionAnswerById(questionIds);
+
+        List<QuestionAnswer> subAnswerList = new ArrayList<>();
+        subQuestionList.forEach(subQuestion -> {
+            List<QuestionAnswer> answerList = subQuestion.getAnswerList();
+            if (!CollectionUtils.isEmpty(answerList)) {
+                int answerSort = 1;
+                for (QuestionAnswer item : answerList) {
+                    item.setId(null);
+                    item.setQuestionId(subQuestion.getId());
+                    //填空题不生成tag
+                    if (!QuestionType.BLANK.getCode().equals(subQuestion.getType())) {
+                        item.setTag(CharUtil.getZm(answerSort - 1));
+                    }
+                    item.setSort(answerSort);
+                    answerSort++;
+                }
+                subAnswerList.addAll(answerList);
+            }
+        });
+        questionAnswerService.saveBatch(subAnswerList);
+    }
+
+
     /**
      * 校验题目信息
      *
@@ -478,6 +554,9 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> i
      */
     @Override
     public List<Question> selectQuestionList(List<String> idList, boolean needAnswerFlag, boolean needAnalysis) {
+        if (CollectionUtils.isEmpty(idList)) {
+            return Collections.emptyList();
+        }
         return questionMapper.selectQuestionList(idList, needAnswerFlag, needAnalysis);
     }
 
@@ -668,4 +747,128 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> i
     public List<QuestionDTO> countQuestionType(List<String> repositoryIds) {
         return questionMapper.countQuestionType(repositoryIds);
     }
+
+    /**
+     * 查询组合题子题目和答案
+     *
+     * @param questionId
+     * @return
+     */
+    @Override
+    public List<Question> listSubQuestionList(String questionId) {
+        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(Question::getParentId, questionId).eq(Question::getChild, true);
+        List<Question> subQuestionList = list(queryWrapper);
+        if (!CollectionUtils.isEmpty(subQuestionList)) {
+            List<String> questionIds = subQuestionList.stream().map(Question::getId).collect(Collectors.toList());
+            List<QuestionAnswer> answerList = questionAnswerService.listAnswerByQuestionIds(questionIds);
+            subQuestionList.forEach(subQuestion -> {
+                List<QuestionAnswer> subQuestionAnswerList = answerList.stream().filter(answer -> answer.getQuestionId()
+                        .equals(subQuestion.getId())).sorted(Comparator.comparing(QuestionAnswer::getSort)).collect(Collectors.toList());
+                subQuestion.setAnswerList(subQuestionAnswerList);
+            });
+        }
+        return subQuestionList;
+    }
+
+    /**
+     * 查询组合题子题目列表和答案
+     *
+     * @param questionIdList
+     * @return
+     */
+    @Override
+    public List<Question> listSubQuestionByIds(List<String> questionIdList) {
+        List<Question> subQuestionList = listSubQuestionNoAnswerByIds(questionIdList);
+        if (!CollectionUtils.isEmpty(subQuestionList)) {
+            List<String> questionIds = subQuestionList.stream().map(Question::getId).collect(Collectors.toList());
+            List<QuestionAnswer> answerList = questionAnswerService.listAnswerByQuestionIds(questionIds);
+            subQuestionList.forEach(subQuestion -> {
+                List<QuestionAnswer> subQuestionAnswerList = answerList.stream().filter(answer -> answer.getQuestionId()
+                        .equals(subQuestion.getId())).sorted(Comparator.comparing(QuestionAnswer::getSort)).collect(Collectors.toList());
+                subQuestion.setAnswerList(subQuestionAnswerList);
+            });
+        }
+        return subQuestionList;
+    }
+
+    /**
+     * 查询组合题子题目列表
+     *
+     * @param questionIdList
+     * @return
+     */
+    @Override
+    public List<Question> listSubQuestionNoAnswerByIds(List<String> questionIdList) {
+        if (CollectionUtils.isEmpty(questionIdList)) {
+            return Collections.emptyList();
+        }
+        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(Question::getParentId, questionIdList).eq(Question::getChild, true);
+        return list(queryWrapper);
+    }
+
+    /**
+     * 组装题目,子题目,答案
+     *
+     * @param records
+     * @return
+     */
+    @Override
+    public List<Question> assembleQuestion(List<Question> records) {
+        //普通题型(单选,多选,判断,简答,填空)
+        List<String> commonIdList = records.stream().filter(r -> !QuestionType.COMBINATION.getCode().equals(r.getType()))
+                .map(Question::getId).collect(Collectors.toList());
+        //组合题
+        List<String> combinationIdList = records.stream().filter(r -> QuestionType.COMBINATION.getCode().equals(r.getType()))
+                .map(Question::getId).collect(Collectors.toList());
+
+        //查询题目和答案
+        List<Question> questionList = searchQuestionWithAnswer(commonIdList, combinationIdList);
+
+        List<Question> resultList = new ArrayList<>();
+        records.forEach(r -> {
+            Question question = questionList.stream().filter(c -> c.getId().equals(r.getId())).findFirst().orElse(null);
+            if (QuestionType.COMBINATION.getCode().equals(r.getType())) {//组合题
+                if (!Objects.isNull(question)) {
+                    r.setRepositoryName(question.getRepositoryName());
+                }
+                r.setSubQuestionList(questionList.stream().filter(c -> StringUtils.isNotBlank(c.getParentId()) &&
+                        c.getParentId().equals(r.getId())).sorted(Comparator.comparing(Question::getSort)).collect(Collectors.toList()));
+                resultList.add(r);
+            } else {
+                if (!Objects.isNull(question)) {
+                    resultList.add(question);
+                }
+            }
+        });
+        return resultList;
+    }
+
+
+    /**
+     * 查询题目和答案
+     *
+     * @param commonIdList
+     * @param combinationIdList
+     * @return
+     */
+    @Override
+    public List<Question> searchQuestionWithAnswer(List<String> commonIdList, List<String> combinationIdList) {
+        List<String> questionIdList = new ArrayList<>();
+
+        if (!CollectionUtils.isEmpty(commonIdList)) {
+            questionIdList.addAll(commonIdList);
+        }
+
+        if (!CollectionUtils.isEmpty(combinationIdList)) {
+            questionIdList.addAll(combinationIdList);
+        }
+        List<Question> subQuestionList = listSubQuestionNoAnswerByIds(combinationIdList);
+        List<String> subQuestionIdList = subQuestionList.stream().map(Question::getId).collect(Collectors.toList());
+        if (!CollectionUtils.isEmpty(subQuestionIdList)) {
+            questionIdList.addAll(subQuestionIdList);
+        }
+        return selectQuestionList(questionIdList, true, false);
+    }
 }

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/questionanswer/mapper/QuestionAnswerMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.questionanswer.entity.QuestionAnswer;
 /**
  * @Description: question_answer
  * @Author: jeecg-boot
- * @Date:   2023-02-22
+ * @Date: 2023-02-22
  * @Version: V1.0
  */
 public interface QuestionAnswerMapper extends BaseMapper<QuestionAnswer> {

+ 7 - 0
web/src/main/java/com/ynfy/buss/exam/questionanswer/service/IQuestionAnswerService.java

@@ -38,6 +38,13 @@ public interface IQuestionAnswerService extends IService<QuestionAnswer> {
 
     void removeByQuestionIds(String[] questionIds);
 
+    /**
+     * 删除组合题子题目的答案
+     *
+     * @param questionIds
+     */
+    void deleteSubQuestionAnswerById(List<String> questionIds);
+
     /**
      * 查询试题集合答案
      *

+ 11 - 0
web/src/main/java/com/ynfy/buss/exam/questionanswer/service/impl/QuestionAnswerServiceImpl.java

@@ -81,6 +81,17 @@ public class QuestionAnswerServiceImpl extends ServiceImpl<QuestionAnswerMapper,
         this.remove(wrapper);
     }
 
+    /**
+     * 删除组合题子题目的答案
+     *
+     * @param questionIds
+     */
+    public void deleteSubQuestionAnswerById(List<String> questionIds) {
+        LambdaQueryWrapper<QuestionAnswer> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(QuestionAnswer::getQuestionId, questionIds);
+        remove(queryWrapper);
+    }
+
 
     /**
      * 查询试题选项

+ 6 - 2
web/src/main/java/com/ynfy/buss/exam/repository/entity/Repository.java

@@ -100,8 +100,12 @@ public class Repository implements Serializable {
 
     @Override
     public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return true;
+        }
         Repository that = (Repository) o;
         return code.equals(that.code);
     }

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/repository/mapper/RepositoryMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.repository.entity.Repository;
 /**
  * @Description: repository
  * @Author: jeecg-boot
- * @Date:   2023-02-22
+ * @Date: 2023-02-22
  * @Version: V1.0
  */
 public interface RepositoryMapper extends BaseMapper<Repository> {

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/repository/service/IRepositoryService.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.repository.entity.Repository;
 /**
  * @Description: repository
  * @Author: jeecg-boot
- * @Date:   2023-02-22
+ * @Date: 2023-02-22
  * @Version: V1.0
  */
 public interface IRepositoryService extends IService<Repository> {

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/repository/service/impl/RepositoryServiceImpl.java

@@ -9,7 +9,7 @@ import org.springframework.stereotype.Service;
 /**
  * @Description: repository
  * @Author: jeecg-boot
- * @Date:   2023-02-22
+ * @Date: 2023-02-22
  * @Version: V1.0
  */
 @Service

+ 48 - 47
web/src/main/java/com/ynfy/buss/exam/userexam/mapper/xml/UserExamMapper.xml

@@ -27,7 +27,8 @@
         <result column="preview_user" property="previewUser" jdbcType="VARCHAR"/>
 
 
-        <collection property="userExamQuestionList" ofType="com.ynfy.buss.exam.userexamquestion.entity.UserExamQuestion">
+        <collection property="userExamQuestionList"
+                    ofType="com.ynfy.buss.exam.userexamquestion.entity.UserExamQuestion">
             <id column="ueq_id" property="id" jdbcType="VARCHAR"/>
             <result column="user_exam_id" property="userExamId" jdbcType="VARCHAR"/>
             <result column="question_id" property="questionId" jdbcType="VARCHAR"/>
@@ -41,55 +42,55 @@
             <result column="actual_score" property="actualScore" jdbcType="INTEGER"/>
             <result column="can_miss_option" property="canMissOption" jdbcType="BOOLEAN"/>
             <result column="can_blank_option" property="canBlankOption" jdbcType="BOOLEAN"/>
+            <result column="child" property="child" jdbcType="BOOLEAN"/>
+            <result column="parent_question_id" property="parentQuestionId" jdbcType="VARCHAR"/>
         </collection>
     </resultMap>
     <select id="listUserExam" resultMap="userExamMap">
-        SELECT
-            ue.id,
-            ue.user_id,
-            ue.exam_id,
-            ue.paper_id,
-            ue.user_time,
-            ue.user_score,
-            ue.commit_time,
-            ue.state,
-            ue.limit_time,
-            ue.qualify_score,
-            ue.total_score,
-            ue.passed,
-            ue.create_time,
-            ue.has_subjective,
-            ue.subjective_score,
-            ue.objective_score,
-            ue.preview_time,
-            ue.preview_user,
-            e.leave_on,
-            e.total_leave_times,
-            e.leave_time,
-            e.exam_result_showtype,
-            e.show_deadline,
-            ueq.ID as ueq_id,
-            ueq.user_exam_id,
-            ueq.question_id,
-            ueq.question_type,
-            ueq.sort,
-            ueq.question_score,
-            ueq.answered,
-            ueq.is_right,
-            ueq.answer,
-            ueq.question_index,
-            ueq.actual_score,
-            ueq.can_miss_option,
-            ueq.can_blank_option
-        FROM
-            user_exam ue
-        LEFT JOIN exam e ON ue.exam_id = e.id
-        LEFT JOIN user_exam_question ueq ON ue.ID = ueq.user_exam_id
-        WHERE
-            ue.id =  #{id}
-        ORDER BY
-            ueq.question_type,
-            ueq.sort
+        SELECT ue.id,
+               ue.user_id,
+               ue.exam_id,
+               ue.paper_id,
+               ue.user_time,
+               ue.user_score,
+               ue.commit_time,
+               ue.state,
+               ue.limit_time,
+               ue.qualify_score,
+               ue.total_score,
+               ue.passed,
+               ue.create_time,
+               ue.has_subjective,
+               ue.subjective_score,
+               ue.objective_score,
+               ue.preview_time,
+               ue.preview_user,
+               e.leave_on,
+               e.total_leave_times,
+               e.leave_time,
+               e.exam_result_showtype,
+               e.show_deadline,
+               ueq.ID as ueq_id,
+               ueq.user_exam_id,
+               ueq.question_id,
+               ueq.question_type,
+               ueq.sort,
+               ueq.question_score,
+               ueq.answered,
+               ueq.is_right,
+               ueq.answer,
+               ueq.question_index,
+               ueq.actual_score,
+               ueq.can_miss_option,
+               ueq.can_blank_option,
+               ueq.child,
+               ueq.parent_question_id
+        FROM user_exam ue
+                 LEFT JOIN exam e ON ue.exam_id = e.id
+                 LEFT JOIN user_exam_question ueq ON ue.ID = ueq.user_exam_id
+        WHERE ue.id = #{id}
+        ORDER BY ueq.question_type,
+                 ueq.sort
     </select>
 
     <select id="selectPageList" resultType="com.ynfy.buss.exam.userexam.entity.UserExam">

+ 18 - 0
web/src/main/java/com/ynfy/buss/exam/userexamquestion/entity/UserExamQuestion.java

@@ -16,6 +16,7 @@ import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.util.Date;
+import java.util.List;
 
 /**
  * @Description: 用户考试试题
@@ -141,4 +142,21 @@ public class UserExamQuestion implements Serializable {
     @Excel(name = "按空给分", width = 15)
     @ApiModelProperty(value = "按空给分")
     private Boolean canBlankOption;
+
+    /**
+     * 是否子题目
+     */
+    @Excel(name = "是否子题目", width = 15)
+    @ApiModelProperty(value = "是否子题目")
+    private Boolean child;
+
+    /**
+     * 父题目id
+     */
+    @Excel(name = "父题目id", width = 15)
+    @ApiModelProperty(value = "父题目id")
+    private String parentQuestionId;
+
+    @TableField(exist = false)
+    private List<UserExamQuestion> subQuestionList;
 }

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/userexamquestion/mapper/UserExamQuestionMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.userexamquestion.entity.UserExamQuestion;
 /**
  * @Description: 用户考试试题
  * @Author: jeecg-boot
- * @Date:   2023-03-05
+ * @Date: 2023-03-05
  * @Version: V1.0
  */
 public interface UserExamQuestionMapper extends BaseMapper<UserExamQuestion> {

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/userexamquestionanswer/mapper/UserExamQuestionAnswerMapper.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.userexamquestionanswer.entity.UserExamQuestionAnswer;
 /**
  * @Description: 用户考试答案表
  * @Author: jeecg-boot
- * @Date:   2023-04-01
+ * @Date: 2023-04-01
  * @Version: V1.0
  */
 public interface UserExamQuestionAnswerMapper extends BaseMapper<UserExamQuestionAnswer> {

+ 1 - 1
web/src/main/java/com/ynfy/buss/exam/userexamquestionanswer/service/IUserExamQuestionAnswerService.java

@@ -6,7 +6,7 @@ import com.ynfy.buss.exam.userexamquestionanswer.entity.UserExamQuestionAnswer;
 /**
  * @Description: 用户考试答案表
  * @Author: jeecg-boot
- * @Date:   2023-04-01
+ * @Date: 2023-04-01
  * @Version: V1.0
  */
 public interface IUserExamQuestionAnswerService extends IService<UserExamQuestionAnswer> {

+ 14 - 14
web/src/main/java/com/ynfy/buss/exam/userexamresult/mapper/xml/UserExamResultMapper.xml

@@ -4,10 +4,10 @@
 
     <select id="selectPageList" resultType="com.ynfy.buss.exam.userexamresult.entity.UserExamResult">
         SELECT
-                u.*,
-                IFNULL(u.update_time,u.create_time) AS  updateTime,
-                e.title as examTitle,
-                e.image as examCover
+            u.*,
+            IFNULL(u.update_time,u.create_time) AS updateTime,
+            e.title as examTitle,
+            e.image as examCover
         FROM user_exam_result u
         INNER JOIN exam e on u.exam_id = e.id
         <where>
@@ -25,16 +25,16 @@
 
     <select id="getUserExamScore" resultType="com.ynfy.buss.exam.userexamresult.entity.vo.UserExamScoreVO">
         SELECT
-            u.try_count,
-            u.max_score,
-            u.passed,
-            e.total_score,
-            e.qualify_score,
-            p.question_count
+               u.try_count,
+               u.max_score,
+               u.passed,
+               e.total_score,
+               e.qualify_score,
+               p.question_count
         FROM user_exam_result u
-        LEFT JOIN exam e on u.exam_id = e.id
-        LEFT JOIN paper p on e.paper_id=p.id
-        WHERE
-            u.user_id = #{userId} AND u.exam_id = #{examId}
+                 LEFT JOIN exam e on u.exam_id = e.id
+                 LEFT JOIN paper p on e.paper_id = p.id
+        WHERE u.user_id = #{userId}
+          AND u.exam_id = #{examId}
     </select>
 </mapper>

+ 9 - 9
web/src/main/java/com/ynfy/buss/practice/userpractice/enums/PracticeMode.java

@@ -22,6 +22,15 @@ public enum PracticeMode {
         this.value = value;
     }
 
+    public static PracticeMode getByCode(Integer code) {
+        for (PracticeMode mode : values()) {
+            if (mode.getCode().equals(code)) {
+                return mode;
+            }
+        }
+        return null;
+    }
+
     public Integer getCode() {
         return code;
     }
@@ -38,13 +47,4 @@ public enum PracticeMode {
         this.value = value;
     }
 
-    public static PracticeMode getByCode(Integer code) {
-        for (PracticeMode mode : values()) {
-            if (mode.getCode().equals(code)) {
-                return mode;
-            }
-        }
-        return null;
-    }
-
 }

+ 3 - 3
web/src/main/java/com/ynfy/buss/practice/userpractice/mapper/xml/UserPracticeMapper.xml

@@ -3,8 +3,8 @@
 <mapper namespace="com.ynfy.buss.practice.userpractice.mapper.UserPracticeMapper">
     <select id="selectPageList" resultType="com.ynfy.buss.practice.userpractice.entity.UserPractice">
         SELECT
-                up.*,
-                r.title as repositoryName
+        up.*,
+        r.title as repositoryName
         FROM user_practice up
         LEFT JOIN repository r on up.repository_id = r.id
         <where>
@@ -12,7 +12,7 @@
                 AND r.title like concat('%', #{userPractice.repositoryName}, '%')
             </if>
             <if test="userPractice.practiceName != null and userPractice.practiceName != ''">
-                AND up.practice_name  like concat('%', #{userPractice.practiceName}, '%')
+                AND up.practice_name like concat('%', #{userPractice.practiceName}, '%')
             </if>
             <if test="userPractice.userId != null and userPractice.userId != ''">
                 AND up.user_id = #{userPractice.userId}

+ 0 - 1
web/src/main/java/com/ynfy/buss/practice/userpractice/service/IUserPracticeService.java

@@ -2,7 +2,6 @@ package com.ynfy.buss.practice.userpractice.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.ynfy.buss.exam.userexam.entity.UserExam;
 import com.ynfy.buss.practice.userpractice.entity.UserPractice;
 import com.ynfy.buss.practice.userpractice.entity.dto.UserPracticeDTO;
 import com.ynfy.buss.practice.userpractice.entity.dto.UserPracticeResultDTO;

+ 3 - 1
web/src/main/java/com/ynfy/common/utils/CronUtils.java

@@ -5,6 +5,7 @@ import java.util.Date;
 
 /**
  * 时间转换quartz表达式
+ *
  * @author bool
  * @date 2020/11/29 下午3:00
  */
@@ -17,10 +18,11 @@ public class CronUtils {
 
     /**
      * 准确的时间点到表达式
+     *
      * @param date
      * @return
      */
-    public static String dateToCron(final Date date){
+    public static String dateToCron(final Date date) {
         SimpleDateFormat fmt = new SimpleDateFormat(DATE_FORMAT);
         String formatTimeStr = "";
         if (date != null) {

+ 3 - 0
web/src/main/java/com/ynfy/common/utils/SM3Utils.java

@@ -1,8 +1,10 @@
 package com.ynfy.common.utils;
+
 import org.bouncycastle.crypto.digests.SM3Digest;
 import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
 import java.security.Security;
 import java.util.Arrays;
 
@@ -27,6 +29,7 @@ public class SM3Utils {
         // 需要将BC的Provider加入到当前Java虚拟机的识别范围内
         Security.addProvider(new BouncyCastleProvider());
     }
+
     /**
      * 计算SM3摘要值
      *

+ 5 - 15
web/src/main/java/com/ynfy/common/utils/SM4Utils.java

@@ -1,20 +1,5 @@
 package com.ynfy.common.utils;
 
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.Security;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.KeyGenerator;
-import javax.crypto.Mac;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.engines.SM4Engine;
 import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
@@ -26,6 +11,11 @@ import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.*;
+
 /**
  * "国标SM3消息摘要算法和SM4加解密算法"工具类,选用了[Bouncy Castle]开源的API,参考自 </br>
  * <a href=

+ 4 - 8
web/src/main/java/com/ynfy/common/utils/ThreadPoolUtil.java

@@ -5,20 +5,16 @@ import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
 public class ThreadPoolUtil {
-    private ThreadPoolUtil() {
-
-    }
-
     private static final int CORE_POOL_SIZE = 20;
-
     private static final int MAXIMUM_POOL_SIZE = 20;
-
     private static final long KEEP_ALIVE_TIME = 60;
-
     private static final int BLOCKING_QUEUE_LENGTH = 1000;
-
     private static ThreadPoolExecutor executor;
 
+    private ThreadPoolUtil() {
+
+    }
+
     public static ThreadPoolExecutor getExecutor() {
         if (executor == null) {
             synchronized (ThreadPoolExecutor.class) {

+ 4 - 5
web/src/main/java/com/ynfy/config/WebMvcConfiguration.java

@@ -42,15 +42,13 @@ import java.util.List;
  * Spring Boot 2.0 解决跨域问题
  *
  * @Author qinfeng
- *
  */
 @Configuration
 public class WebMvcConfiguration implements WebMvcConfigurer {
-    @Resource
-    private AuthorizationInterceptor authorizationInterceptor;
-
     @Resource
     JeecgBaseConfig jeecgBaseConfig;
+    @Resource
+    private AuthorizationInterceptor authorizationInterceptor;
     @Value("${spring.resource.static-locations:}")
     private String staticLocations;
 
@@ -96,6 +94,7 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
         urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
         return new CorsFilter(urlBasedCorsConfigurationSource);
     }
+
     @Override
     public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
         MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(objectMapper());
@@ -135,7 +134,7 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
      * https://blog.csdn.net/u013810234/article/details/110097201
      */
     @Bean
-    public InMemoryHttpTraceRepository getInMemoryHttpTrace(){
+    public InMemoryHttpTraceRepository getInMemoryHttpTrace() {
         return new InMemoryHttpTraceRepository();
     }
 

+ 1 - 8
web/src/main/resources/jeecg/jeecg_config.properties

@@ -2,26 +2,19 @@
 project_path=E:\\workspace\\jeecg-boot
 #bussi_package[User defined]
 bussi_package=com.ynfy.buss
-
-
 #default code path
 #source_root_package=src
 #webroot_package=WebRoot
-
 #maven code path
 source_root_package=src.main.java
 webroot_package=src.main.webapp
-
 #ftl resource url
 templatepath=/jeecg/code-template
 system_encoding=utf-8
-
-#db Table id [User defined] 
+#db Table id [User defined]
 db_table_id=id
-
 #db convert flag[true/false]
 db_filed_convert=true
-
 #page Search Field num [User defined]
 page_search_filed_num=1
 #page_filter_fields

+ 67 - 67
web/src/main/resources/logback-spring.xml

@@ -1,77 +1,77 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <configuration debug="false">
-	<!--定义日志文件的存储地址 -->
-	<property name="LOG_HOME" value="../logs" />
+    <!--定义日志文件的存储地址 -->
+    <property name="LOG_HOME" value="../logs"/>
 
-	<!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />-->
-	<!-- 控制台输出 -->
-	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
-			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符
-			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>-->
-			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n</pattern>
-		</encoder>
-	</appender>
+    <!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />-->
+    <!-- 控制台输出 -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>-->
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n</pattern>
+        </encoder>
+    </appender>
 
-	<!-- 按照每天生成日志文件 -->
-	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
-			<!--日志文件输出的文件名 -->
-			<FileNamePattern>${LOG_HOME}/exam-boot-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
-			<!--日志文件保留天数 -->
-			<MaxHistory>30</MaxHistory>
-			<maxFileSize>10MB</maxFileSize>
-		</rollingPolicy>
-		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
-			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
-			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
-		</encoder>
-	</appender>
+    <!-- 按照每天生成日志文件 -->
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!--日志文件输出的文件名 -->
+            <FileNamePattern>${LOG_HOME}/exam-boot-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
+            <!--日志文件保留天数 -->
+            <MaxHistory>30</MaxHistory>
+            <maxFileSize>10MB</maxFileSize>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
+        </encoder>
+    </appender>
 
-	<!-- 生成 error html格式日志开始 -->
-	<appender name="HTML" class="ch.qos.logback.core.FileAppender">
-		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
-			<!--设置日志级别,过滤掉info日志,只输入error日志-->
-			<level>ERROR</level>
-		</filter>
-		<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
-			<layout class="ch.qos.logback.classic.html.HTMLLayout">
-				<pattern>%p%d%msg%M%F{32}%L</pattern>
-			</layout>
-		</encoder>
-		<file>${LOG_HOME}/error-log.html</file>
-	</appender>
-	<!-- 生成 error html格式日志结束 -->
+    <!-- 生成 error html格式日志开始 -->
+    <appender name="HTML" class="ch.qos.logback.core.FileAppender">
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <!--设置日志级别,过滤掉info日志,只输入error日志-->
+            <level>ERROR</level>
+        </filter>
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="ch.qos.logback.classic.html.HTMLLayout">
+                <pattern>%p%d%msg%M%F{32}%L</pattern>
+            </layout>
+        </encoder>
+        <file>${LOG_HOME}/error-log.html</file>
+    </appender>
+    <!-- 生成 error html格式日志结束 -->
 
-	<!-- 每天生成一个html格式的日志开始 -->
-	<appender name="FILE_HTML" class="ch.qos.logback.core.rolling.RollingFileAppender">
-		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
-			<!--日志文件输出的文件名 -->
-			<FileNamePattern>${LOG_HOME}/jeecgboot-%d{yyyy-MM-dd}.%i.html</FileNamePattern>
-			<!--日志文件保留天数 -->
-			<MaxHistory>30</MaxHistory>
-			<MaxFileSize>10MB</MaxFileSize>
-		</rollingPolicy>
-		<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
-			<layout class="ch.qos.logback.classic.html.HTMLLayout">
-				<pattern>%p%d%msg%M%F{32}%L</pattern>
-			</layout>
-		</encoder>
-	</appender>
-	<!-- 每天生成一个html格式的日志结束 -->
+    <!-- 每天生成一个html格式的日志开始 -->
+    <appender name="FILE_HTML" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!--日志文件输出的文件名 -->
+            <FileNamePattern>${LOG_HOME}/jeecgboot-%d{yyyy-MM-dd}.%i.html</FileNamePattern>
+            <!--日志文件保留天数 -->
+            <MaxHistory>30</MaxHistory>
+            <MaxFileSize>10MB</MaxFileSize>
+        </rollingPolicy>
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="ch.qos.logback.classic.html.HTMLLayout">
+                <pattern>%p%d%msg%M%F{32}%L</pattern>
+            </layout>
+        </encoder>
+    </appender>
+    <!-- 每天生成一个html格式的日志结束 -->
 
-	<!--myibatis log configure -->
-	<logger name="com.apache.ibatis" level="TRACE" />
-	<logger name="java.sql.Connection" level="DEBUG" />
-	<logger name="java.sql.Statement" level="DEBUG" />
-	<logger name="java.sql.PreparedStatement" level="DEBUG" />
+    <!--myibatis log configure -->
+    <logger name="com.apache.ibatis" level="TRACE"/>
+    <logger name="java.sql.Connection" level="DEBUG"/>
+    <logger name="java.sql.Statement" level="DEBUG"/>
+    <logger name="java.sql.PreparedStatement" level="DEBUG"/>
 
-	<!-- 日志输出级别 -->
-	<root level="INFO">
-		<appender-ref ref="STDOUT" />
-		<appender-ref ref="FILE" />
-		<appender-ref ref="HTML" />
-		<appender-ref ref="FILE_HTML" />
-	</root>
+    <!-- 日志输出级别 -->
+    <root level="INFO">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="HTML"/>
+        <appender-ref ref="FILE_HTML"/>
+    </root>
 
 </configuration>