Эх сурвалжийг харах

1、新增blockly编程查询和保存
2、课程类型列表和课程列表新增关联查询进度(课程1⭐,编程3⭐,课程类型总5⭐)
3、优化课程配置列表查询

liyanbo 4 сар өмнө
parent
commit
80c53130c3
26 өөрчлөгдсөн 716 нэмэгдсэн , 103 устгасан
  1. 5 0
      byzs-blockly/pom.xml
  2. 3 2
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/vo/BlocklyConfigRespVO.java
  3. 9 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/mysql/blocklyConfig/BlocklyConfigMapper.java
  4. 8 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyConfig/BlocklyConfigService.java
  5. 19 32
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyConfig/BlocklyConfigServiceImpl.java
  6. 18 0
      byzs-blockly/src/main/resources/mapper/blocklyConfig/BlocklyConfigMapper.xml
  7. 10 0
      byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/dal/mysql/courseconfig/CourseConfigMapper.java
  8. 8 0
      byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/courseconfig/CourseConfigService.java
  9. 18 31
      byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/courseconfig/CourseConfigServiceImpl.java
  10. 12 5
      byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/coursequestion/CourseQuestionService.java
  11. 44 9
      byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/coursequestion/CourseQuestionServiceImpl.java
  12. 20 0
      byzs-course/src/main/resources/mapper/courseconfig/CourseConfigMapper.xml
  13. 4 5
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/WebBlocklyController.java
  14. 39 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/vo/WebBlocklyTypeVO.java
  15. 4 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/vo/WebBlocklyVO.java
  16. 45 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/WebBlocklyProgressController.java
  17. 65 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/vo/WebBlocklyProgressDO.java
  18. 45 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/vo/WebBlocklyProgressDTO.java
  19. 70 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/vo/WebBlocklyProgressVO.java
  20. 8 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/dal/mysql/blockly/WebBlocklyMapper.java
  21. 21 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/dal/mysql/blocklyprogress/WebBlocklyProgressMapper.java
  22. 55 10
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/blockly/WebBlocklyServiceImpl.java
  23. 109 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/blocklyprogress/WebBlocklyProgressServiceImpl.java
  24. 12 9
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/course/WebCourseServiceImpl.java
  25. 25 0
      byzs-web/src/main/resources/mapper/blockly/WebBlocklyMapper.xml
  26. 40 0
      byzs-web/src/main/resources/mapper/blocklyprogress/BlocklyProgressMapper.xml

+ 5 - 0
byzs-blockly/pom.xml

@@ -19,6 +19,11 @@
 
 
     <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>byzs-course</artifactId>
+            <version>${revision}</version>
+        </dependency>
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>byzs-module-system</artifactId>

+ 3 - 2
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/vo/BlocklyConfigRespVO.java

@@ -2,6 +2,7 @@ package cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo;
 
 import cn.iocoder.byzs.framework.excel.core.annotations.DictFormat;
 import cn.iocoder.byzs.framework.excel.core.convert.DictConvert;
+import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionRespVO;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -59,7 +60,7 @@ public class BlocklyConfigRespVO {
     @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
     private String ccAnswerJudge;
 
-//    @Schema(description = "课程-试题")
-//    private BlocklyQuestionRespVO blocklyQuestion;
+    @Schema(description = "课程-试题")
+    private CourseQuestionRespVO blocklyQuestion;
 
 }

+ 9 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/mysql/blocklyConfig/BlocklyConfigMapper.java

@@ -2,8 +2,10 @@ package cn.iocoder.byzs.module.blockly.dal.mysql.blocklyConfig;
 
 import cn.iocoder.byzs.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigPageReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigRespVO;
 import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyConfig.BlocklyConfigDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -27,4 +29,11 @@ public interface BlocklyConfigMapper extends BaseMapperX<BlocklyConfigDO> {
     List<BlocklyConfigPageReqVO> selectBlocklyConfigPage(BlocklyConfigPageReqVO reqVO);
     Integer selectBlocklyConfigCount(BlocklyConfigPageReqVO reqVO);
 
+    /**
+     * 根据课程id列表查询课程配置
+     *
+     * @param ccCourseIds 课程id列表
+     * @return 课程配置列表
+     */
+    List<BlocklyConfigRespVO> getBlocklyConfigQuestionByCourseIds(@Param("ccCourseIds") List<Long> ccCourseIds);
 }

+ 8 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyConfig/BlocklyConfigService.java

