Pārlūkot izejas kodu

1、新增blockly模块
2、配置blockly课程、类型(主题、类型)、配置等所有管理
3、

liyanbo 5 mēneši atpakaļ
vecāks
revīzija
7a8ea7854b
41 mainītis faili ar 2346 papildinājumiem un 7 dzēšanām
  1. 34 0
      byzs-blockly/pom.xml
  2. 105 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/BlocklyController.java
  3. 64 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/vo/BlocklyPageReqVO.java
  4. 85 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/vo/BlocklyRespVO.java
  5. 57 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/vo/BlocklySaveReqVO.java
  6. 111 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/BlocklyConfigController.java
  7. 49 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/vo/BlocklyConfigPageReqVO.java
  8. 65 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/vo/BlocklyConfigRespVO.java
  9. 43 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/vo/BlocklyConfigSaveReqVO.java
  10. 99 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/BlocklyTypeController.java
  11. 28 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/vo/BlocklyTypeListReqVO.java
  12. 41 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/vo/BlocklyTypeRespVO.java
  13. 33 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/vo/BlocklyTypeSaveReqVO.java
  14. 107 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/dataobject/blockly/BlocklyDO.java
  15. 84 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/dataobject/blocklyConfig/BlocklyConfigDO.java
  16. 59 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/dataobject/blocklyType/BlocklyTypeDO.java
  17. 35 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/mysql/blockly/BlocklyMapper.java
  18. 30 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/mysql/blocklyConfig/BlocklyConfigMapper.java
  19. 67 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/mysql/blocklyType/BlocklyTypeMapper.java
  20. 25 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/enums/ErrorCodeConstants.java
  21. 72 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blockly/BlocklyService.java
  22. 166 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blockly/BlocklyServiceImpl.java
  23. 72 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyConfig/BlocklyConfigService.java
  24. 156 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyConfig/BlocklyConfigServiceImpl.java
  25. 64 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyType/BlocklyTypeService.java
  26. 140 0
      byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyType/BlocklyTypeServiceImpl.java
  27. 63 0
      byzs-blockly/src/main/resources/mapper/blockly/BlocklyMapper.xml
  28. 56 0
      byzs-blockly/src/main/resources/mapper/blocklyConfig/BlocklyConfigMapper.xml
  29. 7 0
      byzs-blockly/src/main/resources/mapper/blocklyType/BlocklyTypeMapper.xml
  30. 6 0
      byzs-course/src/main/resources/mapper/course/CourseMapper.xml
  31. 0 2
      byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/dal/mysql/mapgame/MapGameMapper.java
  32. 0 1
      byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/service/mapgame/MapGameService.java
  33. 0 3
      byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/service/mapgame/MapGameServiceImpl.java
  34. 7 0
      byzs-server/pom.xml
  35. 5 0
      byzs-web/pom.xml
  36. 164 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/WebBlocklyController.java
  37. 61 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/blockly/vo/WebBlocklyVO.java
  38. 1 1
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/course/WebCourseController.java
  39. 22 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/dal/mysql/blockly/WebBlocklyMapper.java
  40. 62 0
      byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/blockly/WebBlocklyServiceImpl.java
  41. 1 0
      pom.xml

+ 34 - 0
byzs-blockly/pom.xml

@@ -0,0 +1,34 @@
+<?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"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>byzs-bjdx</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>byzs-blockly</artifactId>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>byzs-module-system</artifactId>
+            <version>${revision}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>byzs-module-infra</artifactId>
+            <version>${revision}</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 105 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/BlocklyController.java