@@ -61,6 +61,14 @@ public interface BlocklyConfigService {
      */
     List<BlocklyConfigRespVO> getBlocklyConfigQuestion(Long ccCourseId);
 
+    /**
+     * 获得课程配置-试题列表
+     *
+     * @param ccCourseIds 课程ids
+     * @return 课程配置
+     */
+    List<BlocklyConfigRespVO> getBlocklyConfigQuestionByCourseIds(List<Long> ccCourseIds);
+
     /**
      * 获得课程配置分页
      *

+ 19 - 32
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyConfig/BlocklyConfigServiceImpl.java

@@ -4,6 +4,9 @@ import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.byzs.framework.common.pojo.PageParam;
 import cn.iocoder.byzs.framework.common.pojo.PageResult;
 import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+import cn.iocoder.byzs.module.bjdx.controller.admin.courseconfig.vo.CourseConfigRespVO;
+import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionRespVO;
+import cn.iocoder.byzs.module.bjdx.service.coursequestion.CourseQuestionService;
 import cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigPageReqVO;
 import cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigRespVO;
 import cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigSaveReqVO;
@@ -33,10 +36,8 @@ public class BlocklyConfigServiceImpl implements BlocklyConfigService {
 
     @Resource
     private BlocklyConfigMapper blocklyConfigMapper;
-//    @Resource
-//    private CourseQuestionMapper courseQuestionMapper;
-//    @Resource
-//    private CourseQuestOptionMapper courseQuestOptionMapper;
+    @Resource
+    private CourseQuestionService questionService;
 
     @Override
     public Long createBlocklyConfig(BlocklyConfigSaveReqVO createReqVO) {
@@ -104,36 +105,22 @@ public class BlocklyConfigServiceImpl implements BlocklyConfigService {
                 .toList();
         if (blocklyConfigList.isEmpty()) {return blocklyConfigList;}
 
-        List<Long> quesIdList = blocklyConfigList.stream()
-                .map(BlocklyConfigRespVO::getCcQuestId)
-                .toList();
-        /*//课程配置试题Map
-        Map<Long, List<CourseQuestionRespVO>> questionMap;
-        //课程配置试题选项Map
-        Map<Long, List<CourseQuestOptionDO>> questOptionMap = new HashMap<>();
-
-        if (quesIdList.isEmpty()) {
-            questionMap = courseQuestionMapper.selectByIds(quesIdList).stream()
-                    .map(doItem -> BeanUtils.toBean(doItem, CourseQuestionRespVO.class))
-                    .collect(Collectors.groupingBy(CourseQuestionRespVO::getId));
-        } else {
-            questionMap = new HashMap<>();
-        }
-        if (quesIdList.isEmpty()) {
-            questOptionMap = courseQuestOptionMapper.selectListByQuestId(questionMap.keySet()).stream().collect(Collectors.groupingBy(CourseQuestOptionDO::getCqoQuestId));
+        //设置课程配置-试题列表(引用)
+        Map<Long, CourseQuestionRespVO> questionMap = questionService.setCourseQuestionList(blocklyConfigList.stream().map(BlocklyConfigRespVO::getCcQuestId).collect(Collectors.toList()));
+        blocklyConfigList.forEach(item -> item.setBlocklyQuestion(questionMap.get(item.getCcQuestId())));
 
-        }
 
-        if(questionMap.size() > 0 && questOptionMap.size() > 0) {
-            //封装
-            Map<Long, List<CourseQuestOptionDO>> finalQuestOptionMap = questOptionMap;
-            blocklyConfigList.forEach(blocklyConfig -> {
-                Long questId = blocklyConfig.getCcQuestId();
-                CourseQuestionRespVO courseQuestion = questionMap.get(questId).get(0);
-                courseQuestion.setCourseQuestOptionList(finalQuestOptionMap.get(questId));
-                blocklyConfig.setCourseQuestion(courseQuestion);
-            });
-        }*/
+        return blocklyConfigList;
+    }
+
+    @Override
+    public List<BlocklyConfigRespVO> getBlocklyConfigQuestionByCourseIds(List<Long> ccCourseIds) {
+
+        List<BlocklyConfigRespVO> blocklyConfigList = blocklyConfigMapper.getBlocklyConfigQuestionByCourseIds(ccCourseIds);
+
+        //设置课程配置-试题列表(引用)
+        Map<Long, CourseQuestionRespVO> questionMap = questionService.setCourseQuestionList(blocklyConfigList.stream().map(BlocklyConfigRespVO::getCcQuestId).collect(Collectors.toList()));
+        blocklyConfigList.forEach(item -> item.setBlocklyQuestion(questionMap.get(item.getCcQuestId())));
 
         return blocklyConfigList;
     }

+ 18 - 0
byzs-blockly/src/main/resources/mapper/blocklyConfig/BlocklyConfigMapper.xml

@@ -53,4 +53,22 @@
             </if>
         </where>
     </select>
+    <select id="getBlocklyConfigQuestionByCourseIds" parameterType="java.util.List"
+            resultType="cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigRespVO">
+        SELECT cc.id, cc.cc_course_id as ccCourseId,c.tenant_id as tenantId,
+                cc_quest_source as ccQuestSource,
+                cc_quest_content as ccQuestContent,
+                cc_quest_option as ccQuestOption,
+                cc_ai_answer as ccAiAnswer,
+                cc_answer as ccAnswer, cc.cc_quest_id as ccQuestId, cc.cc_time as ccTime, cc.cc_answer_judge as ccAnswerJudge,
+               c.bc_name as courseName,
+               cc_ai_quest_tip as ccAiQuestTip
+        FROM blockly_course_config cc
+        LEFT JOIN blockly_course c ON cc.cc_course_id = c.id
+        WHERE cc.cc_course_id IN
+        <foreach collection="ccCourseIds" item="courseId" open="(" close=")" separator=",">
+            #{courseId}
+        </foreach>
+        AND c.deleted = 0 and cc.deleted = 0
+    </select>
 </mapper>

+ 10 - 0
byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/dal/mysql/courseconfig/CourseConfigMapper.java

@@ -2,8 +2,10 @@ package cn.iocoder.byzs.module.bjdx.dal.mysql.courseconfig;
 
 import cn.iocoder.byzs.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.byzs.module.bjdx.controller.admin.courseconfig.vo.CourseConfigPageReqVO;
+import cn.iocoder.byzs.module.bjdx.controller.admin.courseconfig.vo.CourseConfigRespVO;
 import cn.iocoder.byzs.module.bjdx.dal.dataobject.courseconfig.CourseConfigDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -27,4 +29,12 @@ public interface CourseConfigMapper extends BaseMapperX<CourseConfigDO> {
     List<CourseConfigPageReqVO> selectCourseConfigPage(CourseConfigPageReqVO reqVO);
     Integer selectCourseConfigCount(CourseConfigPageReqVO reqVO);
 
+    /**
+     * 获得课程配置-试题列表
+     *
+     * @param ccCourseIds 课程id列表
+     * @return 课程配置
+     */
+    List<CourseConfigRespVO> getCourseConfigQuestionByCourseIds(@Param("ccCourseIds") List<Long> ccCourseIds);
+
 }

+ 8 - 0
byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/courseconfig/CourseConfigService.java

@@ -61,6 +61,14 @@ public interface CourseConfigService {
      */
     List<CourseConfigRespVO> getCourseConfigQuestion(Long ccCourseId);
 
+    /**
+     * 获得课程配置-试题列表
+     *
+     * @param ccCourseIds 课程id列表
+     * @return 课程配置
+     */
+    List<CourseConfigRespVO> getCourseConfigQuestionByCourseIds(List<Long> ccCourseIds);
+
     /**
      * 获得课程配置分页
      *

+ 18 - 31
byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/courseconfig/CourseConfigServiceImpl.java

@@ -13,6 +13,8 @@ import cn.iocoder.byzs.module.bjdx.dal.dataobject.coursequestoption.CourseQuestO
 import cn.iocoder.byzs.module.bjdx.dal.mysql.courseconfig.CourseConfigMapper;
 import cn.iocoder.byzs.module.bjdx.dal.mysql.coursequestion.CourseQuestionMapper;
 import cn.iocoder.byzs.module.bjdx.dal.mysql.coursequestoption.CourseQuestOptionMapper;
+import cn.iocoder.byzs.module.bjdx.service.coursequestion.CourseQuestionService;
+import cn.iocoder.byzs.module.bjdx.service.coursequestion.CourseQuestionServiceImpl;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -37,9 +39,7 @@ public class CourseConfigServiceImpl implements CourseConfigService {
     @Resource
     private CourseConfigMapper courseConfigMapper;
     @Resource
-    private CourseQuestionMapper courseQuestionMapper;
-    @Resource
-    private CourseQuestOptionMapper courseQuestOptionMapper;
+    private CourseQuestionService questionService;
 
     @Override
     public Long createCourseConfig(CourseConfigSaveReqVO createReqVO) {
@@ -107,36 +107,23 @@ public class CourseConfigServiceImpl implements CourseConfigService {
                 .toList();
         if (courseConfigList.isEmpty()) {return courseConfigList;}
 
-        List<Long> quesIdList = courseConfigList.stream()
-                .map(CourseConfigRespVO::getCcQuestId)
-                .toList();
-        //课程配置试题Map
-        Map<Long, List<CourseQuestionRespVO>> questionMap;
-        //课程配置试题选项Map
-        Map<Long, List<CourseQuestOptionDO>> questOptionMap = new HashMap<>();
-
-        if (quesIdList.isEmpty()) {
-            questionMap = courseQuestionMapper.selectByIds(quesIdList).stream()
-                    .map(doItem -> BeanUtils.toBean(doItem, CourseQuestionRespVO.class))
-                    .collect(Collectors.groupingBy(CourseQuestionRespVO::getId));
-        } else {
-            questionMap = new HashMap<>();
-        }
-        if (quesIdList.isEmpty()) {
-            questOptionMap = courseQuestOptionMapper.selectListByQuestId(questionMap.keySet()).stream().collect(Collectors.groupingBy(CourseQuestOptionDO::getCqoQuestId));
+        //设置课程配置-试题列表(引用)
+        Map<Long, CourseQuestionRespVO> questionMap = questionService.setCourseQuestionList(courseConfigList.stream().map(CourseConfigRespVO::getCcQuestId).collect(Collectors.toList()));
+        courseConfigList.forEach(item -> item.setCourseQuestion(questionMap.get(item.getCcQuestId())));
 
-        }
+        return courseConfigList;
+    }
 
-        if(questionMap.size() > 0 && questOptionMap.size() > 0) {
-            //封装
-            Map<Long, List<CourseQuestOptionDO>> finalQuestOptionMap = questOptionMap;
-            courseConfigList.forEach(courseConfig -> {
-                Long questId = courseConfig.getCcQuestId();
-                CourseQuestionRespVO courseQuestion = questionMap.get(questId).get(0);
-                courseQuestion.setCourseQuestOptionList(finalQuestOptionMap.get(questId));
-                courseConfig.setCourseQuestion(courseQuestion);
-            });
-        }
+    @Override
+    public List<CourseConfigRespVO> getCourseConfigQuestionByCourseIds(List<Long> ccCourseIds) {
+
+        //课程配置列表
+        List<CourseConfigRespVO> courseConfigList = courseConfigMapper.getCourseConfigQuestionByCourseIds(ccCourseIds);
+        if (courseConfigList.isEmpty()) {return courseConfigList;}
+
+        //设置课程配置-试题列表(引用)
+        Map<Long, CourseQuestionRespVO> questionMap = questionService.setCourseQuestionList(courseConfigList.stream().map(CourseConfigRespVO::getCcQuestId).collect(Collectors.toList()));
+        courseConfigList.forEach(item -> item.setCourseQuestion(questionMap.get(item.getCcQuestId())));
 
         return courseConfigList;
     }

+ 12 - 5
byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/coursequestion/CourseQuestionService.java

@@ -1,13 +1,14 @@
 package cn.iocoder.byzs.module.bjdx.service.coursequestion;
 
-import java.util.*;
-
+import cn.iocoder.byzs.framework.common.pojo.PageResult;
 import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionPageReqVO;
+import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionRespVO;
 import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionSaveReqVO;
-import jakarta.validation.*;
-import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.*;
 import cn.iocoder.byzs.module.bjdx.dal.dataobject.coursequestion.CourseQuestionDO;
-import cn.iocoder.byzs.framework.common.pojo.PageResult;
+import jakarta.validation.Valid;
+
+import java.util.List;
+import java.util.Map;
 
 /**
  * 课程-试题 Service 接口
@@ -69,4 +70,10 @@ public interface CourseQuestionService {
      */
     PageResult<CourseQuestionDO> getCourseQuestionNoPage(CourseQuestionPageReqVO pageReqVO);
 
+    /**
+     * 设置课程配置-试题列表(引用)
+     *
+     * @param quesIdList 课程配置列表
+     */
+    Map<Long, CourseQuestionRespVO>  setCourseQuestionList(List<Long> quesIdList);
 }

+ 44 - 9
byzs-course/src/main/java/cn/iocoder/byzs/module/bjdx/service/coursequestion/CourseQuestionServiceImpl.java

@@ -1,25 +1,24 @@
 package cn.iocoder.byzs.module.bjdx.service.coursequestion;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.byzs.framework.common.pojo.PageResult;
+import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
 import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionPageReqVO;
+import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionRespVO;
 import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.CourseQuestionSaveReqVO;
+import cn.iocoder.byzs.module.bjdx.dal.dataobject.coursequestion.CourseQuestionDO;
 import cn.iocoder.byzs.module.bjdx.dal.dataobject.coursequestoption.CourseQuestOptionDO;