@@ -0,0 +1,105 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blockly;
+
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyRespVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklySaveReqVO;
+import org.springframework.web.bind.annotation.*;
+import jakarta.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import jakarta.validation.*;
+import jakarta.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.byzs.framework.common.pojo.PageParam;
+import cn.iocoder.byzs.framework.common.pojo.PageResult;
+import cn.iocoder.byzs.framework.common.pojo.CommonResult;
+import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.byzs.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.byzs.framework.apilog.core.annotation.ApiAccessLog;
+
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.*;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blockly.BlocklyDO;
+import cn.iocoder.byzs.module.blockly.service.blockly.BlocklyService;
+import static cn.iocoder.byzs.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.byzs.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 课程")
+@RestController
+@RequestMapping("/blockly/blockly")
+@Validated
+public class BlocklyController {
+
+    @Resource
+    private BlocklyService blocklyService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建课程")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly:create')")
+    public CommonResult<Long> createBlockly(@Valid @RequestBody BlocklySaveReqVO createReqVO) {
+        return success(blocklyService.createBlockly(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新课程")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly:update')")
+    public CommonResult<Boolean> updateBlockly(@Valid @RequestBody BlocklySaveReqVO updateReqVO) {
+        blocklyService.updateBlockly(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除课程")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('blockly:blockly:delete')")
+    public CommonResult<Boolean> deleteBlockly(@RequestParam("id") Long id) {
+        blocklyService.deleteBlockly(id);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete-list")
+    @Parameter(name = "ids", description = "编号", required = true)
+    @Operation(summary = "批量删除课程")
+                @PreAuthorize("@ss.hasPermission('blockly:blockly:delete')")
+    public CommonResult<Boolean> deleteBlocklyList(@RequestParam("ids") List<Long> ids) {
+        blocklyService.deleteBlocklyListByIds(ids);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得课程")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly:query')")
+    public CommonResult<BlocklyRespVO> getBlockly(@RequestParam("id") Long id) {
+        BlocklyDO blockly = blocklyService.getBlockly(id);
+        return success(BeanUtils.toBean(blockly, BlocklyRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得课程分页")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly:query')")
+    public CommonResult<PageResult<BlocklyRespVO>> getBlocklyPage(@Valid BlocklyPageReqVO pageReqVO) {
+        PageResult<BlocklyRespVO> pageResult = blocklyService.getBlocklyPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, BlocklyRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出课程 Excel")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportBlocklyExcel(@Valid BlocklyPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<BlocklyRespVO> list = blocklyService.getBlocklyPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "课程.xls", "数据", BlocklyRespVO.class,list);
+    }
+
+}

+ 64 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/vo/BlocklyPageReqVO.java

@@ -0,0 +1,64 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.byzs.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 课程分页 Request VO")
+@Data
+public class BlocklyPageReqVO extends PageParam {
+
+    @Schema(description = "租户编号")
+    private Long tenantId;
+
+    @Schema(description = "课程id")
+    private Long id;
+
+    @Schema(description = "课程名称")
+    private String bcName;
+
+    @Schema(description = "课程内容类型")
+    private String bcContentType;
+
+    @Schema(description = "课程内容")
+    private String bcContent;
+
+    @ExcelProperty("简介")
+    private String blocklyInfo;
+    @ExcelProperty("人物图标")
+    private String blocklyUserImage;
+    @ExcelProperty("人物初始方向")
+    private Integer blocklyUserDirection;
+    @ExcelProperty("地图方格大小")
+    private String blocklyTileSize;
+    @ExcelProperty("地图背景图")
+    private String blocklyBackground;
+    @ExcelProperty("地图开始坐标")
+    private String blocklyStartPoint;
+    @ExcelProperty("地图结束坐标")
+    private String blocklyEndPoint;
+    @ExcelProperty("地图可行走坐标")
+    private String blocklyWalkablePoints;
+
+    @Schema(description = "课程是否有检查")
+    private String bcIsInspect;
+
+    @Schema(description = "课程类型id")
+    private Long bcType;
+
+    @Schema(description = "课程类型名称")
+    private String bcTypeName;
+
+
+
+    @Schema(description = "课程排序")
+    private Integer bcOrder;
+
+    @Schema(description = "课程标签")
+    private String bcLabel;
+
+    @Schema(description = "课程状态")
+    private String bcStatus;
+
+}

+ 85 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/vo/BlocklyRespVO.java

@@ -0,0 +1,85 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import com.alibaba.excel.annotation.*;
+import cn.iocoder.byzs.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.byzs.framework.excel.core.convert.DictConvert;
+
+@Schema(description = "管理后台 - 课程 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class BlocklyRespVO {
+
+    @Schema(description = "租户编号")
+    private Long tenantId;
+
+    @Schema(description = "课程d", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("课程d")
+    private Long id;
+
+    @Schema(description = "课程名称")
+    @ExcelProperty("课程名称")
+    private String bcName;
+
+    @Schema(description = "课程内容类型")
+    @ExcelProperty("课程内容类型")
+    private String bcContentType;
+
+    @Schema(description = "课程内容")
+    @ExcelProperty("课程内容")
+    private String bcContent;
+
+    @ExcelProperty("简介")
+    @Schema(description = "简介")
+    private String blocklyInfo;
+    @ExcelProperty("人物图标")
+    @Schema(description = "人物图标")
+    private String blocklyUserImage;
+    @ExcelProperty("人物初始方向")
+    @Schema(description = "人物初始方向")
+    private Integer blocklyUserDirection;
+    @ExcelProperty("地图方格大小")
+    @Schema(description = "地图方格大小")
+    private String blocklyTileSize;
+    @ExcelProperty("地图背景图")
+    @Schema(description = "地图背景图")
+    private String blocklyBackground;
+    @ExcelProperty("地图开始坐标")
+    @Schema(description = "地图开始坐标")
+    private String blocklyStartPoint;
+    @ExcelProperty("地图结束坐标")
+    @Schema(description = "地图结束坐标")
+    private String blocklyEndPoint;
+    @ExcelProperty("地图可行走坐标")
+    @Schema(description = "地图可行走坐标")
+    private String blocklyWalkablePoints;
+
+    @Schema(description = "课程是否有检查")
+    @ExcelProperty(value = "课程是否有检查", converter = DictConvert.class)
+    @DictFormat("infra_boolean_string")
+    private String bcIsInspect;
+
+    @Schema(description = "课程类型")
+    @ExcelProperty("课程类型")
+    private Long bcType;
+
+    @Schema(description = "课程类型")
+    @ExcelProperty("课程类型")
+    private String bcTypeName;
+
+    @Schema(description = "课程排序")
+    @ExcelProperty("课程排序")
+    private Integer bcOrder;
+
+    @Schema(description = "课程标签")
+    @ExcelProperty(value = "课程标签", converter = DictConvert.class)
+    @DictFormat("bc_course_label")
+    private String bcLabel;
+
+    @Schema(description = "课程状态")
+    @ExcelProperty(value = "课程状态", converter = DictConvert.class)
+    @DictFormat("common_status")
+    private String bcStatus;
+
+}

+ 57 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blockly/vo/BlocklySaveReqVO.java

@@ -0,0 +1,57 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import jakarta.validation.constraints.*;
+
+@Schema(description = "管理后台 - 课程新增/修改 Request VO")
+@Data
+public class BlocklySaveReqVO {
+
+    @Schema(description = "课程id", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Long id;
+
+    @Schema(description = "课程名称")
+    private String bcName;
+
+    @Schema(description = "课程内容类型")
+    private String bcContentType;
+
+    @Schema(description = "课程内容")
+    private String bcContent;
+
+    @ExcelProperty("简介")
+    private String blocklyInfo;
+    @ExcelProperty("人物图标")
+    private String blocklyUserImage;
+    @ExcelProperty("人物初始方向")
+    private Integer blocklyUserDirection;
+    @ExcelProperty("地图方格大小")
+    private String blocklyTileSize;
+    @ExcelProperty("地图背景图")
+    private String blocklyBackground;
+    @ExcelProperty("地图开始坐标")
+    private String blocklyStartPoint;
+    @ExcelProperty("地图结束坐标")
+    private String blocklyEndPoint;
+    @ExcelProperty("地图可行走坐标")
+    private String blocklyWalkablePoints;
+
+    @Schema(description = "课程是否有检查")
+    private String bcIsInspect;
+
+    @Schema(description = "课程类型")
+    private Long bcType;
+
+    @Schema(description = "课程标签")
+    private String bcLabel;
+
+    @Schema(description = "课程排序")
+    private Integer bcOrder;
+
+    @Schema(description = "课程状态")
+    private String bcStatus;
+
+}

+ 111 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/BlocklyConfigController.java

@@ -0,0 +1,111 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig;
+
+import cn.iocoder.byzs.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.byzs.framework.common.pojo.CommonResult;
+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.framework.excel.core.util.ExcelUtils;
+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;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyConfig.BlocklyConfigDO;
+import cn.iocoder.byzs.module.blockly.service.blocklyConfig.BlocklyConfigService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.byzs.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.byzs.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 课程配置")
+@RestController
+@RequestMapping("/blockly/blockly-config")
+@Validated
+public class BlocklyConfigController {
+
+    @Resource
+    private BlocklyConfigService blocklyConfigService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建课程配置")
+    @PreAuthorize("@ss.hasPermission('blockly:course-config:create')")
+    public CommonResult<Long>createBlocklyConfig(@Valid @RequestBody BlocklyConfigSaveReqVO createReqVO) {
+        return success(blocklyConfigService.createBlocklyConfig(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新课程配置")
+    @PreAuthorize("@ss.hasPermission('blockly:course-config:update')")
+    public CommonResult<Boolean> updateBlocklyConfig(@Valid @RequestBody BlocklyConfigSaveReqVO updateReqVO) {
+        blocklyConfigService.updateBlocklyConfig(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除课程配置")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('blockly:course-config:delete')")
+    public CommonResult<Boolean> deleteBlocklyConfig(@RequestParam("id") Long id) {
+        blocklyConfigService.deleteBlocklyConfig(id);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete-list")
+    @Parameter(name = "ids", description = "编号", required = true)
+    @Operation(summary = "批量删除课程配置")
+                @PreAuthorize("@ss.hasPermission('blockly:course-config:delete')")
+    public CommonResult<Boolean> deleteBlocklyConfigList(@RequestParam("ids") List<Long> ids) {
+        blocklyConfigService.deleteBlocklyConfigListByIds(ids);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得课程配置")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('blockly:course-config:query')")
+    public CommonResult<BlocklyConfigRespVO> getBlocklyConfig(@RequestParam("id") Long id) {
+        BlocklyConfigDO blocklyConfig = blocklyConfigService.getBlocklyConfig(id);
+        return success(BeanUtils.toBean(blocklyConfig, BlocklyConfigRespVO.class));
+    }
+
+    @GetMapping("/getConfigQuest")
+    @Operation(summary = "获得课程配置试题列表")
+    @Parameter(name = "ccCourseId", description = "课程id", required = true, example = "1025")
+    @PreAuthorize("@ss.hasPermission('blockly:course-config:query')")
+    public CommonResult<List<BlocklyConfigRespVO>> getBlocklyConfigQuestion(@RequestParam("ccCourseId") Long ccCourseId) {
+        List<BlocklyConfigRespVO> blocklyConfigList = blocklyConfigService.getBlocklyConfigQuestion(ccCourseId);
+        return success(blocklyConfigList);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得课程配置分页")
+    @PreAuthorize("@ss.hasPermission('blockly:course-config:query')")
+    public CommonResult<PageResult<BlocklyConfigPageReqVO>> getBlocklyConfigPage(@Valid BlocklyConfigPageReqVO pageReqVO) {
+        PageResult<BlocklyConfigPageReqVO> pageResult = blocklyConfigService.getBlocklyConfigPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, BlocklyConfigPageReqVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出课程配置 Excel")
+    @PreAuthorize("@ss.hasPermission('blockly:course-config:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportBlocklyConfigExcel(@Valid BlocklyConfigPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<BlocklyConfigPageReqVO> list = blocklyConfigService.getBlocklyConfigPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "课程配置.xls", "数据", BlocklyConfigPageReqVO.class,
+                        BeanUtils.toBean(list, BlocklyConfigPageReqVO.class));
+    }
+
+}

+ 49 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/vo/BlocklyConfigPageReqVO.java

@@ -0,0 +1,49 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo;
+
+import cn.iocoder.byzs.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 课程配置分页 Request VO")
+@Data
+public class BlocklyConfigPageReqVO extends PageParam {
+
+    @Schema(description = "租户编号")
+    private Long tenantId;
+
+    @Schema(description = "课程配置id")
+    private Integer id;
+
+    @Schema(description = "课程id")
+    private Integer ccCourseId;
+
+    @Schema(description = "课程名称")
+    private String courseName;
+
+    @Schema(description = "课程暂停时长")
+    private Double ccTime;
+
+    @Schema(description = "试题来源")
+    private String ccQuestSource;
+
+    @Schema(description = "试题内容")
+    private String ccQuestContent;
+
+    @Schema(description = "试题选项")
+    private String ccQuestOption;
+
+    @Schema(description = "ai问题提示")
+    private String ccAiQuestTip;
+
+    @Schema(description = "ai答案")
+    private String ccAiAnswer;
+
+    @Schema(description = "答案")
+    private String ccAnswer;
+
+    @Schema(description = "试题id")
+    private Integer ccQuestId;
+
+    @Schema(description = "是否显示答案")
+    private String ccAnswerJudge;
+}

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

@@ -0,0 +1,65 @@
+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 com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 课程配置 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class BlocklyConfigRespVO {
+
+    @Schema(description = "租户编号")
+    private Long tenantId;
+
+    @Schema(description = "课程配置id", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("课程配置id")
+    private Long id;
+
+    @Schema(description = "课程id")
+    @ExcelProperty("课程id")
+    private Long ccCourseId;
+
+    @Schema(description = "课程暂停时长")
+    @ExcelProperty("课程暂停时长")
+    private Integer ccTime;
+
+    @Schema(description = "试题来源")
+    @ExcelProperty("试题来源")
+    private String ccQuestSource;
+
+    @Schema(description = "试题内容")
+    @ExcelProperty("试题内容")
+    private String ccQuestContent;
+
+    @Schema(description = "试题选项")
+    @ExcelProperty("试题选项")
+    private String ccQuestOption;
+
+    @Schema(description = "ai问题提示")
+    private String ccAiQuestTip;
+
+    @Schema(description = "ai答案")
+    @ExcelProperty("ai答案")
+    private String ccAiAnswer;
+
+    @Schema(description = "答案")
+    @ExcelProperty("答案")
+    private String ccAnswer;
+
+    @Schema(description = "试题id")
+    @ExcelProperty("试题id")
+    private Long ccQuestId;
+
+    @Schema(description = "是否显示答案")
+    @ExcelProperty(value = "是否显示答案", converter = DictConvert.class)
+    @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+    private String ccAnswerJudge;
+
+//    @Schema(description = "课程-试题")
+//    private BlocklyQuestionRespVO blocklyQuestion;
+
+}

+ 43 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyConfig/vo/BlocklyConfigSaveReqVO.java

@@ -0,0 +1,43 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 课程配置新增/修改 Request VO")
+@Data
+public class BlocklyConfigSaveReqVO {
+
+    @Schema(description = "课程配置id", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Long id;
+
+    @Schema(description = "课程id")
+    private Integer ccCourseId;
+
+    @Schema(description = "课程暂停时长")
+    private Integer ccTime;
+
+    @Schema(description = "试题来源")
+    private String ccQuestSource;
+
+    @Schema(description = "试题内容")
+    private String ccQuestContent;
+
+    @Schema(description = "试题选项")
+    private String ccQuestOption;
+
+    @Schema(description = "ai问题提示")
+    private String ccAiQuestTip;
+
+    @Schema(description = "ai答案")
+    private String ccAiAnswer;
+
+    @Schema(description = "答案")
+    private String ccAnswer;
+
+    @Schema(description = "试题id")
+    private Integer ccQuestId;
+
+    @Schema(description = "是否显示答案")
+    private String ccAnswerJudge;
+
+}

+ 99 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/BlocklyTypeController.java

@@ -0,0 +1,99 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blocklyType;
+
+import cn.iocoder.byzs.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.byzs.framework.common.pojo.CommonResult;
+import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+import cn.iocoder.byzs.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeListReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeRespVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeSaveReqVO;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyType.BlocklyTypeDO;
+import cn.iocoder.byzs.module.blockly.service.blocklyType.BlocklyTypeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.byzs.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.byzs.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 课程-类型")
+@RestController
+@RequestMapping("/blockly/blockly-type")
+@Validated
+public class BlocklyTypeController {
+
+    @Resource
+    private BlocklyTypeService blocklyTypeService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建课程-类型")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly-type:create')")
+    public CommonResult<Long> createBlocklyType(@Valid @RequestBody BlocklyTypeSaveReqVO createReqVO) {
+        return success(blocklyTypeService.createBlocklyType(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新课程-类型")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly-type:update')")
+    public CommonResult<Boolean> updateBlocklyType(@Valid @RequestBody BlocklyTypeSaveReqVO updateReqVO) {
+        blocklyTypeService.updateBlocklyType(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除课程-类型")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('blockly:blockly-type:delete')")
+    public CommonResult<Boolean> deleteBlocklyType(@RequestParam("id") Long id) {
+        blocklyTypeService.deleteBlocklyType(id);
+        return success(true);
+    }
+
+
+    @GetMapping("/get")
+    @Operation(summary = "获得课程-类型")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly-type:query')")
+    public CommonResult<BlocklyTypeRespVO> getBlocklyType(@RequestParam("id") Long id) {
+        BlocklyTypeDO blocklyType = blocklyTypeService.getBlocklyType(id);
+        return success(BeanUtils.toBean(blocklyType, BlocklyTypeRespVO.class));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "获得课程-类型列表")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly-type:query')")
+    public CommonResult<List<BlocklyTypeRespVO>> getBlocklyTypeList(@Valid BlocklyTypeListReqVO listReqVO) {
+        List<BlocklyTypeDO> list = blocklyTypeService.getBlocklyTypeList(listReqVO);
+        return success(BeanUtils.toBean(list, BlocklyTypeRespVO.class));
+    }
+
+    @GetMapping("/simple-list")
+    @Operation(summary = "获得课程-类型列表")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly-type:query')")
+    public CommonResult<List<BlocklyTypeRespVO>> getBlocklyTypeSimpleList(@Valid BlocklyTypeListReqVO listReqVO) {
+        List<BlocklyTypeDO> list = blocklyTypeService.getBlocklyTypeSimpleList(listReqVO);
+        return success(BeanUtils.toBean(list, BlocklyTypeRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出课程-类型 Excel")
+    @PreAuthorize("@ss.hasPermission('blockly:blockly-type:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportBlocklyTypeExcel(@Valid BlocklyTypeListReqVO listReqVO,
+              HttpServletResponse response) throws IOException {
+        List<BlocklyTypeDO> list = blocklyTypeService.getBlocklyTypeList(listReqVO);
+        // 导出 Excel
+        ExcelUtils.write(response, "课程-类型.xls", "数据", BlocklyTypeRespVO.class,
+                        BeanUtils.toBean(list, BlocklyTypeRespVO.class));
+    }
+
+}

+ 28 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/vo/BlocklyTypeListReqVO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(description = "管理后台 - 课程-类型列表 Request VO")
+@Data
+public class BlocklyTypeListReqVO {
+
+    @Schema(description = "课程类型名称")
+    private String ctType;
+
+    @Schema(description = "课程类型封面")
+    private String ctTypeImage;
+
+    @Schema(description = "课程类型父级id")
+    private Integer ctParentId;
+
+    @Schema(description = "课程节点")
+    private String ctTypeNode;
+
+    @Schema(description = "课程排序")
+    private Integer ctTypeSort;
+
+    @Schema(description = "课程类型描述")
+    private String ctTypeDescribe;
+
+}

+ 41 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/vo/BlocklyTypeRespVO.java

@@ -0,0 +1,41 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 课程-类型 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class BlocklyTypeRespVO {
+
+    @Schema(description = "租户编号")
+    private Long tenantId;
+
+    @Schema(description = "课程类型id", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("课程类型id")
+    private Long id;
+
+    @Schema(description = "课程类型名称")
+    @ExcelProperty("课程类型名称")
+    private String ctType;
+
+    @Schema(description = "课程类型封面")
+    private String ctTypeImage;
+
+    @Schema(description = "课程类型父级id")
+    @ExcelProperty("课程类型父级id")
+    private Integer ctParentId;
+
+    @Schema(description = "课程节点")
+    private String ctTypeNode;
+
+    @Schema(description = "课程排序")
+    private Integer ctTypeSort;
+
+    @Schema(description = "课程类型描述")
+    @ExcelProperty("课程类型描述")
+    private String ctTypeDescribe;
+
+}

+ 33 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/controller/admin/blocklyType/vo/BlocklyTypeSaveReqVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import jakarta.validation.constraints.*;
+
+@Schema(description = "管理后台 - 课程-类型新增/修改 Request VO")
+@Data
+public class BlocklyTypeSaveReqVO {
+
+    @Schema(description = "课程类型id", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Long id;
+
+    @Schema(description = "课程类型名称")
+    private String ctType;
+
+    @Schema(description = "课程类型封面")
+    private String ctTypeImage;
+
+    @Schema(description = "课程类型父级id")
+    private Long ctParentId;
+
+    @Schema(description = "课程节点")
+    private String ctTypeNode;
+
+    @Schema(description = "课程排序")
+    private Integer ctTypeSort;
+
+    @Schema(description = "课程类型描述")
+    private String ctTypeDescribe;
+
+}

+ 107 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/dataobject/blockly/BlocklyDO.java

@@ -0,0 +1,107 @@
+package cn.iocoder.byzs.module.blockly.dal.dataobject.blockly;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.byzs.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 课程 DO
+ *
+ * @author lyb
+ */
+@TableName("blockly_course")
+@KeySequence("blockly_course_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BlocklyDO extends BaseDO {
+
+    /**
+     * 租户编号
+     */
+    private Long tenantId;
+
+    /**
+     * 课程d
+     */
+    @TableId
+    private Long id;
+    /**
+     * 课程名称
+     */
+    private String bcName;
+
+    /**
+     * 课程内容类型
+     */
+    private String bcContentType;
+
+    /**
+     * 课程内容
+     */
+    private String bcContent;
+
+    /**
+     * 简介
+     */
+    private String blocklyInfo;
+    /**
+     * 人物图标
+     */
+    private String blocklyUserImage;
+    /**
+     * 人物初始方向
+     */
+    private Integer blocklyUserDirection;
+    /**
+     * 地图方格大小
+     */
+    private String blocklyTileSize;
+    /**
+     * 地图背景图
+     */
+    private String blocklyBackground;
+    /**
+     * 地图开始坐标
+     */
+    private String blocklyStartPoint;
+    /**
+     * 地图结束坐标
+     */
+    private String blocklyEndPoint;
+    /**
+     * 地图可行走坐标
+     */
+    private String blocklyWalkablePoints;
+
+    /**
+     * 课程是否有检查
+     *
+     */
+    private String bcIsInspect;
+    /**
+     * 课程类型
+     */
+    private Long bcType;
+    /**
+     * 课程标签
+     *
+     */
+    private String bcLabel;
+
+    /**
+     * 课程排序
+     */
+    private Integer bcOrder;
+    /**
+     * 课程状态
+     *
+     */
+    private String bcStatus;
+
+
+}

+ 84 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/dataobject/blocklyConfig/BlocklyConfigDO.java

@@ -0,0 +1,84 @@
+package cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyConfig;
+
+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.*;
+
+/**
+ * 课程配置 DO
+ *
+ * @author 博雅智算源码
+ */
+@TableName("bjdx_course_config")
+@KeySequence("bjdx_course_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BlocklyConfigDO extends BaseDO {
+
+    /**
+     * 租户编号
+     */
+    private Long tenantId;
+
+    /**
+     * 课程配置id
+     */
+    @TableId
+    private Long id;
+    /**
+     * 课程id
+     */
+    private Long ccCourseId;
+    /**
+     * 课程暂停时长
+     */
+    private Integer ccTime;
+
+    /**
+     * 试题来源
+     */
+    private String ccQuestSource;
+
+    /**
+     * 试题内容
+     */
+    private String ccQuestContent;
+
+    /**
+     * 试题选项
+     */
+    private String ccQuestOption;
+
+    /**
+     * ai问题提示
+     */
+    private String ccAiQuestTip;
+
+    /**
+     * ai答案
+     */
+    private String ccAiAnswer;
+
+    /**
+     * 答案
+     */
+    private String ccAnswer;
+    /**
+     * 试题id
+     */
+    private Integer ccQuestId;
+    /**
+     * 是否显示答案
+     *
+     * 枚举 {@link TODO infra_boolean_string 对应的类}
+     */
+    private String ccAnswerJudge;
+
+
+}

+ 59 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/dataobject/blocklyType/BlocklyTypeDO.java

@@ -0,0 +1,59 @@
+package cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyType;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.byzs.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 课程-类型 DO
+ *
+ * @author lyb
+ */
+@TableName("blockly_course_type")
+@KeySequence("blockly_course_type_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class BlocklyTypeDO extends BaseDO {
+
+    public static final Long CT_PARENT_ID_ROOT = 0L;
+
+    /**
+     * 课程类型id
+     */
+    @TableId
+    private Long id;
+    /**
+     * 课程类型名称
+     */
+    private String ctType;
+    /**
+     * 课程类型封面
+     */
+    private String ctTypeImage;
+    /**
+     * 课程类型父级id
+     */
+    private Long ctParentId;
+    /**
+     * 课程节点
+     */
+    private String ctTypeNode;
+    /**
+     * 课程排序
+     */
+    private Integer ctTypeSort;
+    /**
+     * 课程类型描述
+     */
+    private String ctTypeDescribe;
+    /**
+     * 租户id
+     */
+    private Long tenantId;
+
+}

+ 35 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/mysql/blockly/BlocklyMapper.java

@@ -0,0 +1,35 @@
+package cn.iocoder.byzs.module.blockly.dal.mysql.blockly;
+
+import cn.iocoder.byzs.framework.common.pojo.PageResult;
+import cn.iocoder.byzs.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.byzs.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blockly.BlocklyDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 课程 Mapper
+ *
+ * @author lyb
+ */
+@Mapper
+public interface BlocklyMapper extends BaseMapperX<BlocklyDO> {
+
+    default PageResult<BlocklyDO> selectPage(BlocklyPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<BlocklyDO>()
+                .likeIfPresent(BlocklyDO::getBcName, reqVO.getBcName())
+                .eqIfPresent(BlocklyDO::getBcContentType, reqVO.getBcContentType())
+                .eqIfPresent(BlocklyDO::getBcContent, reqVO.getBcContent())
+                .eqIfPresent(BlocklyDO::getBcIsInspect, reqVO.getBcIsInspect())
+                .eqIfPresent(BlocklyDO::getBcType, reqVO.getBcType())
+                .eqIfPresent(BlocklyDO::getBcLabel, reqVO.getBcLabel())
+                .eqIfPresent(BlocklyDO::getBcStatus, reqVO.getBcStatus())
+                .orderByAsc(BlocklyDO::getBcLabel, BlocklyDO::getBcOrder));
+    }
+
+    List<BlocklyPageReqVO> selectBlocklyPage(BlocklyPageReqVO reqVO);
+    List<BlocklyPageReqVO> selectBlocklyList(BlocklyPageReqVO reqVO);
+    int selectBlocklyPageCount(BlocklyPageReqVO reqVO);
+}

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

@@ -0,0 +1,30 @@
+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.dal.dataobject.blocklyConfig.BlocklyConfigDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 课程配置 Mapper
+ *
+ * @author 博雅智算源码
+ */
+@Mapper
+public interface BlocklyConfigMapper extends BaseMapperX<BlocklyConfigDO> {
+
+//    default PageResult<BlocklyConfigDO> selectPage(BlocklyConfigPageReqVO reqVO) {
+//        return selectPage(reqVO, new LambdaQueryWrapperX<BlocklyConfigDO>()
+//                .eqIfPresent(BlocklyConfigDO::getCcCourseId, reqVO.getCcCourseId())
+//                .eqIfPresent(BlocklyConfigDO::getCcTime, reqVO.getCcTime())
+//                .eqIfPresent(BlocklyConfigDO::getCcQuestId, reqVO.getCcQuestId())
+//                .eqIfPresent(BlocklyConfigDO::getCcAnswerJudge, reqVO.getCcAnswerJudge())
+//                .orderByDesc(BlocklyConfigDO::getId));
+//    }
+
+    List<BlocklyConfigPageReqVO> selectBlocklyConfigPage(BlocklyConfigPageReqVO reqVO);
+    Integer selectBlocklyConfigCount(BlocklyConfigPageReqVO reqVO);
+
+}

+ 67 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/dal/mysql/blocklyType/BlocklyTypeMapper.java

@@ -0,0 +1,67 @@
+package cn.iocoder.byzs.module.blockly.dal.mysql.blocklyType;
+
+import java.util.*;
+
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeListReqVO;
+import cn.iocoder.byzs.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.byzs.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyType.BlocklyTypeDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.*;
+
+/**
+ * 课程-类型 Mapper
+ *
+ * @author lyb
+ */
+@Mapper
+public interface BlocklyTypeMapper extends BaseMapperX<BlocklyTypeDO> {
+
+    default List<BlocklyTypeDO> selectList(BlocklyTypeListReqVO reqVO) {
+        LambdaQueryWrapperX<BlocklyTypeDO> queryWrapper = getBlocklyTypeDOLambdaQueryWrapperX(reqVO);
+
+        // 使用BaseMapperX的selectList方法执行查询
+        return selectList(queryWrapper);
+    }
+
+    default List<BlocklyTypeDO> selectSimpleList(BlocklyTypeListReqVO reqVO) {
+        LambdaQueryWrapperX<BlocklyTypeDO> queryWrapper = getBlocklyTypeDOLambdaQueryWrapperX(reqVO);
+
+        // 使用BaseMapperX的selectList方法执行查询
+        return selectList(queryWrapper.select(BlocklyTypeDO::getId, BlocklyTypeDO::getCtParentId, BlocklyTypeDO::getCtType, BlocklyTypeDO::getCtTypeNode, BlocklyTypeDO::getCtTypeSort));
+    }
+
+    private static LambdaQueryWrapperX<BlocklyTypeDO> getBlocklyTypeDOLambdaQueryWrapperX(BlocklyTypeListReqVO reqVO) {
+        LambdaQueryWrapperX<BlocklyTypeDO> queryWrapper = new LambdaQueryWrapperX<BlocklyTypeDO>()
+                .likeIfPresent(BlocklyTypeDO::getCtType, reqVO.getCtType())
+                .eqIfPresent(BlocklyTypeDO::getCtParentId, reqVO.getCtParentId());
+
+        // 处理getCtTypeNode的特殊条件:不为空且不等于0时,同时查询该值和0
+        Object ctTypeNode = reqVO.getCtTypeNode();
+        if (ctTypeNode != null && !ctTypeNode.toString().equals("0")) {
+            queryWrapper.and(wrapper -> wrapper
+                    .eq(BlocklyTypeDO::getCtTypeNode, ctTypeNode)
+                    .or()
+                    .eq(BlocklyTypeDO::getCtTypeNode, 0)
+            );
+        } else {
+            // 保持原有的eqIfPresent行为
+            queryWrapper.eqIfPresent(BlocklyTypeDO::getCtTypeNode, ctTypeNode);
+        }
+
+        // 完成构建queryWrapper并添加剩余条件
+        queryWrapper
+                .likeIfPresent(BlocklyTypeDO::getCtTypeDescribe, reqVO.getCtTypeDescribe())
+                .orderByAsc(BlocklyTypeDO::getCtTypeNode, BlocklyTypeDO::getCtTypeSort);
+        return queryWrapper;
+    }
+
+	default BlocklyTypeDO selectByCtParentIdAndCtType(Long ctParentId, String ctType) {
+	    return selectOne(BlocklyTypeDO::getCtParentId, ctParentId, BlocklyTypeDO::getCtType, ctType);
+	}
+
+    default Long selectCountByCtParentId(Long ctParentId) {
+        return selectCount(BlocklyTypeDO::getCtParentId, ctParentId);
+    }
+
+}

+ 25 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/enums/ErrorCodeConstants.java

@@ -0,0 +1,25 @@
+package cn.iocoder.byzs.module.blockly.enums;
+
+import cn.iocoder.byzs.framework.common.exception.ErrorCode;
+
+/**
+ * 课程 错误码枚举类
+ * <p>
+ * 课程,使用 1-018-000-000 段
+ */
+public interface ErrorCodeConstants {
+    // ========== 课程 1_018_000_000 ==========
+    ErrorCode BLOCKLY_NOT_EXISTS = new ErrorCode(1_018_000_000, "课程不存在");
+
+    // ========== 课程-类型 1_018_010_000 ==========
+    ErrorCode BLOCKLY_TYPE_NOT_EXISTS = new ErrorCode(1_018_010_001, "-课程-类型不存在");
+    ErrorCode BLOCKLY_TYPE_EXITS_CHILDREN = new ErrorCode(1_018_010_002, "存在存在子课程-类型,无法删除");
+    ErrorCode BLOCKLY_TYPE_PARENT_NOT_EXITS = new ErrorCode(1_018_010_003, "父级课程-类型不存在");
+    ErrorCode BLOCKLY_TYPE_PARENT_ERROR = new ErrorCode(1_018_010_004, "不能设置自己为父课程-类型");
+    ErrorCode BLOCKLY_TYPE_CT_TYPE_DUPLICATE = new ErrorCode(1_018_010_005, "已经存在该课程类型名称的课程-类型");
+    ErrorCode BLOCKLY_TYPE_PARENT_IS_CHILD = new ErrorCode(1_018_010_006, "不能设置自己的子BlocklyType为父BlocklyType");
+
+
+    // ========== 课程配置 1_019_020_000 ==========
+    ErrorCode BLOCKLY_CONFIG_NOT_EXISTS = new ErrorCode(1_019_020_000, "课程配置不存在");
+}

+ 72 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blockly/BlocklyService.java

@@ -0,0 +1,72 @@
+package cn.iocoder.byzs.module.blockly.service.blockly;
+
+import java.util.*;
+
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklySaveReqVO;
+import jakarta.validation.*;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.*;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blockly.BlocklyDO;
+import cn.iocoder.byzs.framework.common.pojo.PageResult;
+
+/**
+ * 课程 Service 接口
+ *
+ * @author lyb
+ */
+public interface BlocklyService {
+
+    /**
+     * 创建课程
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createBlockly(@Valid BlocklySaveReqVO createReqVO);
+
+    /**
+     * 更新课程
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateBlockly(@Valid BlocklySaveReqVO updateReqVO);
+
+    /**
+     * 删除课程
+     *
+     * @param id 编号
+     */
+    void deleteBlockly(Long id);
+
+    /**
+    * 批量删除课程
+    *
+    * @param ids 编号
+    */
+    void deleteBlocklyListByIds(List<Long> ids);
+
+    /**
+     * 获得课程
+     *
+     * @param id 编号
+     * @return 课程
+     */
+    BlocklyDO getBlockly(Long id);
+
+    /**
+     * 获得课程分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 课程分页
+     */
+    PageResult<BlocklyRespVO> getBlocklyPage(BlocklyPageReqVO pageReqVO);
+
+    /**
+     * 获得课程
+     *
+     * @param pageReqVO 查询
+     * @return 课程分页
+     */
+    List<BlocklyPageReqVO> getBlocklyList(BlocklyPageReqVO pageReqVO);
+
+}

+ 166 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blockly/BlocklyServiceImpl.java

@@ -0,0 +1,166 @@
+package cn.iocoder.byzs.module.blockly.service.blockly;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.iocoder.byzs.framework.common.pojo.PageResult;
+import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyRespVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklySaveReqVO;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blockly.BlocklyDO;
+import cn.iocoder.byzs.module.blockly.dal.mysql.blockly.BlocklyMapper;
+import cn.iocoder.byzs.module.infra.dal.dataobject.file.FileDO;
+import cn.iocoder.byzs.module.infra.framework.file.core.client.FileClient;
+import cn.iocoder.byzs.module.infra.service.file.FileConfigService;
+import cn.iocoder.byzs.module.infra.service.file.FileServiceImpl;
+import jakarta.annotation.Resource;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.byzs.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.byzs.module.blockly.enums.ErrorCodeConstants.*;
+
+/**
+ * 课程 Service 实现类
+ *
+ * @author lyb
+ */
+@Service
+@Validated
+public class BlocklyServiceImpl implements BlocklyService {
+
+    @Resource
+    private BlocklyMapper blocklyMapper;
+    @Resource
+    private FileServiceImpl fileService;
+    @Resource
+    private FileConfigService fileConfigService;
+
+    @Override
+    public Long createBlockly(BlocklySaveReqVO createReqVO) {
+        // 插入
+        BlocklyDO blockly = BeanUtils.toBean(createReqVO, BlocklyDO.class);
+        blocklyMapper.insert(blockly);
+
+        if (blockly.getBcContentType().equals("video")) {
+            getSelf().asymcVideoFfmpeg(blockly);
+        }
+        // 返回
+        return blockly.getId();
+    }
+
+    @Override
+    public void updateBlockly(BlocklySaveReqVO updateReqVO) {
+        // 校验存在
+        validateBlocklyExists(updateReqVO.getId());
+        // 更新
+        BlocklyDO updateObj = BeanUtils.toBean(updateReqVO, BlocklyDO.class);
+        blocklyMapper.updateById(updateObj);
+
+        if (updateObj.getBcContentType().equals("video")) {
+            getSelf().asymcVideoFfmpeg(updateObj);
+        }
+    }
+
+    @Async
+    public void asymcVideoFfmpeg(BlocklyDO blockly) {
+        //只对mp4文件进行处理
+        String blocklyVideoPath = blockly.getBcContent();
+        String ext = blocklyVideoPath.substring(blocklyVideoPath.lastIndexOf("."));
+        if (StrUtil.isBlank(ext) || ext.equals(".m3u8")) {
+            return;
+        }
+
+        //获取相对文件路径
+        FileDO fileDO= fileService.getFilePathByUrl(blockly.getBcContent());
+        if (fileDO == null || fileDO.getPath() == null) {
+            return;
+        }
+
+        //切片
+        FileClient client = fileConfigService.getMasterFileClient();
+        String m3u8Path = client.ffmpegHts(fileDO.getPath());
+        if(StrUtil.isNotEmpty(m3u8Path)){
+            blocklyMapper.updateById(new BlocklyDO().setId(blockly.getId()).setBcContent(m3u8Path));
+            fileDO.setUrl(m3u8Path);
+            fileService.updateFileById(fileDO);
+
+            // 从文件存储器中删除【暂时不删除mp4文件,删除时需要同步更改fileDo中path的相对路径更改成m3u8文件】
+//            try {
+//                client.delete(blocklyVideoPath);
+//            } catch (Exception e) {
+//                throw new RuntimeException(e);
+//            }
+        }
+    }
+
+    @Override
+    public void deleteBlockly(Long id) {
+        // 校验存在
+        validateBlocklyExists(id);
+        // 删除
+        blocklyMapper.deleteById(id);
+    }
+
+    @Override
+        public void deleteBlocklyListByIds(List<Long> ids) {
+        // 校验存在
+        validateBlocklyExists(ids);
+        // 删除
+        blocklyMapper.deleteByIds(ids);
+        }
+
+    private void validateBlocklyExists(List<Long> ids) {
+        List<BlocklyDO> list = blocklyMapper.selectByIds(ids);
+        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
+            throw exception(BLOCKLY_NOT_EXISTS);
+        }
+    }
+
+    private void validateBlocklyExists(Long id) {
+        if (blocklyMapper.selectById(id) == null) {
+            throw exception(BLOCKLY_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public BlocklyDO getBlockly(Long id) {
+        return blocklyMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<BlocklyRespVO> getBlocklyPage(BlocklyPageReqVO pageReqVO) {
+        pageReqVO.setPageNo((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize());
+        List<BlocklyPageReqVO> blocklyPageReqVOS = blocklyMapper.selectBlocklyPage(pageReqVO);
+
+        // 获取总记录数
+        int total = blocklyMapper.selectBlocklyPageCount(pageReqVO);
+        // 调用服务层方法获取分页结果
+        return new PageResult<>(
+                blocklyPageReqVOS.stream()
+                        .map(doItem -> BeanUtils.toBean(doItem, BlocklyRespVO.class))
+                        .collect(Collectors.toList()),
+                (long) total
+        );
+    }
+
+    @Override
+    public List<BlocklyPageReqVO> getBlocklyList(BlocklyPageReqVO pageReqVO) {
+        return blocklyMapper.selectBlocklyList(pageReqVO);
+    }
+
+    /**
+     * 获得自身的代理对象,解决 AOP 生效问题
+     *
+     * @return 自己
+     */
+    private BlocklyServiceImpl getSelf() {
+        return SpringUtil.getBean(getClass());
+    }
+
+}

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

@@ -0,0 +1,72 @@
+package cn.iocoder.byzs.module.blockly.service.blocklyConfig;
+
+import cn.iocoder.byzs.framework.common.pojo.PageResult;
+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;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyConfig.BlocklyConfigDO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * 课程配置 Service 接口
+ *
+ * @author 博雅智算源码
+ */
+public interface BlocklyConfigService {
+
+    /**
+     * 创建课程配置
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createBlocklyConfig(@Valid BlocklyConfigSaveReqVO createReqVO);
+
+    /**
+     * 更新课程配置
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateBlocklyConfig(@Valid BlocklyConfigSaveReqVO updateReqVO);
+
+    /**
+     * 删除课程配置
+     *
+     * @param id 编号
+     */
+    void deleteBlocklyConfig(Long id);
+
+    /**
+    * 批量删除课程配置
+    *
+    * @param ids 编号
+    */
+    void deleteBlocklyConfigListByIds(List<Long> ids);
+
+    /**
+     * 获得课程配置
+     *
+     * @param id 编号
+     * @return 课程配置
+     */
+    BlocklyConfigDO getBlocklyConfig(Long id);
+
+    /**
+     * 获得课程配置-试题列表
+     *
+     * @param ccCourseId 课程id
+     * @return 课程配置
+     */
+    List<BlocklyConfigRespVO> getBlocklyConfigQuestion(Long ccCourseId);
+
+    /**
+     * 获得课程配置分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 课程配置分页
+     */
+    PageResult<BlocklyConfigPageReqVO> getBlocklyConfigPage(BlocklyConfigPageReqVO pageReqVO);
+
+}

+ 156 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyConfig/BlocklyConfigServiceImpl.java

@@ -0,0 +1,156 @@
+package cn.iocoder.byzs.module.blockly.service.blocklyConfig;
+
+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.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;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyConfig.BlocklyConfigDO;
+import cn.iocoder.byzs.module.blockly.dal.mysql.blocklyConfig.BlocklyConfigMapper;
+import cn.iocoder.byzs.module.blockly.service.blocklyConfig.BlocklyConfigService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.byzs.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.byzs.module.blockly.enums.ErrorCodeConstants.BLOCKLY_CONFIG_NOT_EXISTS;
+
+/**
+ * 课程配置 Service 实现类
+ *
+ * @author 博雅智算源码
+ */
+@Service
+@Validated
+public class BlocklyConfigServiceImpl implements BlocklyConfigService {
+
+    @Resource
+    private BlocklyConfigMapper blocklyConfigMapper;
+//    @Resource
+//    private CourseQuestionMapper courseQuestionMapper;
+//    @Resource
+//    private CourseQuestOptionMapper courseQuestOptionMapper;
+
+    @Override
+    public Long createBlocklyConfig(BlocklyConfigSaveReqVO createReqVO) {
+        // 插入
+        BlocklyConfigDO blocklyConfig = BeanUtils.toBean(createReqVO, BlocklyConfigDO.class);
+        blocklyConfigMapper.insert(blocklyConfig);
+        // 返回
+        return blocklyConfig.getId();
+    }
+
+    @Override
+    public void updateBlocklyConfig(BlocklyConfigSaveReqVO updateReqVO) {
+        // 校验存在
+        validateBlocklyConfigExists(updateReqVO.getId());
+        // 更新
+        BlocklyConfigDO updateObj = BeanUtils.toBean(updateReqVO, BlocklyConfigDO.class);
+        blocklyConfigMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteBlocklyConfig(Long id) {
+        // 校验存在
+        validateBlocklyConfigExists(id);
+        // 删除
+        blocklyConfigMapper.deleteById(id);
+    }
+
+    @Override
+        public void deleteBlocklyConfigListByIds(List<Long> ids) {
+        // 校验存在
+        validateBlocklyConfigExists(ids);
+        // 删除
+        blocklyConfigMapper.deleteByIds(ids);
+        }
+
+    private void validateBlocklyConfigExists(List<Long> ids) {
+        List<BlocklyConfigDO> list = blocklyConfigMapper.selectByIds(ids);
+        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
+            throw exception(BLOCKLY_CONFIG_NOT_EXISTS);
+        }
+    }
+
+    private void validateBlocklyConfigExists(Long id) {
+        if (blocklyConfigMapper.selectById(id) == null) {
+            throw exception(BLOCKLY_CONFIG_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public BlocklyConfigDO getBlocklyConfig(Long id) {
+        return blocklyConfigMapper.selectById(id);
+    }
+
+    @Override
+    public List<BlocklyConfigRespVO> getBlocklyConfigQuestion(Long ccCourseId) {
+        //课程配置列表
+        BlocklyConfigPageReqVO blocklyConfigPageReqVO = new BlocklyConfigPageReqVO();
+        blocklyConfigPageReqVO.setCcCourseId(Math.toIntExact(ccCourseId))
+                .setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<BlocklyConfigPageReqVO> blocklyConfigPageReqVOS = blocklyConfigMapper.selectBlocklyConfigPage(blocklyConfigPageReqVO);
+
+        //课程配置列表转VO
+        List<BlocklyConfigRespVO> blocklyConfigList = blocklyConfigPageReqVOS.stream()
+                .map(doItem -> BeanUtils.toBean(doItem, BlocklyConfigRespVO.class))
+                .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));
+
+        }
+
+        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 PageResult<BlocklyConfigPageReqVO> getBlocklyConfigPage(BlocklyConfigPageReqVO pageReqVO) {
+        pageReqVO.setPageNo((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize());
+        List<BlocklyConfigPageReqVO> blocklyConfigPageReqVOS = blocklyConfigMapper.selectBlocklyConfigPage(pageReqVO);
+        Integer count = blocklyConfigMapper.selectBlocklyConfigCount(pageReqVO);
+
+        // 调用服务层方法获取分页结果
+        return new PageResult<>(
+                blocklyConfigPageReqVOS.stream()
+                       .map(doItem -> BeanUtils.toBean(doItem, BlocklyConfigPageReqVO.class))
+                       .collect(Collectors.toList()),
+                (long) count
+        );
+    }
+
+}

+ 64 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyType/BlocklyTypeService.java

@@ -0,0 +1,64 @@
+package cn.iocoder.byzs.module.blockly.service.blocklyType;
+
+import java.util.*;
+
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeListReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeSaveReqVO;
+import jakarta.validation.*;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyType.BlocklyTypeDO;
+
+/**
+ * 课程-类型 Service 接口
+ *
+ * @author lyb
+ */
+public interface BlocklyTypeService {
+
+    /**
+     * 创建课程-类型
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createBlocklyType(@Valid BlocklyTypeSaveReqVO createReqVO);
+
+    /**
+     * 更新课程-类型
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateBlocklyType(@Valid BlocklyTypeSaveReqVO updateReqVO);
+
+    /**
+     * 删除课程-类型
+     *
+     * @param id 编号
+     */
+    void deleteBlocklyType(Long id);
+
+
+    /**
+     * 获得课程-类型
+     *
+     * @param id 编号
+     * @return 课程-类型
+     */
+    BlocklyTypeDO getBlocklyType(Long id);
+
+    /**
+     * 获得课程-类型列表
+     *
+     * @param listReqVO 查询条件
+     * @return 课程-类型列表
+     */
+    List<BlocklyTypeDO> getBlocklyTypeList(BlocklyTypeListReqVO listReqVO);
+
+    /**
+     * 获得课程-类型列表
+     *
+     * @param listReqVO 查询条件
+     * @return 课程-类型列表
+     */
+    List<BlocklyTypeDO> getBlocklyTypeSimpleList(BlocklyTypeListReqVO listReqVO);
+
+}

+ 140 - 0
byzs-blockly/src/main/java/cn/iocoder/byzs/module/blockly/service/blocklyType/BlocklyTypeServiceImpl.java

@@ -0,0 +1,140 @@
+package cn.iocoder.byzs.module.blockly.service.blocklyType;
+
+import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeListReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeSaveReqVO;
+import cn.iocoder.byzs.module.blockly.dal.dataobject.blocklyType.BlocklyTypeDO;
+import cn.iocoder.byzs.module.blockly.dal.mysql.blocklyType.BlocklyTypeMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+import java.util.Objects;
+
+import static cn.iocoder.byzs.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.byzs.module.blockly.enums.ErrorCodeConstants.*;
+
+/**
+ * 课程-类型 Service 实现类
+ *
+ * @author lyb
+ */
+@Service
+@Validated
+public class BlocklyTypeServiceImpl implements BlocklyTypeService {
+
+    @Resource
+    private BlocklyTypeMapper blocklyTypeMapper;
+
+    @Override
+    public Long createBlocklyType(BlocklyTypeSaveReqVO createReqVO) {
+        // 校验课程类型父级id的有效性
+        validateParentBlocklyType(null, createReqVO.getCtParentId());
+        // 校验课程类型名称的唯一性
+        validateBlocklyTypeCtTypeUnique(null, createReqVO.getCtParentId(), createReqVO.getCtType());
+
+        // 插入
+        BlocklyTypeDO blocklyType = BeanUtils.toBean(createReqVO, BlocklyTypeDO.class);
+        blocklyTypeMapper.insert(blocklyType);
+        // 返回
+        return blocklyType.getId();
+    }
+
+    @Override
+    public void updateBlocklyType(BlocklyTypeSaveReqVO updateReqVO) {
+        // 校验存在
+        validateBlocklyTypeExists(updateReqVO.getId());
+        // 校验课程类型父级id的有效性
+        validateParentBlocklyType(updateReqVO.getId(), updateReqVO.getCtParentId());
+        // 校验课程类型名称的唯一性
+        validateBlocklyTypeCtTypeUnique(updateReqVO.getId(), updateReqVO.getCtParentId(), updateReqVO.getCtType());
+
+        // 更新
+        BlocklyTypeDO updateObj = BeanUtils.toBean(updateReqVO, BlocklyTypeDO.class);
+        blocklyTypeMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteBlocklyType(Long id) {
+        // 校验存在
+        validateBlocklyTypeExists(id);
+        // 校验是否有子课程-类型
+        if (blocklyTypeMapper.selectCountByCtParentId(id) > 0) {
+            throw exception(BLOCKLY_TYPE_EXITS_CHILDREN);
+        }
+        // 删除
+        blocklyTypeMapper.deleteById(id);
+    }
+
+
+    private void validateBlocklyTypeExists(Long id) {
+        if (blocklyTypeMapper.selectById(id) == null) {
+            throw exception(BLOCKLY_TYPE_NOT_EXISTS);
+        }
+    }
+
+    private void validateParentBlocklyType(Long id, Long ctParentId) {
+        if (ctParentId == null || BlocklyTypeDO.CT_PARENT_ID_ROOT.equals(ctParentId)) {
+            return;
+        }
+        // 1. 不能设置自己为父课程-类型
+        if (Objects.equals(id, ctParentId)) {
+            throw exception(BLOCKLY_TYPE_PARENT_ERROR);
+        }
+        // 2. 父课程-类型不存在
+        BlocklyTypeDO parentBlocklyType = blocklyTypeMapper.selectById(ctParentId);
+        if (parentBlocklyType == null) {
+            throw exception(BLOCKLY_TYPE_PARENT_NOT_EXITS);
+        }
+        // 3. 递归校验父课程-类型,如果父课程-类型是自己的子课程-类型,则报错,避免形成环路
+        if (id == null) { // id 为空,说明新增,不需要考虑环路
+            return;
+        }
+        for (int i = 0; i < Short.MAX_VALUE; i++) {
+            // 3.1 校验环路
+            ctParentId = parentBlocklyType.getCtParentId();
+            if (Objects.equals(id, ctParentId)) {
+                throw exception(BLOCKLY_TYPE_PARENT_IS_CHILD);
+            }
+            // 3.2 继续递归下一级父课程-类型
+            if (ctParentId == null || BlocklyTypeDO.CT_PARENT_ID_ROOT.equals(ctParentId)) {
+                break;
+            }
+            parentBlocklyType = blocklyTypeMapper.selectById(ctParentId);
+            if (parentBlocklyType == null) {
+                break;
+            }
+        }
+    }
+
+    private void validateBlocklyTypeCtTypeUnique(Long id, Long ctParentId, String ctType) {
+        BlocklyTypeDO blocklyType = blocklyTypeMapper.selectByCtParentIdAndCtType(ctParentId, ctType);
+        if (blocklyType == null) {
+            return;
+        }
+        // 如果 id 为空,说明不用比较是否为相同 id 的课程-类型
+        if (id == null) {
+            throw exception(BLOCKLY_TYPE_CT_TYPE_DUPLICATE);
+        }
+        if (!Objects.equals(blocklyType.getId(), id)) {
+            throw exception(BLOCKLY_TYPE_CT_TYPE_DUPLICATE);
+        }
+    }
+
+    @Override
+    public BlocklyTypeDO getBlocklyType(Long id) {
+        return blocklyTypeMapper.selectById(id);
+    }
+
+    @Override
+    public List<BlocklyTypeDO> getBlocklyTypeList(BlocklyTypeListReqVO listReqVO) {
+        return blocklyTypeMapper.selectList(listReqVO);
+    }
+
+    @Override
+    public List<BlocklyTypeDO> getBlocklyTypeSimpleList(BlocklyTypeListReqVO listReqVO) {
+        return blocklyTypeMapper.selectSimpleList(listReqVO);
+    }
+
+}

+ 63 - 0
byzs-blockly/src/main/resources/mapper/blockly/BlocklyMapper.xml

@@ -0,0 +1,63 @@
+<?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.blockly.dal.mysql.blockly.BlocklyMapper">
+
+    <sql id="selectBlocklyList">
+        FROM blockly_course c
+        LEFT JOIN blockly_course_type ct ON c.bc_type = ct.id
+        <where>
+            c.deleted = 0
+            <if test="bcName != null and bcName != ''">
+                AND c.bc_name LIKE CONCAT('%', #{bcName}, '%')
+            </if>
+            <if test="bcStatus!= null and bcStatus!= ''">
+                AND c.bc_status = #{bcStatus}
+            </if>
+            <if test="bcType!= null and bcType!= ''">
+                AND c.bc_type = #{bcType}
+            </if>
+            <if test="bcLabel!= null and bcLabel!= ''">
+                AND c.bc_label = #{bcLabel}
+            </if>
+            <if test="bcContentType!= null and bcContentType!= ''">
+                AND c.bc_content_type = #{bcContentType}
+            </if>
+            <if test="bcIsInspect!= null and bcIsInspect!= ''">
+                AND c.bc_is_inspect = #{bcIsInspect}
+            </if>
+        </where>
+        ORDER BY ct.ct_type_sort, c.bc_order
+    </sql>
+    <select id="selectBlocklyPageCount" parameterType="cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO"
+            resultType="int">
+        SELECT COUNT(*)
+        <include refid="selectBlocklyList"/>
+    </select>
+    <select id="selectBlocklyPage" parameterType="cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO"
+            resultType="cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO">
+        SELECT c.tenant_id as tenantId,c.id, c.bc_name bcName,
+               c.bc_content_type bcContentType, c.bc_content bcContent,
+               c.blockly_info blocklyInfo, c.blockly_user_image blocklyUserImage, c.blockly_user_direction blocklyUserDirection,
+               c.blockly_tile_size blocklyTileSize, c.blockly_background blocklyBackground,
+               c.blockly_start_point blocklyStartPoint, c.blockly_end_point blocklyEndPoint,
+               c.blockly_walkable_points blocklyWalkablePoints,
+               c.bc_is_inspect bcIsInspect, c.bc_label bcLabel, c.bc_order bcOrder, c.bc_status bcStatus,
+               ct.ct_type bcTypeName
+        <include refid="selectBlocklyList"/>
+        <if test="pageSize != null and pageSize != -1">
+            limit #{pageNo}, #{pageSize}
+        </if>
+    </select>
+    <select id="selectBlocklyList" parameterType="cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO"
+            resultType="cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO">
+        SELECT c.tenant_id as tenantId,c.id, c.bc_name bcName, c.bc_type bcType,
+               c.bc_content_type bcContentType, c.bc_content bcContent,
+               c.blockly_info blocklyInfo, c.blockly_user_image blocklyUserImage, c.blockly_user_direction blocklyUserDirection,
+               c.blockly_tile_size blocklyTileSize, c.blockly_background blocklyBackground,
+               c.blockly_start_point blocklyStartPoint, c.blockly_end_point blocklyEndPoint,
+               c.blockly_walkable_points blocklyWalkablePoints,
+               c.bc_is_inspect bcIsInspect, c.bc_label bcLabel, c.bc_order bcOrder, c.bc_status bcStatus,
+               ct.ct_type bcTypeName
+        <include refid="selectBlocklyList"/>
+    </select>
+</mapper>

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

@@ -0,0 +1,56 @@
+<?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.blockly.dal.mysql.blocklyConfig.BlocklyConfigMapper">
+
+
+    <select id="selectBlocklyConfigPage" parameterType="cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigPageReqVO" resultType="cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigPageReqVO">
+        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>
+                c.deleted = 0 and cc.deleted = 0
+            <if test="ccCourseId != null and ccCourseId != ''">
+                AND cc.cc_course_id  = #{ccCourseId}
+            </if>
+            <if test="ccQuestContent != null and ccQuestContent != ''">
+                AND cc.cc_quest_content LIKE CONCAT('%', #{ccQuestContent}, '%')
+            </if>
+            <if test="ccTime != null and ccTime != ''">
+                AND cc.cc_time LIKE CONCAT('%', #{ccTime}, '%')
+            </if>
+            <if test="ccAnswerJudge != null and ccAnswerJudge != ''">
+                AND cc.cc_answer_judge = #{ccAnswerJudge}
+            </if>
+        </where>
+        <if test="pageSize != null and pageSize != -1">
+            limit #{pageNo}, #{pageSize}
+        </if>
+    </select>
+    <select id="selectBlocklyConfigCount" parameterType="cn.iocoder.byzs.module.blockly.controller.admin.blocklyConfig.vo.BlocklyConfigPageReqVO" resultType="java.lang.Integer">
+        SELECT count(*)
+        FROM bjdx_course_config cc
+        LEFT JOIN bjdx_course c ON cc.cc_course_id = c.id
+        <where>
+            c.deleted = 0 and cc.deleted = 0
+            <if test="ccCourseId != null and ccCourseId != ''">
+                AND cc.cc_course_id  = #{ccCourseId}
+            </if>
+            <if test="ccQuestContent != null and ccQuestContent != ''">
+                AND cc.cc_quest_content LIKE CONCAT('%', #{ccQuestContent}, '%')
+            </if>
+            <if test="ccTime != null and ccTime != ''">
+                AND cc.cc_time LIKE CONCAT('%', #{ccTime}, '%')
+            </if>
+            <if test="ccAnswerJudge != null and ccAnswerJudge != ''">
+                AND cc.cc_answer_judge = #{ccAnswerJudge}
+            </if>
+        </where>
+    </select>
+</mapper>

+ 7 - 0
byzs-blockly/src/main/resources/mapper/blocklyType/BlocklyTypeMapper.xml

@@ -0,0 +1,7 @@
+<?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.blockly.dal.mysql.blocklyType.BlocklyTypeMapper">
+
+
+
+</mapper>

+ 6 - 0
byzs-course/src/main/resources/mapper/course/CourseMapper.xml

@@ -19,9 +19,15 @@
             <if test="courseType!= null and courseType!= ''">
                 AND c.course_type = #{courseType}
             </if>
+            <if test="courseLabel!= null and courseLabel!= ''">
+                AND c.course_label = #{courseLabel}
+            </if>
             <if test="courseContentType!= null and courseContentType!= ''">
                 AND c.course_content_type = #{courseContentType}
             </if>
+            <if test="courseIsInspect!= null and courseIsInspect!= ''">
+                AND c.course_is_inspect = #{courseIsInspect}
+            </if>
         </where>
         ORDER BY ct.ct_type_sort, c.course_order
     </sql>

+ 0 - 2
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/dal/mysql/mapgame/MapGameMapper.java

@@ -1,7 +1,5 @@
 package cn.iocoder.byzs.module.ai.dal.mysql.mapgame;
 
-import java.util.*;
-
 import cn.iocoder.byzs.framework.common.pojo.PageResult;
 import cn.iocoder.byzs.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.byzs.framework.mybatis.core.mapper.BaseMapperX;

+ 0 - 1
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/service/mapgame/MapGameService.java

@@ -5,7 +5,6 @@ import jakarta.validation.*;
 import cn.iocoder.byzs.module.ai.controller.admin.mapgame.vo.*;
 import cn.iocoder.byzs.module.ai.dal.dataobject.mapgame.MapGameDO;
 import cn.iocoder.byzs.framework.common.pojo.PageResult;
-import cn.iocoder.byzs.framework.common.pojo.PageParam;
 
 /**
  * AI-blockly地图编程游戏 Service 接口

+ 0 - 3
byzs-module-ai/src/main/java/cn/iocoder/byzs/module/ai/service/mapgame/MapGameServiceImpl.java

@@ -4,20 +4,17 @@ import cn.hutool.core.collection.CollUtil;
 import org.springframework.stereotype.Service;
 import jakarta.annotation.Resource;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
 import cn.iocoder.byzs.module.ai.controller.admin.mapgame.vo.*;
 import cn.iocoder.byzs.module.ai.dal.dataobject.mapgame.MapGameDO;
 import cn.iocoder.byzs.framework.common.pojo.PageResult;
-import cn.iocoder.byzs.framework.common.pojo.PageParam;
 import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
 
 import cn.iocoder.byzs.module.ai.dal.mysql.mapgame.MapGameMapper;
 
 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.framework.common.util.collection.CollectionUtils.diffList;
 import static cn.iocoder.byzs.module.ai.enums.ErrorCodeConstants.*;
 
 /**

+ 7 - 0
byzs-server/pom.xml

@@ -45,6 +45,13 @@
             <version>${revision}</version>
         </dependency>
 
+        <!-- 北京大学课件管理 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>byzs-blockly</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
         <!-- WEB -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>

+ 5 - 0
byzs-web/pom.xml

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

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

@@ -0,0 +1,164 @@
+package cn.iocoder.byzs.module.web.controller.admin.blockly;
+
+import cn.iocoder.byzs.framework.common.pojo.CommonResult;
+import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+import cn.iocoder.byzs.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.byzs.module.blockly.controller.admin.blockly.vo.BlocklyPageReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeListReqVO;
+import cn.iocoder.byzs.module.blockly.controller.admin.blocklyType.vo.BlocklyTypeRespVO;
+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.WebBlocklyVO;
+import cn.iocoder.byzs.module.web.service.blockly.WebBlocklyServiceImpl;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.annotation.security.PermitAll;
+import lombok.Data;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.byzs.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "Web-前端接口")
+@RestController
+@RequestMapping("/bjdxWeb/blockly")
+@Validated
+public class WebBlocklyController {
+
+    @Resource
+    private BlocklyService blocklyService;
+    @Resource
+    private WebBlocklyServiceImpl webBlocklyService;
+    @Resource
+    private BlocklyTypeService blocklyTypeService;
+
+    @GetMapping("/getTypeTheme")
+    @Operation(summary = "获得blockly主题")
+    @TenantIgnore
+    @PermitAll
+    public CommonResult<List<BlocklyTypeRespVO>> getTypeTheme() {
+        BlocklyTypeListReqVO listReqVO = new BlocklyTypeListReqVO().setCtTypeNode("0");
+        List<BlocklyTypeDO> list = blocklyTypeService.getBlocklyTypeList(listReqVO);
+        List<BlocklyTypeRespVO> listVo = BeanUtils.toBean(list, BlocklyTypeRespVO.class);
+        return success(listVo);
+    }
+
+    @GetMapping("/getTypeByThemeId")
+    @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);
+    }
+
+    @GetMapping("/getBlocklyByTypeId")
+    @Operation(summary = "获得blockly课程-根据课程类型id")
+    @TenantIgnore
+    @PermitAll
+    public CommonResult<List<WebBlocklyVO>> getBlocklyByTypeId(@RequestParam("typeId") Long typeId) {
+        List<WebBlocklyVO> blocklyVoList = webBlocklyService.getBlocklyVoByTypeId(typeId);
+        return success(blocklyVoList);
+    }
+
+    @GetMapping("/getBlocklyTypeTree")
+    @Operation(summary = "获取课程类型树结构(配置权限使用)")
+    @TenantIgnore
+    @PermitAll
+    public CommonResult<List<TreeNode>> getBlocklyTypeTree() {
+        // 获取所有课程类型
+        List<BlocklyTypeDO> allBlocklyTypes = blocklyTypeService.getBlocklyTypeList(new BlocklyTypeListReqVO());
+        // 获取所有课程
+        List<BlocklyPageReqVO> allBlocklys = blocklyService.getBlocklyList(new BlocklyPageReqVO());
+        
+        // 构建课程类型ID到课程类型的映射
+        Map<Long, BlocklyTypeDO> blocklyTypeMap = new HashMap<>();
+        for (BlocklyTypeDO blocklyType : allBlocklyTypes) {
+            blocklyTypeMap.put(blocklyType.getId(), blocklyType);
+        }
+        
+        // 构建课程类型树节点
+        List<TreeNode> treeNodes = new ArrayList<>();
+        
+        // 处理根节点 (parentId = 0)
+        for (BlocklyTypeDO blocklyType : allBlocklyTypes) {
+            if (BlocklyTypeDO.CT_PARENT_ID_ROOT.equals(blocklyType.getCtParentId())) {
+                TreeNode node = new TreeNode();
+                node.setId(blocklyType.getId());
+                node.setParentId(blocklyType.getCtParentId());
+                node.setName(blocklyType.getCtType() + "(年级)");
+                node.setType("年级");
+                treeNodes.add(node);
+            }
+        }
+        
+        // 递归添加子节点
+        for (TreeNode node : treeNodes) {
+            addChildNodes(node, allBlocklyTypes, allBlocklys, blocklyTypeMap);
+        }
+        
+        return success(treeNodes);
+    }
+    
+    /**
+     * 递归添加子节点
+     */
+    private void addChildNodes(TreeNode parentNode, List<BlocklyTypeDO> allBlocklyTypes, 
+                             List<BlocklyPageReqVO> allBlocklys, Map<Long, BlocklyTypeDO> blocklyTypeMap) {
+        List<TreeNode> children = new ArrayList<>();
+        
+        // 添加子课程类型
+        for (BlocklyTypeDO blocklyType : allBlocklyTypes) {
+            if (parentNode.getId().equals(blocklyType.getCtParentId())) {
+                TreeNode childNode = new TreeNode();
+                childNode.setId(blocklyType.getId());
+                childNode.setParentId(blocklyType.getCtParentId());
+                String nodeType = blocklyType.getCtTypeNode().equals("1") ? "通识课" : "实操课";
+                childNode.setName(blocklyType.getCtType() + "(类型)");
+                childNode.setType(nodeType);
+                children.add(childNode);
+                
+                // 递归添加子节点的子节点
+                addChildNodes(childNode, allBlocklyTypes, allBlocklys, blocklyTypeMap);
+            }
+        }
+        
+        // 添加关联的课程
+        for (BlocklyPageReqVO blockly : allBlocklys) {
+            if (parentNode.getId().equals(blockly.getBcType())) {
+                TreeNode childNode = new TreeNode();
+                childNode.setId(blockly.getId()*100);
+                childNode.setParentId(blockly.getBcType());
+                childNode.setName(blockly.getBcName());
+                childNode.setType("blockly");
+                children.add(childNode);
+            }
+        }
+        
+        parentNode.setChildren(children);
+    }
+    
+    /**
+     * 树节点类,用于表示课程类型树结构
+     */
+    @Data
+    public static class TreeNode {
+        private Long id;
+        private Long parentId;
+        private String name;
+        private String type; // blocklyType 或 blockly
+        private List<TreeNode> children = new ArrayList<>();
+    }
+}

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

@@ -0,0 +1,61 @@
+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 io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - 课程分页 Request VO")
+@Data
+public class WebBlocklyVO extends PageParam {
+
+    @Schema(description = "课程id")
+    private Long id;
+
+    @Schema(description = "课程名称")
+    private String bcName;
+
+    @Schema(description = "课程内容类型")
+    private String bcContentType;
+
+    @Schema(description = "课程内容")
+    private String bcContent;
+
+
+    @Schema(description = "简介")
+    private String blocklyInfo;
+    @Schema(description = "人物图标")
+    private String blocklyUserImage;
+    @Schema(description = "人物初始方向")
+    private Integer blocklyUserDirection;
+    @Schema(description = "地图方格大小")
+    private String blocklyTileSize;
+    @Schema(description = "地图背景图")
+    private String blocklyBackground;
+    @Schema(description = "地图开始坐标")
+    private String blocklyStartPoint;
+    @Schema(description = "地图结束坐标")
+    private String blocklyEndPoint;
+    @Schema(description = "地图可行走坐标")
+    private String blocklyWalkablePoints;
+
+    @Schema(description = "课程是否有检查")
+    private String bcIsInspect;
+
+    @Schema(description = "课程类型id")
+    private Long bcType;
+
+    @Schema(description = "课程类型名称")
+    private String bcTypeName;
+
+    @Schema(description = "课程标签")
+    private String bcLabel;
+
+    @Schema(description = "课程状态")
+    private String bcStatus;
+
+    private List<BlocklyConfigRespVO> blocklyConfigList;
+
+}

+ 1 - 1
byzs-web/src/main/java/cn/iocoder/byzs/module/web/controller/admin/course/WebCourseController.java

@@ -78,7 +78,7 @@ public class WebCourseController {
     }
 
     @GetMapping("/getCourseTypeTree")
-    @Operation(summary = "获取课程类型树结构")
+    @Operation(summary = "获取课程类型树结构(配置权限使用)")
     @TenantIgnore
     @PermitAll
     public CommonResult<List<TreeNode>> getCourseTypeTree() {

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

@@ -0,0 +1,22 @@
+package cn.iocoder.byzs.module.web.dal.mysql.blockly;
+
+import cn.iocoder.byzs.module.bjdx.controller.admin.course.vo.CoursePageReqVO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * web课程 Mapper
+ *
+ * @author lyb
+ */
+@Mapper
+public interface WebBlocklyMapper {
+    /**
+     * 查询课程列表
+     *
+     * @param reqVO 分页查询参数
+     * @return 课程列表
+     */
+    List<CoursePageReqVO> selectCourse(CoursePageReqVO reqVO);
+}

+ 62 - 0
byzs-web/src/main/java/cn/iocoder/byzs/module/web/service/blockly/WebBlocklyServiceImpl.java

@@ -0,0 +1,62 @@
+package cn.iocoder.byzs.module.web.service.blockly;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.byzs.framework.common.util.object.BeanUtils;
+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.web.controller.admin.blockly.vo.WebBlocklyVO;
+import cn.iocoder.byzs.module.web.dal.mysql.blockly.WebBlocklyMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * web课程 Service 实现类
+ *
+ * @author lyb
+ */
+@Service
+@Validated
+public class WebBlocklyServiceImpl {
+
+    @Resource
+    private BlocklyService blocklyService;
+    @Resource
+    private BlocklyConfigService blocklyConfigService;
+    @Resource
+    private WebBlocklyMapper webBlocklyMapper;
+
+
+    public List<WebBlocklyVO> getBlocklyVoByTypeId(Long typeId) {
+        List<BlocklyPageReqVO> blocklyList = blocklyService.getBlocklyList(new BlocklyPageReqVO().setBcType(typeId));
+
+        // 目前数据少先循环查询即可
+//        List<Long> idList = blocklyList.stream()
+//                .map(BlocklyPageReqVO::getId)
+//                .collect(Collectors.toList());
+
+        //优化需要走web端类
+//        webBlocklyMapper
+
+        List<WebBlocklyVO> blocklyVoList = new ArrayList<>();
+
+        for (BlocklyPageReqVO blockly : blocklyList) {
+            List<BlocklyConfigRespVO> blocklyConfigQuestion = blocklyConfigService.getBlocklyConfigQuestion(blockly.getId());
+            List<BlocklyConfigRespVO> list = new ArrayList<>();
+            if (CollUtil.isNotEmpty(blocklyConfigQuestion)) {
+                // 根据 ccTime 升序排序(已满足需求)
+                list = blocklyConfigQuestion.stream().sorted(Comparator.comparing(BlocklyConfigRespVO::getCcTime)).toList();
+            }
+            blocklyVoList.add(BeanUtils.toBean(blockly, WebBlocklyVO.class).setBlocklyConfigList(list));
+        }
+
+        return blocklyVoList;
+    }
+
+}

+ 1 - 0
pom.xml

@@ -20,6 +20,7 @@
 
         <module>byzs-course</module>
         <module>byzs-web</module>
+        <module>byzs-blockly</module>
     </modules>
 
     <name>${project.artifactId}</name>