+import cn.iocoder.byzs.module.bjdx.dal.mysql.coursequestion.CourseQuestionMapper;
 import cn.iocoder.byzs.module.bjdx.dal.mysql.coursequestoption.CourseQuestOptionMapper;
-import org.springframework.stereotype.Service;
 import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.util.*;
-import cn.iocoder.byzs.module.bjdx.controller.admin.coursequestion.vo.*;
-import cn.iocoder.byzs.module.bjdx.dal.dataobject.coursequestion.CourseQuestionDO;
-import cn.iocoder.byzs.framework.common.pojo.PageResult;
-import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
-
-import cn.iocoder.byzs.module.bjdx.dal.mysql.coursequestion.CourseQuestionMapper;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.byzs.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.byzs.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.byzs.module.bjdx.enums.ErrorCodeConstants.*;
+import static cn.iocoder.byzs.module.bjdx.enums.ErrorCodeConstants.COURSE_QUESTION_NOT_EXISTS;
 
 /**
  * 课程-试题 Service 实现类
@@ -106,4 +105,40 @@ public class CourseQuestionServiceImpl implements CourseQuestionService {
         return courseQuestionMapper.selectPage(pageReqVO);
     }
 
+    /**
+     * 设置课程配置-试题列表(引用)
+     *
+     * @param quesIdList 课程配置的试题列表
+     */
+    @Override
+    public Map<Long, CourseQuestionRespVO> setCourseQuestionList(List<Long> quesIdList) {
+        if (quesIdList.isEmpty() || quesIdList.stream().allMatch(Objects::isNull)) {
+            return Collections.emptyMap();
+        }
+
+        Map<Long, CourseQuestionRespVO> map = new HashMap<>();
+
+        //课程配置试题Map
+        Map<Long, List<CourseQuestionRespVO>> questionMap;
+        //课程配置试题选项Map
+        Map<Long, List<CourseQuestOptionDO>> questOptionMap = new HashMap<>();
+
+        questionMap = courseQuestionMapper.selectByIds(quesIdList).stream()
+                .map(doItem -> BeanUtils.toBean(doItem, CourseQuestionRespVO.class))
+                .collect(Collectors.groupingBy(CourseQuestionRespVO::getId));
+
+        if(!questionMap.isEmpty()) {
+            questOptionMap = courseQuestOptionMapper.selectListByQuestId(questionMap.keySet()).stream().collect(Collectors.groupingBy(CourseQuestOptionDO::getCqoQuestId));
+
+            //封装
+            Map<Long, List<CourseQuestOptionDO>> finalQuestOptionMap = questOptionMap;
+            quesIdList.forEach(questId -> {
+                CourseQuestionRespVO courseQuestion = questionMap.get(questId).get(0);
+                courseQuestion.setCourseQuestOptionList(finalQuestOptionMap.get(questId));
+                map.put(questId, courseQuestion);
+            });
+            return map;
+        }
+        return Collections.emptyMap();
+    }
 }

+ 20 - 0
byzs-course/src/main/resources/mapper/courseconfig/CourseConfigMapper.xml

@@ -53,4 +53,24 @@
             </if>
         </where>
     </select>
+    <select id="getCourseConfigQuestionByCourseIds"
+            parameterType="java.util.List"
+            resultType="cn.iocoder.byzs.module.bjdx.controller.admin.courseconfig.vo.CourseConfigRespVO">
+
+        SELECT cc.id, cc.cc_course_id as ccCourseId,c.tenant_id as tenantId,
+        cc_quest_source as ccQuestSource,
+        cc_quest_content as ccQuestContent,
+        cc_quest_option as ccQuestOption,
+        cc_ai_answer as ccAiAnswer,
+        cc_answer as ccAnswer, cc.cc_quest_id as ccQuestId, cc.cc_time as ccTime, cc.cc_answer_judge as ccAnswerJudge,
+        c.course_name as courseName,
+        cc_ai_quest_tip as ccAiQuestTip
+        FROM bjdx_course_config cc
+        LEFT JOIN bjdx_course c ON cc.cc_course_id = c.id
+        WHERE cc.cc_course_id IN
+        <foreach collection="ccCourseIds" item="courseId" open="(" close=")" separator=",">
+            #{courseId}
+        </foreach>
+        AND c.deleted = 0 and cc.deleted = 0
+    </select>
 </mapper>

+ 4 - 5
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/WebBlocklyController.java

@@ -9,6 +9,7 @@ import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTyp
 import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyType.BlocklyTypeDO;
 import cn.iocoder.byzs.module.blockly.service.blockly.BlocklyService;
 import cn.iocoder.byzs.module.blockly.service.blocklyType.BlocklyTypeService;
+import cn.iocoder.byzs.module.web.controller.admin.blockly.vo.WebBlocklyTypeVO;
 import cn.iocoder.byzs.module.web.controller.admin.blockly.vo.WebBlocklyVO;
 import cn.iocoder.byzs.module.web.service.blockly.WebBlocklyServiceImpl;
 import io.swagger.v3.oas.annotations.Operation;
@@ -57,11 +58,9 @@ public class WebBlocklyController {
     @Operation(summary = "获得blockly课程类型-根据主题id")
     @TenantIgnore
     @PermitAll
-    public CommonResult<List<BlocklyTypeRespVO>> getTypeScByGradeId(@RequestParam("id") Integer id) {
-        BlocklyTypeListReqVO listReqVO = new BlocklyTypeListReqVO().setCtParentId(id);
-        List<BlocklyTypeDO> list = blocklyTypeService.getBlocklyTypeList(listReqVO);
-        List<BlocklyTypeRespVO> listVo = BeanUtils.toBean(list, BlocklyTypeRespVO.class);
-        return success(listVo);
+    public CommonResult<List<WebBlocklyTypeVO>> getTypeByThemeId(@RequestParam("id") Integer themeId) {
+        List<WebBlocklyTypeVO> list = webBlocklyService.getTypeByThemeId(themeId);
+        return success(list);
     }
 
     @GetMapping("/getBlocklyByTypeId")

+ 39 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/vo/WebBlocklyTypeVO.java

@@ -0,0 +1,39 @@
+package cn.iocoder.byzs.module.web.controller.admin.blockly.vo;
+
+import cn.iocoder.byzs.framework.common.pojo.PageParam;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigRespVO;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "blockly课程类型VO")
+@Data
+public class WebBlocklyTypeVO extends PageParam {
+
+    @Schema(description = "租户编号")
+    private Long tenantId;
+
+    @Schema(description = "课程类型id", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Long id;
+
+    @Schema(description = "课程类型名称")
+    private String ctType;
+
+    @Schema(description = "课程类型封面")
+    private String ctTypeImage;
+
+    @Schema(description = "课程类型父级id")
+    private Integer ctParentId;
+
+    @Schema(description = "课程排序")
+    private Integer ctTypeSort;
+
+    @Schema(description = "完成进度")
+    private Integer progress;
+
+    @Schema(description = "用户id")
+    private Long userId;
+
+}

+ 4 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/vo/WebBlocklyVO.java

@@ -56,6 +56,10 @@ public class WebBlocklyVO extends PageParam {
     @Schema(description = "课程状态")
     private String bcStatus;
 
+    // 课程配置列表
     private List<BlocklyConfigRespVO> blocklyConfigList;
 
+    // 课程进度
+    private Integer progress;
+
 }

+ 45 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/WebBlocklyProgressController.java

@@ -0,0 +1,45 @@
+package cn.iocoder.byzs.module.web.controller.admin.blocklyprogress;
+
+import cn.iocoder.byzs.framework.common.pojo.CommonResult;
+import cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDO;
+import cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressVO;
+import cn.iocoder.byzs.module.web.service.blocklyprogress.WebBlocklyProgressServiceImpl;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import jodd.util.StringUtil;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Objects;
+
+import static cn.iocoder.byzs.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.byzs.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.byzs.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static cn.iocoder.byzs.module.bjdx.enums.ErrorCodeConstants.PROGRESS_NOT_EXISTS;
+
+
+@Tag(name = "管理后台 - 评估报告-课程开课率")
+@RestController
+@RequestMapping("/blocklyReport/progress")
+@Validated
+public class WebBlocklyProgressController {
+
+    @Resource
+    private WebBlocklyProgressServiceImpl progressService;
+
+    @PostMapping("/saveReportProgress")
+    @Operation(summary = "保存评估报告进度")
+    public CommonResult<Boolean> saveReportProgress(@Valid @RequestBody WebBlocklyProgressDO reportProgressDO) {
+        // 校验
+        if (StringUtil.isBlank(reportProgressDO.getBrpType()) || reportProgressDO.getBrpType().equals("courseQuest") && Objects.isNull(reportProgressDO.getBrpCourseConfigId())) {
+            throw exception(PROGRESS_NOT_EXISTS);
+        }
+        //用户id
+        reportProgressDO.setBrpUserId(getLoginUserId());
+        // 插入
+        progressService.saveReportProgress(reportProgressDO);
+        return success(true);
+    }
+}

+ 65 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/vo/WebBlocklyProgressDO.java

@@ -0,0 +1,65 @@
+package cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo;
+
+import cn.iocoder.byzs.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+import lombok.experimental.Accessors;
+
+/**
+ * 评估报告-课程开课率 DO
+ *
+ * @author lyb
+ */
+@TableName("blockly_report_progress")
+@KeySequence("blockly_report_progress_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class WebBlocklyProgressDO extends BaseDO {
+
+    /**
+     * 主键id
+     */
+    @TableId
+    private Long brpId;
+    /**
+     * 用户id
+     */
+    private Long brpUserId;
+    /**
+     * 主题id
+     */
+    @NonNull
+    private Long brpZtId;
+    /**
+     * 课程类型id
+     */
+    @NonNull
+    private Long brpCtId;
+    /**
+     * 课程id
+     */
+    @NonNull
+    private Long brpCourseId;
+    /**
+     * 课程配置id
+     */
+    private Long brpCourseConfigId;
+    /**
+     * 进度分类
+     */
+    @NonNull
+    private String brpType;
+    /**
+     * 进度(课程开课率、课程互动率、ai问答次数)
+     */
+    private Double brpProgress;
+
+
+}

+ 45 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/vo/WebBlocklyProgressDTO.java

@@ -0,0 +1,45 @@
+package cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 评估报告-课程开课率 VO
+ *
+ * @author lyb
+ */
+@Data
+@Accessors(chain = true)
+public class WebBlocklyProgressDTO {
+
+    /**
+     /**
+     * 用户id
+     */
+    private String userName;
+    /**
+     * 主题id
+     */
+    private Long brpZtId;
+    /**
+     * 课程类型名称
+     */
+    private String kcflName;
+    /**
+     * 课程名称
+     */
+    private String courseName;
+    /**
+     * 课程配置id
+     */
+    private Long courseConfigId;
+    /**
+     * 进度分类
+     */
+    private String progressType;
+    /**
+     * 进度(课程开课率、课程互动率、ai问答次数)
+     */
+    private Double progress;
+
+}

+ 70 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blocklyprogress/vo/WebBlocklyProgressVO.java

@@ -0,0 +1,70 @@
+package cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * 评估报告-课程开课率 DO
+ *
+ * @author lyb
+ */
+@Data
+@Accessors(chain = true)
+public class WebBlocklyProgressVO {
+
+    /**
+     /**
+     * 用户id
+     */
+//    private String userName;
+    /**
+     * 年级名称
+     */
+//    private String njName;
+    /**
+     * 年级课程开课率
+     */
+    private Double njCourseProgress;
+    /**
+     * 年级课程互动率
+     */
+    private Double njCourseConfigProgress;
+
+    /**
+     * ai问答次数
+     */
+    private Integer aiCount = 0;
+
+    /**
+     * 评语
+     */
+    private String comment;
+
+    /**
+     * 课程开课率
+     */
+    private List<ReportProgressVo> reportCourseProgressList;
+
+    /**
+     * 课程互动率
+     */
+    private List<ReportProgressVo> reportCourseConfigProgressList;
+
+
+    @Data
+    @Accessors(chain = true)
+    public static class ReportProgressVo {
+
+        /**
+         * 课程类型名称
+         */
+        private String kcflName;
+        /**
+         * 进度(课程开课率、课程互动率、ai问答次数)
+         */
+        private Double progress = 0d;
+    }
+
+}

+ 8 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/dal/mysql/blockly/WebBlocklyMapper.java

@@ -1,6 +1,7 @@
 package cn.iocoder.byzs.module.web.dal.mysql.blockly;
 
 import cn.iocoder.byzs.module.bjdx.controller.admin.course.vo.CoursePageReqVO;
+import cn.iocoder.byzs.module.web.controller.admin.blockly.vo.WebBlocklyTypeVO;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
@@ -19,4 +20,11 @@ public interface WebBlocklyMapper {
      * @return 课程列表
      */
     List<CoursePageReqVO> selectCourse(CoursePageReqVO reqVO);
+
+    /**
+     * 根据主题id查询课程类型列表
+     * @param blocklyTypeVO
+     * @return
+     */
+    List<WebBlocklyTypeVO> getTypeByThemeId(WebBlocklyTypeVO blocklyTypeVO);
 }

+ 21 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/dal/mysql/blocklyprogress/WebBlocklyProgressMapper.java

@@ -0,0 +1,21 @@
+package cn.iocoder.byzs.module.web.dal.mysql.blocklyprogress;
+
+import cn.iocoder.byzs.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDO;
+import cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDTO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 评估报告-课程开课率 Mapper
+ *
+ * @author lyb
+ */
+@Mapper
+public interface WebBlocklyProgressMapper extends BaseMapperX<WebBlocklyProgressDO> {
+
+    List<WebBlocklyProgressDTO> selectProgres(WebBlocklyProgressDO blocklyProgressDO);
+
+    List<WebBlocklyProgressDO> getBlocklyProgressByTypeId(WebBlocklyProgressDO blocklyProgressDO);
+}

+ 55 - 10
byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/blockly/WebBlocklyServiceImpl.java

@@ -3,12 +3,17 @@ package cn.iocoder.byzs.module.web.service.blockly;
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.byzs.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+import cn.iocoder.byzs.framework.web.core.util.WebFrameworkUtils;
 import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO;
 import cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigRespVO;
 import cn.iocoder.byzs.module.blockly.service.blockly.BlocklyService;
 import cn.iocoder.byzs.module.blockly.service.blocklyConfig.BlocklyConfigService;
+import cn.iocoder.byzs.module.blockly.service.blocklyType.BlocklyTypeService;
+import cn.iocoder.byzs.module.web.controller.admin.blockly.vo.WebBlocklyTypeVO;
 import cn.iocoder.byzs.module.web.controller.admin.blockly.vo.WebBlocklyVO;
+import cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDO;
 import cn.iocoder.byzs.module.web.dal.mysql.blockly.WebBlocklyMapper;
+import cn.iocoder.byzs.module.web.service.blocklyprogress.WebBlocklyProgressServiceImpl;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -16,6 +21,8 @@ import org.springframework.validation.annotation.Validated;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * web课程 Service 实现类
@@ -26,38 +33,76 @@ import java.util.List;
 @Validated
 public class WebBlocklyServiceImpl {
 
+    @Resource
+    private BlocklyTypeService blocklyTypeService;
     @Resource
     private BlocklyService blocklyService;
     @Resource
     private BlocklyConfigService blocklyConfigService;
     @Resource
     private WebBlocklyMapper webBlocklyMapper;
+    @Resource
+    private WebBlocklyProgressServiceImpl webBlocklyProgressService;
 
 
+    /**
+     * 根据课程类型id查询课程列表
+     *
+     * @param typeId 课程类型id
+     * @return 课程列表
+     */
     public List<WebBlocklyVO> getBlocklyVoByTypeId(Long typeId) {
         List<BlocklyPageReqVO> blocklyList = blocklyService.getBlocklyList(new BlocklyPageReqVO().setBcType(typeId).setBcStatus(String.valueOf(CommonStatusEnum.ENABLE.getStatus())));
 
-        // 目前数据少先循环查询即可
-//        List<Long> idList = blocklyList.stream()
-//                .map(BlocklyPageReqVO::getId)
-//                .collect(Collectors.toList());
+        //填充课程配置
+        List<Long> idList = blocklyList.stream()
+                .map(BlocklyPageReqVO::getId)
+                .collect(Collectors.toList());
+        List<BlocklyConfigRespVO> blocklyConfigQuestionList = blocklyConfigService.getBlocklyConfigQuestionByCourseIds(idList);
+        Map<Long, List<BlocklyConfigRespVO>> blocklyConfigQuestionMap = blocklyConfigQuestionList.stream().collect(Collectors.groupingBy(BlocklyConfigRespVO::getCcCourseId));
 
-        //优化需要走web端类
-//        webBlocklyMapper
+        //课程小节进度
+        List<WebBlocklyProgressDO> progressList = webBlocklyProgressService.getBlocklyProgressByTypeId(typeId);
+        Map<Long, WebBlocklyProgressDO> progressMap = progressList.stream().collect(Collectors.toMap(WebBlocklyProgressDO::getBrpUserId, a -> a));
 
-        List<WebBlocklyVO> blocklyVoList = new ArrayList<>();
 
+
+        //重新组装
+        List<WebBlocklyVO> blocklyVoList = new ArrayList<>();
         for (BlocklyPageReqVO blockly : blocklyList) {
-            List<BlocklyConfigRespVO> blocklyConfigQuestion = blocklyConfigService.getBlocklyConfigQuestion(blockly.getId());
+
+            //组装试题配置
+            List<BlocklyConfigRespVO> blocklyConfigQuestion = blocklyConfigQuestionMap.get(blockly.getId());
             List<BlocklyConfigRespVO> list = new ArrayList<>();
+
+            WebBlocklyVO webBlocklyVO = BeanUtils.toBean(blockly, WebBlocklyVO.class);
             if (CollUtil.isNotEmpty(blocklyConfigQuestion)) {
-                // 根据 ccTime 升序排序(已满足需求)
+                // 根据 ccTime 升序排序(弹框时间
                 list = blocklyConfigQuestion.stream().sorted(Comparator.comparing(BlocklyConfigRespVO::getCcTime)).toList();
             }
-            blocklyVoList.add(BeanUtils.toBean(blockly, WebBlocklyVO.class).setBlocklyConfigList(list));
+            webBlocklyVO.setBlocklyConfigList(list);
+
+            //填充进度
+            if (progressMap.containsKey(blockly.getId())) {
+                WebBlocklyProgressDO progress = progressMap.get(blockly.getId());
+                webBlocklyVO.setProgress(progress.getBrpProgress().intValue());
+            }else{
+                webBlocklyVO.setProgress(0);
+            }
+
+            blocklyVoList.add(webBlocklyVO);
         }
 
         return blocklyVoList;
     }
 
+    /**
+     * 根据课程类型查询课程类型列表
+     * @param themeId
+     * @return
+     */
+    public List<WebBlocklyTypeVO> getTypeByThemeId(Integer themeId) {
+        WebBlocklyTypeVO blocklyTypeVO = new WebBlocklyTypeVO().setCtParentId(themeId).setUserId(WebFrameworkUtils.getLoginUserId());
+        return webBlocklyMapper.getTypeByThemeId(blocklyTypeVO);
+    }
 }

+ 109 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/blocklyprogress/WebBlocklyProgressServiceImpl.java

@@ -0,0 +1,109 @@
+package cn.iocoder.byzs.module.web.service.blocklyprogress;
+
+import cn.iocoder.byzs.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.byzs.framework.web.core.util.WebFrameworkUtils;
+import cn.iocoder.byzs.module.bjdx.dal.mysql.reportmanage.ReportManageMapper;
+import cn.iocoder.byzs.module.blockly.dal.mysql.blocklyType.BlocklyTypeMapper;
+import cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDO;
+import cn.iocoder.byzs.module.web.dal.mysql.blocklyprogress.WebBlocklyProgressMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+/**
+ * 评估报告-课程开课率 Service 实现类
+ *
+ * @author lyb
+ */
+@Service
+@Validated
+public class WebBlocklyProgressServiceImpl {
+
+    @Resource
+    private WebBlocklyProgressMapper progressMapper;
+    @Resource
+    private ReportManageMapper reportManageMapper;
+    @Resource
+    private BlocklyTypeMapper blocklyTypeMapper;
+
+    /**
+     * 保存评估报告进度
+     *
+     * @param progress 评估报告进度
+     */
+    public void saveReportProgress(WebBlocklyProgressDO progress) {
+
+        LambdaQueryWrapperX<WebBlocklyProgressDO> progressLambdaQueryWrapperX = new LambdaQueryWrapperX<WebBlocklyProgressDO>()
+                .eqIfPresent(WebBlocklyProgressDO::getBrpUserId, progress.getBrpUserId())
+                .eqIfPresent(WebBlocklyProgressDO::getBrpType, progress.getBrpType())
+                .eqIfPresent(WebBlocklyProgressDO::getBrpZtId, progress.getBrpZtId());
+
+        switch (progress.getBrpType()) {
+            case "course":// 课程开课率
+                progressLambdaQueryWrapperX
+                        .eqIfPresent(WebBlocklyProgressDO::getBrpCtId, progress.getBrpCtId())
+                        .eqIfPresent(WebBlocklyProgressDO::getBrpCourseId, progress.getBrpCourseId());
+                WebBlocklyProgressDO courseProgressDo = progressMapper.selectOne(progressLambdaQueryWrapperX);
+                if (courseProgressDo == null) {
+                    progressMapper.insert(progress);
+                }else if (progress.getBrpProgress() > courseProgressDo.getBrpProgress()) {
+                    courseProgressDo.setBrpProgress(progress.getBrpProgress());
+                    progressMapper.updateById(courseProgressDo);
+                }
+                break;
+
+            case "courseQuest":// 课程互动率
+                progressLambdaQueryWrapperX
+                        .eqIfPresent(WebBlocklyProgressDO::getBrpCtId, progress.getBrpCtId())
+                        .eqIfPresent(WebBlocklyProgressDO::getBrpCourseId, progress.getBrpCourseId())
+                        .eqIfPresent(WebBlocklyProgressDO::getBrpCourseConfigId, progress.getBrpCourseConfigId());
+                WebBlocklyProgressDO courseConfigProgressDo = progressMapper.selectOne(progressLambdaQueryWrapperX);
+
+                if (courseConfigProgressDo == null) {
+                    progressMapper.insert(progress);
+                }
+                break;
+
+            case "aiCount":// ai问答次数
+                WebBlocklyProgressDO aiCountProgressDo = progressMapper.selectOne(progressLambdaQueryWrapperX);
+
+                if (aiCountProgressDo == null) {
+                    progressMapper.insert(progress);
+                }else {
+                    aiCountProgressDo.setBrpProgress(aiCountProgressDo.getBrpProgress()+1);
+                    progressMapper.updateById(aiCountProgressDo);
+                }
+                break;
+
+            case "blockly":// blocklu编程课通关
+                progressLambdaQueryWrapperX
+                        .eqIfPresent(WebBlocklyProgressDO::getBrpCtId, progress.getBrpCtId())
+                        .eqIfPresent(WebBlocklyProgressDO::getBrpCourseId, progress.getBrpCourseId());
+                WebBlocklyProgressDO blocklyProgressDo = progressMapper.selectOne(progressLambdaQueryWrapperX);
+
+                if (blocklyProgressDo == null) {
+                    progressMapper.insert(progress);
+                }else {
+                    if (progress.getBrpProgress() > blocklyProgressDo.getBrpProgress()){
+                        blocklyProgressDo.setBrpProgress(progress.getBrpProgress());
+                        progressMapper.updateById(blocklyProgressDo);
+                    }
+                }
+                break;
+        }
+    }
+
+    /**
+     * 根据类型id查询进度
+     *
+     * @param typeId 类型id
+     * @return 进度列表
+     */
+    public List<WebBlocklyProgressDO> getBlocklyProgressByTypeId(Long typeId) {
+        WebBlocklyProgressDO webBlocklyProgressDO = new WebBlocklyProgressDO().setBrpCtId(typeId).setBrpUserId(WebFrameworkUtils.getLoginUserId());
+        List<WebBlocklyProgressDO> blocklyProgressList = progressMapper.getBlocklyProgressByTypeId(webBlocklyProgressDO);
+        return blocklyProgressList;
+    }
+}

+ 12 - 9
byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/course/WebCourseServiceImpl.java

@@ -16,6 +16,7 @@ import org.springframework.validation.annotation.Validated;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 /**
@@ -38,21 +39,23 @@ public class WebCourseServiceImpl{
     public List<WebCourseVO> getCourseVoByTypeId(Long typeId) {
         List<CoursePageReqVO> courseList = courseService.getCourseList(new CoursePageReqVO().setCourseType(typeId).setCourseStatus(String.valueOf(CommonStatusEnum.ENABLE.getStatus())));
 
-        // 目前数据少先循环查询即可
-//        List<Long> idList = courseList.stream()
-//                .map(CoursePageReqVO::getId)
-//                .collect(Collectors.toList());
+        List<Long> idList = courseList.stream()
+                .map(CoursePageReqVO::getId)
+                .collect(Collectors.toList());
 
-        //优化需要走web端类
-//        webCourseMapper
+        // 获得课程配置-试题列表
+        List<CourseConfigRespVO> courseConfigQuestionList = courseConfigService.getCourseConfigQuestionByCourseIds(idList);
+        // 转换为 Map,以 ccCourseId 为键
+        Map<Long, List<CourseConfigRespVO>> configMap = courseConfigQuestionList.stream()
+                .collect(Collectors.groupingBy(CourseConfigRespVO::getCcCourseId));
 
+        //重新组装
         List<WebCourseVO> courseVoList = new ArrayList<>();
-
         for (CoursePageReqVO course : courseList) {
-            List<CourseConfigRespVO> courseConfigQuestion = courseConfigService.getCourseConfigQuestion(course.getId());
+            List<CourseConfigRespVO> courseConfigQuestion = configMap.get(course.getId());
             List<CourseConfigRespVO> list = new ArrayList<>();
             if (CollUtil.isNotEmpty(courseConfigQuestion)) {
-                // 根据 ccTime 升序排序(已满足需求
+                // 根据 ccTime 升序排序(提示时间
                 list = courseConfigQuestion.stream().sorted(Comparator.comparing(CourseConfigRespVO::getCcTime)).toList();
             }
             courseVoList.add(BeanUtils.toBean(course, WebCourseVO.class).setCourseConfigList(list));

+ 25 - 0
byzs-web/src/main/resources/mapper/blockly/WebBlocklyMapper.xml

@@ -0,0 +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="cn.iocoder.byzs.module.web.dal.mysql.blockly.WebBlocklyMapper">
+
+    <select id="getTypeByThemeId"
+            parameterType="cn.iocoder.byzs.module.web.controller.admin.blockly.vo.WebBlocklyTypeVO"
+            resultType="cn.iocoder.byzs.module.web.controller.admin.blockly.vo.WebBlocklyTypeVO">
+        SELECT
+            bct.id as id,
+            bct.ct_type as ctType,
+            bct.ct_parent_id as ctParentId,
+            bct.ct_type_image as ctTypeImage,
+            bct.ct_type_sort as ctTypeSort,
+            ROUND(COALESCE((SUM(brp.brp_progress) * 1.0 / NULLIF(COUNT(bct.ct_type) * 100, 0)) * 5, 0), 0) AS progress
+        FROM
+            blockly_course_type bct
+                LEFT JOIN blockly_report_progress brp ON bct.id = brp.brp_ct_id
+                AND bct.ct_parent_id = brp.brp_zt_id
+        WHERE
+            bct.ct_parent_id = #{ctParentId}
+              AND brp.brp_user_id = #{userId}
+              AND brp.brp_course_config_id IS NULL
+        GROUP BY id, ctType, ctParentId, ctTypeImage, ctTypeSort;
+    </select>
+</mapper>

+ 40 - 0
byzs-web/src/main/resources/mapper/blocklyprogress/BlocklyProgressMapper.xml

@@ -0,0 +1,40 @@
+<?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="cn.iocoder.byzs.module.web.dal.mysql.blocklyprogress.WebBlocklyProgressMapper">
+
+    <select id="selectProgres" parameterType="cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDO"
+            resultType="cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDTO">
+        SELECT
+            u.nickname as userName,
+            CONCAT(LPAD(fl.ct_type_sort, 2, '0'), ' ', fl.ct_type) as kcflName,
+            c.course_name as courseName,
+            rp.brp_progress as progress,
+            rp.brp_type as progressType
+        FROM blockly_report_progress rp
+        LEFT JOIN blockly_course_type fl ON rp.brp_ct_id =fl.id
+        LEFT JOIN blockly_course c ON rp.brp_course_id = c.id
+        LEFT JOIN system_users u ON rp.brp_user_id = u.id
+        <where>
+            <if test="brpUserId != null">
+                and rp.brp_user_id = #{brpUserId}
+            </if>
+            <if test="brpZtId != null">
+                AND rp.brp_zt_id = #{brpZtId}
+            </if>
+        </where>
+    </select>
+
+    <select id="getBlocklyProgressByTypeId"
+            parameterType="cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDO"
+            resultType="cn.iocoder.byzs.module.web.controller.admin.blocklyprogress.vo.WebBlocklyProgressDO">
+
+        SELECT
+            brp_course_id AS brpUserId,
+            COALESCE(ROUND(brp_progress / 100), 0) AS brpProgress
+        FROM blockly_report_progress
+        WHERE
+            brp_ct_id = #{brpCtId}
+            AND brp_user_id = #{brpUserId}
+            AND brp_course_config_id IS NULL
+    </select>
+</mapper>