94781 字
约 315 分钟
50
servlet:
无标签

附录D 内容管理服务模块源码详解

模块概览(面向 0 基础读者):

  • 负责课程信息、分类、教学计划、课程发布管理。
  • 拆分为 content-api、content-service、content-model 三层。
  • 集成 MyBatis-Plus、Feign、XXL-Job、FreeMarker 实现课程发布全流程。

核心目录:

  • xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api
  • xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service
  • xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/feignclient
  • xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model
  • xuecheng-plus-content/xuecheng-plus-content-api/src/main/resources/templates

文件:xuecheng-plus-content/api/com/xuecheng/content/api/CourseBaseInfoController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.base.exception.ValidationGroups; // 导入 ValidationGroups 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.model.dto.AddCourseDto; // 导入 AddCourseDto 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.EditCourseDto; // 导入 EditCourseDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import io.swagger.annotations.Api; // 导入 Api 类
import io.swagger.annotations.ApiOperation; // 导入 ApiOperation 类
import org.apache.commons.lang.StringUtils; // 导入 StringUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.security.access.prepost.PreAuthorize; // 导入 PreAuthorize 类
import org.springframework.security.core.context.SecurityContextHolder; // 导入 SecurityContextHolder 类
import org.springframework.validation.annotation.Validated; // 导入 Validated 类
import org.springframework.web.bind.annotation.*; // 导入 * 类

import java.time.LocalDateTime; // 导入 LocalDateTime 类
import java.util.ArrayList; // 导入 ArrayList 类
import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/11 15:44 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Api(value = "课程信息管理接口",tags = "课程信息管理接口") // 使用注解 @Api(value = "课程信息管理接口",tags = "课程信息管理接口")
@RestController // 使用注解 @RestController
public class CourseBaseInfoController { // 定义 class CourseBaseInfoController

    @Autowired // 使用注解 @Autowired
    CourseBaseInfoService courseBaseInfoService; // 执行语句

    @ApiOperation("课程分页查询接口") // 使用注解 @ApiOperation("课程分页查询接口")
    @PreAuthorize("hasAuthority('xc_teachmanager_course_list')")//指定权限标识符,拥有此权限才可以访问此方法 // 使用注解 @PreAuthorize("hasAuthority('xc_teachmanager_course_list')")
    @PostMapping("/course/list") // 使用注解 @PostMapping("/course/list")
    public PageResult<CourseBase> list(PageParams pageParams, @RequestBody(required=false) QueryCourseParamsDto queryCourseParamsDto) { // 定义方法 list(...)

        //当前登录用户 // 保留原作者写的行内注释
        SecurityUtil.XcUser user = SecurityUtil.getUser(); // 把 SecurityUtil.getUser() 的返回值赋给 user
        //用户所属机构id // 保留原作者写的行内注释
        Long companyId = null; // 把变量 companyId 初始化为 null
        if(StringUtils.isNotEmpty(user.getCompanyId())){ // 定义方法 方法(...)
            companyId = Long.parseLong(user.getCompanyId()); // 把 Long.parseLong(user.getCompanyId()) 赋值给 companyId
        } // 代码块结束
        PageResult<CourseBase> courseBasePageResult = courseBaseInfoService.queryCourseBaseList(companyId,pageParams, queryCourseParamsDto); // 把 courseBaseInfoService.queryCourseBaseList(companyId,pageParams, queryCourseParamsDto) 赋值给 courseBasePageResult
        return courseBasePageResult; // 返回 courseBasePageResult

    } // 代码块结束
    @ApiOperation("新增课程") // 使用注解 @ApiOperation("新增课程")
    @PostMapping("/course") // 使用注解 @PostMapping("/course")
    public CourseBaseInfoDto createCourseBase(@RequestBody @Validated(ValidationGroups.Inster.class) AddCourseDto addCourseDto){ // 定义方法 createCourseBase(...)

        //获取到用户所属机构的id // 保留原作者写的行内注释
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
//        int i = 1/0; // 保留原作者写的行内注释
        CourseBaseInfoDto courseBase = courseBaseInfoService.createCourseBase(companyId, addCourseDto); // 把 courseBaseInfoService.createCourseBase(companyId, addCourseDto) 赋值给 courseBase
        return courseBase; // 返回 courseBase
    } // 代码块结束

    @ApiOperation("根据课程id查询接口") // 使用注解 @ApiOperation("根据课程id查询接口")
    @GetMapping("/course/{courseId}") // 使用注解 @GetMapping("/course/{courseId}")
    public CourseBaseInfoDto getCourseBaseById(@PathVariable Long courseId){ // 定义方法 getCourseBaseById(...)
        //获取当前用户的身份 // 保留原作者写的行内注释
//        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); // 保留原作者写的行内注释
//        System.out.println(principal); // 保留原作者写的行内注释
        SecurityUtil.XcUser user = SecurityUtil.getUser(); // 把 SecurityUtil.getUser() 的返回值赋给 user
        System.out.println(user.getUsername()); // 执行语句
        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId); // 把 courseBaseInfoService.getCourseBaseInfo(courseId) 赋值给 courseBaseInfo
        return courseBaseInfo; // 返回 courseBaseInfo
    } // 代码块结束


    @ApiOperation("修改课程") // 使用注解 @ApiOperation("修改课程")
    @PutMapping("/course") // 使用注解 @PutMapping("/course")
    public CourseBaseInfoDto modifyCourseBase(@RequestBody @Validated(ValidationGroups.Update.class) EditCourseDto editCourseDto){ // 定义方法 modifyCourseBase(...)
        //获取到用户所属机构的id // 保留原作者写的行内注释
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
        CourseBaseInfoDto courseBaseInfoDto = courseBaseInfoService.updateCourseBase(companyId, editCourseDto); // 把 courseBaseInfoService.updateCourseBase(companyId, editCourseDto) 赋值给 courseBaseInfoDto
        return courseBaseInfoDto; // 返回 courseBaseInfoDto
    } // 代码块结束



} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/api/CourseCategoryController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.service.CourseCategoryService; // 导入 CourseCategoryService 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.bind.annotation.RestController; // 导入 RestController 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程分类相关接口 // 保留原作者写的块注释
 * @date 2023/2/12 11:54 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@RestController // 使用注解 @RestController
public class CourseCategoryController { // 定义 class CourseCategoryController

    @Autowired // 使用注解 @Autowired
    CourseCategoryService courseCategoryService; // 执行语句


    @GetMapping("/course-category/tree-nodes") // 使用注解 @GetMapping("/course-category/tree-nodes")
    public List<CourseCategoryTreeDto> queryTreeNodes() { // 定义方法 queryTreeNodes(...)
        return courseCategoryService.queryTreeNodes("1"); // 返回 courseCategoryService.queryTreeNodes("1")
    } // 代码块结束

} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/api/CourseOpenController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.bind.annotation.PathVariable; // 导入 PathVariable 类
import org.springframework.web.bind.annotation.RequestMapping; // 导入 RequestMapping 类
import org.springframework.web.bind.annotation.RestController; // 导入 RestController 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/21 10:33 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
// 获取课程信息接口 // 保留原作者写的行内注释
@RestController // 使用注解 @RestController
@RequestMapping("/open") // 使用注解 @RequestMapping("/open")
public class CourseOpenController { // 定义 class CourseOpenController

    @Autowired // 使用注解 @Autowired
    private CourseBaseInfoService courseBaseInfoService; // 执行语句

    @Autowired // 使用注解 @Autowired
    private CoursePublishService coursePublishService; // 执行语句

    //根据课程id查询课程信息 // 保留原作者写的行内注释
    @GetMapping("/course/whole/{courseId}") // 使用注解 @GetMapping("/course/whole/{courseId}")
    public CoursePreviewDto getPreviewInfo(@PathVariable("courseId") Long courseId) { // 定义方法 getPreviewInfo(...)
        //获取课程预览信息 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId); // 把 coursePublishService.getCoursePreviewInfo(courseId) 赋值给 coursePreviewInfo
        return coursePreviewInfo; // 返回 coursePreviewInfo
    } // 代码块结束



} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/api/CoursePublishController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.alibaba.fastjson.JSON; // 导入 JSON 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import io.swagger.annotations.ApiOperation; // 导入 ApiOperation 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Controller; // 导入 Controller 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.bind.annotation.PathVariable; // 导入 PathVariable 类
import org.springframework.web.bind.annotation.PostMapping; // 导入 PostMapping 类
import org.springframework.web.bind.annotation.ResponseBody; // 导入 ResponseBody 类
import org.springframework.web.servlet.ModelAndView; // 导入 ModelAndView 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布相关接口 // 保留原作者写的块注释
 * @date 2023/2/21 9:28 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Controller // 使用注解 @Controller
public class CoursePublishController { // 定义 class CoursePublishController

    @Autowired // 使用注解 @Autowired
    CoursePublishService coursePublishService; // 执行语句


    @ApiOperation("获取课程发布信息") // 使用注解 @ApiOperation("获取课程发布信息")
    @ResponseBody // 使用注解 @ResponseBody
    @GetMapping("/course/whole/{courseId}") // 使用注解 @GetMapping("/course/whole/{courseId}")
    public CoursePreviewDto getCoursePublish(@PathVariable("courseId") Long courseId) { // 定义方法 getCoursePublish(...)
        //封装数据 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewDto = new CoursePreviewDto(); // 创建 CoursePreviewDto 对象并赋值给 coursePreviewDto

        //查询课程发布表 // 保留原作者写的行内注释
//        CoursePublish coursePublish = coursePublishService.getCoursePublish(courseId); // 保留原作者写的行内注释
        //先从缓存查询,缓存中有直接返回,没有再查询数据库 // 保留原作者写的行内注释
        CoursePublish coursePublish = coursePublishService.getCoursePublishCache(courseId); // 把 coursePublishService.getCoursePublishCache(courseId) 赋值给 coursePublish
        if(coursePublish == null){ // 定义方法 方法(...)
            return coursePreviewDto; // 返回 coursePreviewDto
        } // 代码块结束
        //开始向coursePreviewDto填充数据 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto(); // 创建 CourseBaseInfoDto 对象并赋值给 courseBaseInfoDto
        BeanUtils.copyProperties(coursePublish,courseBaseInfoDto); // 执行语句
        //课程计划信息 // 保留原作者写的行内注释
        String teachplanJson = coursePublish.getTeachplan(); // 把 coursePublish.getTeachplan() 的返回值赋给 teachplanJson
        //转成List<TeachplanDto> // 保留原作者写的行内注释
        List<TeachplanDto> teachplanDtos = JSON.parseArray(teachplanJson, TeachplanDto.class); // 把 JSON.parseArray(teachplanJson, TeachplanDto.class) 赋值给 teachplanDtos
        coursePreviewDto.setCourseBase(courseBaseInfoDto); // 执行语句
        coursePreviewDto.setTeachplans(teachplanDtos); // 执行语句
        return coursePreviewDto; // 返回 coursePreviewDto

    } // 代码块结束


    // 课程预览接口 // 保留原作者写的行内注释
    // 传入课程id,表示预览哪门课程 // 保留原作者写的行内注释
    @GetMapping("/coursepreview/{courseId}") // 使用注解 @GetMapping("/coursepreview/{courseId}")
    public ModelAndView preview(@PathVariable("courseId") Long courseId) { // 定义方法 preview(...)

        ModelAndView modelAndView = new ModelAndView(); // 创建 ModelAndView 对象并赋值给 modelAndView
        //查询课程的信息作为模型数据 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId); // 把 coursePublishService.getCoursePreviewInfo(courseId) 赋值给 coursePreviewInfo
        //指定模型 // 保留原作者写的行内注释
        modelAndView.addObject("model", coursePreviewInfo); // 执行语句
        //指定模板 // 保留原作者写的行内注释
        modelAndView.setViewName("course_template");//根据视图名称加.ftl找到模板 // 执行语句
        return modelAndView; // 返回 modelAndView
    } // 代码块结束

    // 提交审核 // 保留原作者写的行内注释
    @ResponseBody // 使用注解 @ResponseBody
    @PostMapping("/courseaudit/commit/{courseId}") // 使用注解 @PostMapping("/courseaudit/commit/{courseId}")
    public void commitAudit(@PathVariable("courseId") Long courseId) { // 定义方法 commitAudit(...)
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
        coursePublishService.commitAudit(companyId, courseId); // 执行语句
    } // 代码块结束

    @ApiOperation("课程发布") // 使用注解 @ApiOperation("课程发布")
    @ResponseBody // 使用注解 @ResponseBody
    @PostMapping ("/coursepublish/{courseId}") // 使用注解 @PostMapping ("/coursepublish/{courseId}")
    public void coursepublish(@PathVariable("courseId") Long courseId){ // 定义方法 coursepublish(...)
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
        coursePublishService.publish(companyId,courseId); // 执行语句
    } // 代码块结束

    @ApiOperation("查询课程发布信息") // 使用注解 @ApiOperation("查询课程发布信息")
    @ResponseBody // 使用注解 @ResponseBody
    @GetMapping("/r/coursepublish/{courseId}") // 使用注解 @GetMapping("/r/coursepublish/{courseId}")
    public CoursePublish getCoursepublish(@PathVariable("courseId") Long courseId) { // 定义方法 getCoursepublish(...)
        //查询课程发布信息 // 保留原作者写的行内注释
        CoursePublish coursePublish = coursePublishService.getCoursePublish(courseId); // 把 coursePublishService.getCoursePublish(courseId) 赋值给 coursePublish
        return coursePublish; // 返回 coursePublish
    } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/api/FreemarkerController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import org.springframework.stereotype.Controller; // 导入 Controller 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.servlet.ModelAndView; // 导入 ModelAndView 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description Freemarker入门程序 // 保留原作者写的块注释
 * @date 2023/2/19 16:12 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Controller // 使用注解 @Controller
public class FreemarkerController { // 定义 class FreemarkerController

    @GetMapping("/testfreemarker") // 使用注解 @GetMapping("/testfreemarker")
    public ModelAndView test() { // 定义方法 test(...)

        ModelAndView modelAndView = new ModelAndView(); // 创建 ModelAndView 对象并赋值给 modelAndView
        //指定模型 // 保留原作者写的行内注释
        modelAndView.addObject("name","小明"); // 执行语句
        //指定模板 // 保留原作者写的行内注释
        modelAndView.setViewName("test");//根据视图名称加.ftl找到模板 // 执行语句
        return modelAndView; // 返回 modelAndView


    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/api/SecurityUtil.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.alibaba.fastjson.JSON; // 导入 JSON 类
import lombok.Data; // 导入 Data 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.security.core.context.SecurityContextHolder; // 导入 SecurityContextHolder 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/24 11:26 // 保留原作者写的块注释
 * 获取当前用户身份工具类 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
public class SecurityUtil { // 定义 class SecurityUtil

    public static XcUser getUser() { // 定义方法 getUser(...)
        try { // 尝试执行可能抛出异常的代码
            //拿到当前用户身份 // 保留原作者写的行内注释
            Object principalObj = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); // 把 SecurityContextHolder.getContext().getAuthentication().getPrincipal() 的返回值赋给 principalObj
            if (principalObj instanceof String) { // 定义方法 方法(...)
                //取出用户身份信息 // 保留原作者写的行内注释
                String principal = principalObj.toString(); // 把 principalObj.toString() 的返回值赋给 principal
                //将json转成对象 // 保留原作者写的行内注释
                XcUser user = JSON.parseObject(principal, XcUser.class); // 把 JSON.parseObject(principal, XcUser.class) 赋值给 user
                return user; // 返回 user
            } // 代码块结束
        } catch (Exception e) { // 定义方法 catch(...)
            log.error("获取当前登录用户身份出错:{}", e.getMessage()); // 执行语句
            e.printStackTrace(); // 执行语句
        } // 代码块结束
        return null; // 返回 null
    } // 代码块结束


    @Data // 使用注解 @Data
    public static class XcUser implements Serializable { // 代码逻辑

        private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

        private String id; // 执行语句

        private String username; // 执行语句

        private String password; // 执行语句

        private String salt; // 执行语句

        private String name; // 执行语句
        private String nickname; // 执行语句
        private String wxUnionid; // 执行语句
        private String companyId; // 执行语句
        /** // 保留原作者写的块注释
         * 头像 // 保留原作者写的块注释
         */ // 保留原作者写的块注释
        private String userpic; // 执行语句

        private String utype; // 执行语句

        private LocalDateTime birthday; // 执行语句

        private String sex; // 执行语句

        private String email; // 执行语句

        private String cellphone; // 执行语句

        private String qq; // 执行语句

        /** // 保留原作者写的块注释
         * 用户状态 // 保留原作者写的块注释
         */ // 保留原作者写的块注释
        private String status; // 执行语句

        private LocalDateTime createTime; // 执行语句

        private LocalDateTime updateTime; // 执行语句


    } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/api/TeachplanController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.content.model.dto.BindTeachplanMediaDto; // 导入 BindTeachplanMediaDto 类
import com.xuecheng.content.model.dto.SaveTeachplanDto; // 导入 SaveTeachplanDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.service.TeachplanService; // 导入 TeachplanService 类
import io.swagger.annotations.Api; // 导入 Api 类
import io.swagger.annotations.ApiOperation; // 导入 ApiOperation 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.web.bind.annotation.*; // 导入 * 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划管理相关的接口 // 保留原作者写的块注释
 * @date 2023/2/14 11:25 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Api(value = "课程计划编辑接口",tags = "课程计划编辑接口") // 使用注解 @Api(value = "课程计划编辑接口",tags = "课程计划编辑接口")
@RestController // 使用注解 @RestController
public class TeachplanController { // 定义 class TeachplanController

    @Autowired // 使用注解 @Autowired
    TeachplanService teachplanService; // 执行语句

   @ApiOperation("查询课程计划树形结构") // 使用注解 @ApiOperation("查询课程计划树形结构")
   //查询课程计划  GET /teachplan/22/tree-nodes // 保留原作者写的行内注释
   @GetMapping("/teachplan/{courseId}/tree-nodes") // 使用注解 @GetMapping("/teachplan/{courseId}/tree-nodes")
 public List<TeachplanDto> getTreeNodes(@PathVariable Long courseId){ // 定义方法 getTreeNodes(...)
       List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId); // 把 teachplanService.findTeachplanTree(courseId) 赋值给 teachplanTree

       return teachplanTree; // 返回 teachplanTree
   } // 代码块结束

    @ApiOperation("课程计划创建或修改") // 使用注解 @ApiOperation("课程计划创建或修改")
    @PostMapping("/teachplan") // 使用注解 @PostMapping("/teachplan")
    public void saveTeachplan( @RequestBody SaveTeachplanDto teachplan){ // 定义方法 saveTeachplan(...)
        teachplanService.saveTeachplan(teachplan); // 执行语句
    } // 代码块结束

    @ApiOperation(value = "课程计划和媒资信息绑定") // 使用注解 @ApiOperation(value = "课程计划和媒资信息绑定")
    @PostMapping("/teachplan/association/media") // 使用注解 @PostMapping("/teachplan/association/media")
    public void associationMedia(@RequestBody BindTeachplanMediaDto bindTeachplanMediaDto){ // 定义方法 associationMedia(...)
        teachplanService.associationMedia(bindTeachplanMediaDto); // 执行语句
    } // 代码块结束

} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/config/ResouceServerConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; // 导入 EnableGlobalMethodSecurity 类
import org.springframework.security.config.annotation.web.builders.HttpSecurity; // 导入 HttpSecurity 类
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; // 导入 EnableResourceServer 类
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 导入 ResourceServerConfigurerAdapter 类
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; // 导入 ResourceServerSecurityConfigurer 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类

/** // 保留原作者写的块注释
 * @description 资源服务配置 // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @date 2022/10/18 16:33 // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
 @Configuration // 使用注解 @Configuration
 @EnableResourceServer // 使用注解 @EnableResourceServer
 @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) // 使用注解 @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
 public class ResouceServerConfig extends ResourceServerConfigurerAdapter { // 定义 class ResouceServerConfig


  //资源服务标识 // 保留原作者写的行内注释
  public static final String RESOURCE_ID = "xuecheng-plus"; // 把 "xuecheng-plus" 赋值给 RESOURCE_ID

  @Autowired // 使用注解 @Autowired
  TokenStore tokenStore; // 执行语句

  @Override // 使用注解 @Override
  public void configure(ResourceServerSecurityConfigurer resources) { // 定义方法 configure(...)
   resources.resourceId(RESOURCE_ID)//资源 id // 代码逻辑
           .tokenStore(tokenStore) // 代码逻辑
           .stateless(true); // 执行语句
  } // 代码块结束

 @Override // 使用注解 @Override
 public void configure(HttpSecurity http) throws Exception { // 定义方法 configure(...)
  http.csrf().disable() // 代码逻辑
          .authorizeRequests() // 代码逻辑
//                .antMatchers("/r/**","/course/**").authenticated()//所有/r/**的请求必须认证通过 // 保留原作者写的行内注释
          .anyRequest().permitAll() // 代码逻辑
  ; // 执行语句
 } // 代码块结束

 } // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/content/config/TokenConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; // 导入 AuthorizationServerTokenServices 类
import org.springframework.security.oauth2.provider.token.DefaultTokenServices; // 导入 DefaultTokenServices 类
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; // 导入 TokenEnhancerChain 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; // 导入 JwtAccessTokenConverter 类
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; // 导入 JwtTokenStore 类

import java.util.Arrays; // 导入 Arrays 类

/** // 保留原作者写的块注释
 * @author Administrator // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 **/ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class TokenConfig { // 定义 class TokenConfig

    String SIGNING_KEY = "mq123"; // 把 "mq123" 赋值给 SIGNING_KEY


//    @Bean // 保留原作者写的行内注释
//    public TokenStore tokenStore() { // 保留原作者写的行内注释
//        //使用内存存储令牌(普通令牌) // 保留原作者写的行内注释
//        return new InMemoryTokenStore(); // 保留原作者写的行内注释
//    } // 保留原作者写的行内注释

    @Autowired // 使用注解 @Autowired
    private JwtAccessTokenConverter accessTokenConverter; // 执行语句

    @Bean // 使用注解 @Bean
    public TokenStore tokenStore() { // 定义方法 tokenStore(...)
        return new JwtTokenStore(accessTokenConverter()); // 返回 new JwtTokenStore(accessTokenConverter())
    } // 代码块结束

    @Bean // 使用注解 @Bean
    public JwtAccessTokenConverter accessTokenConverter() { // 定义方法 accessTokenConverter(...)
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); // 创建 JwtAccessTokenConverter 对象并赋值给 converter
        converter.setSigningKey(SIGNING_KEY); // 执行语句
        return converter; // 返回 converter
    } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/api/com/xuecheng/ContentApplication.java

package com.xuecheng; // 声明类所在的包路径

import com.spring4all.swagger.EnableSwagger2Doc; // 导入 EnableSwagger2Doc 类
import org.springframework.boot.SpringApplication; // 导入 SpringApplication 类
import org.springframework.boot.autoconfigure.SpringBootApplication; // 导入 SpringBootApplication 类
import org.springframework.cloud.openfeign.EnableFeignClients; // 导入 EnableFeignClients 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 内容管理服务启动类 // 保留原作者写的块注释
 * @date 2023/2/11 15:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"}) // 使用注解 @EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"})
@EnableSwagger2Doc // 使用注解 @EnableSwagger2Doc
@SpringBootApplication // 使用注解 @SpringBootApplication
public class ContentApplication { // 定义 class ContentApplication
    public static void main(String[] args) { // 定义方法 main(...)
        SpringApplication.run(ContentApplication.class, args); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/api/resources/bootstrap.yml

spring: # Spring Boot 顶层配置前缀
  application:
    name: content-api # 注册到 Nacos 的服务名称
  cloud:
    nacos:
      server-addr: 192.168.101.65:8848 # Nacos 服务器地址
      discovery: # 服务发现配置
        namespace: dev402 # 使用 dev402 命名空间隔离环境
        group: xuecheng-plus-project # 服务分组,方便统一管理
      config: # 配置中心相关设置
        namespace: dev402 # 与服务发现保持一致
        group: xuecheng-plus-project # 配置归属的分组
        file-extension: yaml # 默认从 yaml 文件加载配置
        refresh-enabled: true # 允许运行时刷新配置
        extension-configs: # 额外加载的业务配置
          - data-id: content-service-${spring.profiles.active}.yaml # 内容服务内部业务配置
            group: xuecheng-plus-project
            refresh: true
        shared-configs: # 跨模块共享的通用配置
          - data-id: swagger-${spring.profiles.active}.yaml # Swagger 文档配置
            group: xuecheng-plus-common
            refresh: true
          - data-id: logging-${spring.profiles.active}.yaml # 日志级别与输出配置
            group: xuecheng-plus-common
            refresh: true
          - data-id: freemarker-config-dev.yaml # Freemarker 模板渲染配置
            group: xuecheng-plus-common
            refresh: true
          - data-id: feign-${spring.profiles.active}.yaml # OpenFeign 超时、负载等配置
            group: xuecheng-plus-common
            refresh: true
          - data-id: redis-${spring.profiles.active}.yaml # Redis 连接信息
            group: xuecheng-plus-common
            refresh: true

  profiles:
    active: dev # 激活 dev 开发环境

文件:xuecheng-plus-content/api/resources/log4j2-dev.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- 声明日志配置使用的 XML 版本 -->
<Configuration monitorInterval="180" packages=""> <!-- 每 180 秒自动检测配置是否更新 -->
    <properties> <!-- 定义可复用的变量 -->
        <property name="logdir">logs</property> <!-- 日志输出目录 -->
        <property name="PATTERN">%date{YYYY-MM-dd HH:mm:ss,SSS} %level [%thread][%file:%line] - %msg%n%throwable</property> <!-- 统一的日志格式 -->
    </properties>
    <Appenders> <!-- 定义日志输出去向 -->
        <Console name="Console" target="SYSTEM_OUT"> <!-- 控制台输出 -->
            <PatternLayout pattern="${PATTERN}"/>
        </Console>

        <RollingFile name="ErrorAppender" fileName="${logdir}/error.log" <!-- 错误日志文件 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/error.%d{yyyy-MM-dd-HH}.log" append="true"> <!-- 按天/小时滚动 -->
            <PatternLayout pattern="${PATTERN}"/>
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <!-- 只接收 ERROR 级别以上 -->
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- 每小时切割一次文件 -->
            </Policies>
        </RollingFile>

        <RollingFile name="DebugAppender" fileName="${logdir}/info.log" <!-- 常规日志文件 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/info.%d{yyyy-MM-dd-HH}.log" append="true">
            <PatternLayout pattern="${PATTERN}"/>
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- 接收 DEBUG 及以上日志 -->
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
            </Policies>
        </RollingFile>
        
         <Async name="AsyncAppender" includeLocation="true"> <!-- 采用异步输出,降低主线程压力 -->
            <AppenderRef ref="ErrorAppender"/>
            <AppenderRef ref="DebugAppender"/>
        </Async>
    </Appenders>
    
    <Loggers> <!-- 自定义日志级别 -->
        <logger name="org.springframework" level="INFO"/>
        <logger name="org.mybatis" level="INFO"/>
        <logger name="cn.itcast.wanxinp2p.consumer.mapper" level="DEBUG"/>
        <logger name="springfox" level="INFO"/>
        <logger name="org.apache.http" level="INFO"/>
        <logger name="com.netflix.discovery" level="INFO"/>
        <logger name="RocketmqCommon"  level="INFO" />
        <logger name="RocketmqRemoting" level="INFO"  />
        <logger name="RocketmqClient" level="WARN"/>
        <logger name="org.dromara.hmily" level="WARN"/>
        <logger name="org.dromara.hmily.lottery" level="WARN"/>
        <logger name="org.dromara.hmily.bonuspoint" level="WARN"/>
        <Root level="DEBUG" includeLocation="true"> <!-- 根日志器,默认 DEBUG 级别 -->
            <AppenderRef ref="AsyncAppender"/>
            <AppenderRef ref="Console"/>
            <AppenderRef ref="DebugAppender"/>
        </Root>
    </Loggers>
</Configuration>

文件:xuecheng-plus-content/api/resources/singleServerConfig.yaml

singleServerConfig: # ElasticJob Lite 单机配置
  appName: content-job # 执行器名称
  namespace: content-job-namespace # 注册中心命名空间
  serverLists: 192.168.101.65:2181 # ZooKeeper 地址
  operationTimeoutMilliseconds: 5000 # 操作超时时间
  maxRetries: 3 # 失败重试次数
  baseSleepTimeMilliseconds: 1000 # 初始重试间隔
  maxSleepTimeMilliseconds: 3000 # 最大重试间隔

文件:xuecheng-plus-content/api/templates/course_template.ftl

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="/static/img/asset-favicon.ico">
    <title>学成在线-${model.courseBase.name}</title>

    <link rel="stylesheet" href="/static/plugins/normalize-css/normalize.css" />
    <link rel="stylesheet" href="/static/plugins/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="/static/css/page-learing-article.css" />
</head>

<body data-spy="scroll" data-target="#articleNavbar" data-offset="150">
<!-- 页面头部 -->
<!--#include virtual="/include/header.html"-->
<!--页面头部结束sss-->
<div id="learningArea">
<div class="article-banner">
    <div class="banner-bg"></div>
    <div class="banner-info">
        <div class="banner-left">
            <p>${model.courseBase.mtName}<span>\ ${model.courseBase.stName}</span></p>
            <p class="tit">${model.courseBase.name}</p>
            <p class="pic">
                <#if model.courseBase.charge=='201000'>
                    <span class="new-pic">免费</span>
                <#else>
                    <span class="new-pic">特惠价格¥${model.courseBase.price!''}</span>
                    <span class="old-pic">原价¥${model.courseBase.originalPrice!''}</span>
                </#if>
            </p>
            <p class="info">
                <a href="#" @click.prevent="startLearning()">马上学习</a>
                <span><em>难度等级</em>
                <#if model.courseBase.grade=='204001'>
                    初级
                 <#elseif model.courseBase.grade=='204002'>
                    中级
                <#elseif model.courseBase.grade=='204003'>
                    高级
                </#if>
                </span>
                <span><em>课程时长</em>2小时27分</span>
                <span><em>评分</em>4.7分</span>
                <span><em>授课模式</em>
                 <#if model.courseBase.teachmode=='200002'>
                     录播
                 <#elseif model.courseBase.teachmode=='200003'>
                     直播
                 </#if>
                </span>
            </p>
        </div>
        <div class="banner-rit">
            <p>
                <a href="http://www.51xuecheng.cn/course/preview/learning.html?id=${model.courseBase.id}" target="_blank">
                    <#if model.courseBase.pic??>
                        <img src="http://file.51xuecheng.cn${model.courseBase.pic}" alt="" width="270" height="156">
                    <#else>
                        <img src="/static/img/widget-video.png" alt="" width="270" height="156">
                    </#if>

                </a>
            </p>
            <p class="vid-act"><span> <i class="i-heart"></i>收藏 23 </span> <span>分享 <i class="i-weixin"></i><i class="i-qq"></i></span></p>
        </div>
    </div>
</div>
<div class="article-cont">
    <div class="tit-list">
        <a href="javascript:;" id="articleClass" class="active">课程介绍</a>
        <a href="javascript:;" id="articleItem">目录</a>
        <a href="javascript:;" id="artcleAsk">问答</a>
        <a href="javascript:;" id="artcleNot">笔记</a>
        <a href="javascript:;" id="artcleCod">评价</a>
        <!--<div class="down-fill">
            <span>资料下载</span>
            <ul>
                <li>java视频资料</li>
                <li>java视频资料</li>
                <li>java视频资料</li>
            </ul>
        </div>-->
    </div>
    <div class="article-box">
        <div class="articleClass" style="display: block">
            <!--<div class="rit-title">评价</div>-->
            <div class="article-cont">
                <div class="article-left-box">
                    <div class="content">

                        <div class="content-com suit">
                            <div class="title"><span>适用人群</span></div>
                            <div class="cont cktop">
                                <div >
                                    <p>${model.courseBase.users!""}</p>
                                </div>
                                <!--<span class="on-off">更多 <i class="i-chevron-bot"></i></span>-->
                            </div>
                        </div>
                        <div class="content-com course">
                            <div class="title"><span>课程制作</span></div>
                            <div class="cont">
                                <div class="img-box"><img src="/static/img/widget-myImg.jpg" alt=""></div>
                                <div class="info-box">
                                    <p class="name">教学方:<em>XX老师</em></p>
                                    <!-- <p class="lab">高级前端开发工程师 10年开发经验</p>-->
                                    <p class="info">JavaEE开发与教学多年,精通JavaEE技术体系,对流行框架JQuery、DWR、Struts1/2,Hibernate,Spring,MyBatis、JBPM、Lucene等有深入研究。授课逻辑严谨,条理清晰,注重学生独立解决问题的能力。</p>
                                    <!-- <p><span>难度等级</span>中级</p>
                                     <p><span>课程时长</span>8-16小时/周,共4周</p>
                                     <p><span>如何通过</span>通过所有的作业及考核,作业共4份,考核为一次终极考核</p>
                                     <p><span>用户评分</span>平均用户评分 <em>4.9</em> <a href="#">查看全部评价</a></p>
                                     <p><span>课程价格</span>特惠价格<em>¥999</em> <i> 原价1999 </i></p>-->
                                </div>
                            </div>

                        </div>
                        <div class="content-com about">
                            <div class="title"><span>课程介绍</span></div>
                            <div class="cont cktop">
                                <div >
                                    <p>${model.courseBase.description!""}</p>
                                </div>
                                <!--<span class="on-off">更多 <i class="i-chevron-bot"></i></span>-->
                            </div>
                        </div>
                        <div class="content-com prob">
                            <div class="title"><span>常见问题</span></div>
                            <div class="cont">
                                <ul>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 我什么时候能够访问课程视频与作业?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 如何需要额外的时间来完成课程会怎么样?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 我支付次课程之后会得到什么?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 退款条例是如何规定的?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 有助学金?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>

                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->

            </div>
        </div>
        <div class="articleItem" style="display: none">
            <div class="article-cont-catalog">
                <div class="article-left-box">
                    <div class="content">
                        <#list model.teachplans as firstNode>
                            <div class="item">
                                <div class="title act"><i class="i-chevron-top"></i>${firstNode.pname}<span class="time">x小时</span></div>
                                <div class="drop-down" style="height: 260px;">
                                    <ul class="list-box">
                                        <#list firstNode.teachPlanTreeNodes as secondNode>
                                            <li><a href="http://www.51xuecheng.cn/course/preview/learning.html?id=${model.courseBase.id}&chapter=${secondNode.teachplanMedia.teachplanId!''}" target="_blank">${secondNode.pname}</a></li>
                                        </#list>
                                    </ul>
                                </div>
                            </div>
                        </#list>
                       <#-- <div class="item">
                            <div class="title act"><i class="i-chevron-top"></i>第一阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down" style="height: 260px;">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>-->


                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
        <#--<div class="articleItem" style="display: none">
            <div class="article-cont-catalog">
                <div class="article-left-box">
                    <div class="content">
                        <div class="item">
                            <div class="title act"><i class="i-chevron-top"></i>第一阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down" style="height: 260px;">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第二阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第三阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第四阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第五阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>

                        </div>
                        <div class="item">
                            <a href="#" class="overwrite">毕业考核</a>
                        </div>
                    </div>
                </div>
                <!--侧边栏&ndash;&gt;
                <!--#include virtual="/include/course_detail_side.html"&ndash;&gt;
                <!--侧边栏&ndash;&gt;
            </div>
        </div>-->
        <div class="artcleAsk" style="display: none">
            <div class="article-cont-ask">
                <div class="article-left-box">
                    <div class="content">
                        <div class="content-title">
                            <p><a class="all">全部</a><a>精选</a><a>我的</a></p>
                            <p><a class="all">全部</a><span><a>1.1</a><a>1.2</a><a>1.3</a><a>1.4</a><a>1.5</a></span><a href="$" class="more">更多 <i class="i-chevron-bot"></i></a></p>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p><span>我来回答</span></p>
                                <p>2017-3-20 <span><i></i>回答2</span><span><i></i>浏览2</span></p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>

                        <div class="itemlast">
                            <a href="#" class="overwrite">显示更多问题</a>
                        </div>
                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
        <div class="artcleNot" style="display: none;">
            <div class="article-cont-note">
                <div class="article-left-box">
                    <div class="content">
                        <div class="content-title">
                            <p><a class="all">全部</a><a>精选</a><a>我的</a></p>
                            <p><a class="all">全部</a><span><a>1.1</a><a>1.2</a><a>1.3</a><a>1.4</a><a>1.5</a></span><a href="$" class="more">更多 <i class="i-chevron-bot"></i></a></p>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <span class="video-time"><i class="i-play"></i>2`10`</span>
                                <p><img src="/static/img/widget-demo.png" width="221" alt=""></p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-coll"></i>采集</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-edt"></i>编辑</span><span><i class="i-del"></i>删除</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-edt"></i>编辑</span><span><i class="i-del"></i>删除</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-edt"></i>编辑</span><span><i class="i-del"></i>删除</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
        <div class="artcleCod" style="display: none;">
            <div class="article-cont">
                <div class="article-left-box">
                    <div class="comment-box">
                        <div class="evaluate">
                            <div class="eva-top">
                                <div class="tit">课程评分 </div>
                                <div class="star">
                                    <div class="score"><i>5</i></div>
                                </div><span class="star-score"> <i>5</i> 分</span></div>
                            <div class="eva-cont">
                                <div class="tit">学员评语 </div>
                                <div class="text-box">
                                    <textarea class="form-control" rows="5" placeholder="扯淡、吐槽、表扬、鼓励......想说啥说啥!"></textarea>
                                    <div class="text-right"><span>发表评论</span></div>
                                </div>
                            </div>
                        </div>
                        <div class="course-evaluate">
                            <div class="top-tit">评论
                                <span>
                        <label><input name="eval" type="radio" value="" checked /> 所有学生 </label>
                        <label><input name="eval" type="radio" value="" /> 完成者 </label>
                    </span>
                            </div>
                            <div class="top-cont">
                                <div class="cont-top-left">
                                    <div class="star-scor">
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        <div class="scor">4.9分</div>
                                    </div>
                                    <div class="all-scor">总评分:12343</div>
                                </div>
                                <div class="cont-top-right">
                                    <div class="star-grade">五星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>95</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">四星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>5</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">三星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>0</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">二星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>2</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">一星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>1</i>%</div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="comment-item-box">
                                <div class="title">评论 <span>12453条评论</span></div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>4</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="get-more">页面加载中...</div>
                            </div>
                        </div>
                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
    </div>
</div>
    <div class="popup-course">
        <div class="mask"></div>
        <!--欢迎访问课程弹窗- start -->
        <div class="popup-course-box">
            <div class="title">${model.courseBase.name} <span class="close-popup-course-box">×</span></div>
            <div class="content">
                <p>欢迎学习本课程,本课程免费您可以立即学习,也可加入我的课程表享受更优质的服务。</p>
                <p><a href="#" @click.prevent="addCourseTable()">加入我的课程表</a>  <a href="#" @click.prevent="startLearngin()">立即学习</a></p>
            </div>
        </div>
    </div>
    <div class="popup-box">
        <div class="mask"></div>
        <!--支付弹窗- start -->
        <div class="popup-pay-box">
            <div class="title">${model.courseBase.name} <span class="close-popup-pay-box">×</span></div>
            <div class="content">
                <img :src="qrcode" width="200" height="200" alt="请点击支付宝支付按钮,并完成扫码支付。"/>

                <div class="info">
                    <p class="info-tit">${model.courseBase.name}<span>课程有效期:${model.courseBase.validDays}天</span></p>
                    <p class="info-pic">课程价格 : <span>¥${model.courseBase.originalPrice!''}元</span></p>
                    <p class="info-new-pic">优惠价格 : <span>¥${model.courseBase.price!''}元</span></p>
                </div>
            </div>
            <div class="fact-pic">实际支付: <span>¥${model.courseBase.price!''}元</span></div>
            <div class="go-pay"><a href="#" @click.prevent="wxPay()">微信支付</a><a href="#" @click.prevent="aliPay()">支付宝支付</a><a href="#" @click.prevent="querypayresult()">支付完成</a><a href="#" @click.prevent="startLearngin()">试学</a></div>
        </div>
        <!--支付弹窗- end -->
        <div class="popup-comment-box">

        </div>
    </div>
    <!-- 页面底部 -->
    <!--底部版权-->
    <!--#include virtual="/include/footer.html"-->
    <!--底部版权-->
</div>
<script>var courseId = "${model.courseBase.id}";var courseCharge = "${model.courseBase.charge}"</script>
<!--#include virtual="/include/course_detail_dynamic.html"-->
</body>

文件:xuecheng-plus-content/api/templates/test.ftl

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>

文件:xuecheng-plus-content/api_pom.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- 内容 API 模块 -->
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>xuecheng-plus-content</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>xuecheng-plus-content-api</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xuecheng-plus-content-service</artifactId>
            <version>0.0.1-SNAPSHOT</version> <!-- 引入业务实现 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId> <!-- 资源服务认证 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId> <!-- OAuth2 授权能力 -->
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <!-- 注册中心 -->
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <!-- 配置中心 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-context</artifactId> <!-- 支持动态刷新、环境变量 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId> <!-- 提供 Web MVC 能力 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId> <!-- 改用 log4j2 -->
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId> <!-- 参数校验 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId> <!-- 日志输出 -->
        </dependency>
        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.0.RELEASE</version> <!-- 自动生成接口文档 -->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId> <!-- 页面静态化渲染 -->
        </dependency>
    </dependencies>
</project>

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/AddCourseDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.base.exception.ValidationGroups; // 导入 ValidationGroups 类
import io.swagger.annotations.ApiModel; // 导入 ApiModel 类
import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

import javax.validation.constraints.NotEmpty; // 导入 NotEmpty 类
import javax.validation.constraints.Size; // 导入 Size 类
import java.math.BigDecimal; // 导入 BigDecimal 类

/** // 保留原作者写的块注释
 * @description 添加课程dto // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @date 2022/9/7 17:40 // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@ApiModel(value="AddCourseDto", description="新增课程基本信息") // 使用注解 @ApiModel(value="AddCourseDto", description="新增课程基本信息")
public class AddCourseDto { // 定义 class AddCourseDto

 @NotEmpty(message = "新增课程名称不能为空",groups={ValidationGroups.Inster.class}) // 使用注解 @NotEmpty(message = "新增课程名称不能为空",groups={ValidationGroups.Inster.class})
 @NotEmpty(message = "修改课程名称不能为空",groups={ValidationGroups.Update.class}) // 使用注解 @NotEmpty(message = "修改课程名称不能为空",groups={ValidationGroups.Update.class})
// @NotEmpty(message = "课程名称不能为空") // 保留原作者写的行内注释
 @ApiModelProperty(value = "课程名称", required = true) // 使用注解 @ApiModelProperty(value = "课程名称", required = true)
 private String name; // 执行语句

 @NotEmpty(message = "适用人群不能为空") // 使用注解 @NotEmpty(message = "适用人群不能为空")
 @Size(message = "适用人群内容过少",min = 10) // 使用注解 @Size(message = "适用人群内容过少",min = 10)
 @ApiModelProperty(value = "适用人群", required = true) // 使用注解 @ApiModelProperty(value = "适用人群", required = true)
 private String users; // 执行语句

 @ApiModelProperty(value = "课程标签") // 使用注解 @ApiModelProperty(value = "课程标签")
 private String tags; // 执行语句

 @NotEmpty(message = "课程分类不能为空") // 使用注解 @NotEmpty(message = "课程分类不能为空")
 @ApiModelProperty(value = "大分类", required = true) // 使用注解 @ApiModelProperty(value = "大分类", required = true)
 private String mt; // 执行语句

 @NotEmpty(message = "课程分类不能为空") // 使用注解 @NotEmpty(message = "课程分类不能为空")
 @ApiModelProperty(value = "小分类", required = true) // 使用注解 @ApiModelProperty(value = "小分类", required = true)
 private String st; // 执行语句

 @NotEmpty(message = "课程等级不能为空") // 使用注解 @NotEmpty(message = "课程等级不能为空")
 @ApiModelProperty(value = "课程等级", required = true) // 使用注解 @ApiModelProperty(value = "课程等级", required = true)
 private String grade; // 执行语句

 @ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true) // 使用注解 @ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true)
 private String teachmode; // 执行语句

 @ApiModelProperty(value = "课程介绍") // 使用注解 @ApiModelProperty(value = "课程介绍")
 @Size(message = "课程描述内容过少",min = 10) // 使用注解 @Size(message = "课程描述内容过少",min = 10)
 private String description; // 执行语句

 @ApiModelProperty(value = "课程图片", required = true) // 使用注解 @ApiModelProperty(value = "课程图片", required = true)
 private String pic; // 执行语句

 @NotEmpty(message = "收费规则不能为空") // 使用注解 @NotEmpty(message = "收费规则不能为空")
 @ApiModelProperty(value = "收费规则,对应数据字典", required = true) // 使用注解 @ApiModelProperty(value = "收费规则,对应数据字典", required = true)
 private String charge; // 执行语句

 @ApiModelProperty(value = "价格") // 使用注解 @ApiModelProperty(value = "价格")
 private Float price; // 执行语句
 @ApiModelProperty(value = "原价") // 使用注解 @ApiModelProperty(value = "原价")
 private Float originalPrice; // 执行语句


 @ApiModelProperty(value = "qq") // 使用注解 @ApiModelProperty(value = "qq")
 private String qq; // 执行语句

 @ApiModelProperty(value = "微信") // 使用注解 @ApiModelProperty(value = "微信")
 private String wechat; // 执行语句
 @ApiModelProperty(value = "电话") // 使用注解 @ApiModelProperty(value = "电话")
 private String phone; // 执行语句

 @ApiModelProperty(value = "有效期") // 使用注解 @ApiModelProperty(value = "有效期")
 private Integer validDays; // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/BindTeachplanMediaDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import io.swagger.annotations.ApiModel; // 导入 ApiModel 类
import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 绑定媒资和课程计划的模型类 // 保留原作者写的块注释
 * @date 2023/2/19 15:37 // 保留原作者写的块注释
 */ // 保留原作者写的块注释

@Data // 使用注解 @Data
@ApiModel(value="BindTeachplanMediaDto", description="教学计划-媒资绑定提交数据") // 使用注解 @ApiModel(value="BindTeachplanMediaDto", description="教学计划-媒资绑定提交数据")
public class BindTeachplanMediaDto { // 定义 class BindTeachplanMediaDto

 @ApiModelProperty(value = "媒资文件id", required = true) // 使用注解 @ApiModelProperty(value = "媒资文件id", required = true)
 private String mediaId; // 执行语句

 @ApiModelProperty(value = "媒资文件名称", required = true) // 使用注解 @ApiModelProperty(value = "媒资文件名称", required = true)
 private String fileName; // 执行语句

 @ApiModelProperty(value = "课程计划标识", required = true) // 使用注解 @ApiModelProperty(value = "课程计划标识", required = true)
 private Long teachplanId; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/CourseBaseInfoDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

import java.math.BigDecimal; // 导入 BigDecimal 类

/** // 保留原作者写的块注释
 * @description 课程基本信息dto // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @date 2022/9/7 17:44 // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CourseBaseInfoDto extends CourseBase { // 定义 class CourseBaseInfoDto


 /** // 保留原作者写的块注释
  * 收费规则,对应数据字典 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String charge; // 执行语句

 /** // 保留原作者写的块注释
  * 价格 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private Float price; // 执行语句


 /** // 保留原作者写的块注释
  * 原价 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private Float originalPrice; // 执行语句

 /** // 保留原作者写的块注释
  * 咨询qq // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String qq; // 执行语句

 /** // 保留原作者写的块注释
  * 微信 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String wechat; // 执行语句

 /** // 保留原作者写的块注释
  * 电话 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String phone; // 执行语句

 /** // 保留原作者写的块注释
  * 有效期天数 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private Integer validDays; // 执行语句

 /** // 保留原作者写的块注释
  * 大分类名称 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String mtName; // 执行语句

 /** // 保留原作者写的块注释
  * 小分类名称 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String stName; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/CourseCategoryTreeDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.content.model.po.CourseCategory; // 导入 CourseCategory 类
import lombok.Data; // 导入 Data 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 11:51 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CourseCategoryTreeDto extends CourseCategory implements java.io.Serializable { // 定义 class CourseCategoryTreeDto

   //子节点 // 保留原作者写的行内注释
   List<CourseCategoryTreeDto> childrenTreeNodes; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/CoursePreviewDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import lombok.Data; // 导入 Data 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程预览模型类 // 保留原作者写的块注释
 * @date 2023/2/21 10:00 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CoursePreviewDto { // 定义 class CoursePreviewDto

 //课程基本信息,营销信息 // 保留原作者写的行内注释
 private CourseBaseInfoDto courseBase; // 执行语句

 //课程计划信息 // 保留原作者写的行内注释
 private List<TeachplanDto> teachplans; // 执行语句

 //课程师资信息... // 保留原作者写的行内注释

} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/EditCourseDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/14 10:22 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class EditCourseDto extends AddCourseDto { // 定义 class EditCourseDto

 @ApiModelProperty(value = "课程id", required = true) // 使用注解 @ApiModelProperty(value = "课程id", required = true)
 private Long id; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/QueryCourseParamsDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import lombok.Data; // 导入 Data 类
import lombok.ToString; // 导入 ToString 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程查询条件模型类 // 保留原作者写的块注释
 * @date 2023/2/11 15:37 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@ToString // 使用注解 @ToString
public class QueryCourseParamsDto { // 定义 class QueryCourseParamsDto

    //审核状态 // 保留原作者写的行内注释
    private String auditStatus; // 执行语句
    //课程名称 // 保留原作者写的行内注释
    private String courseName; // 执行语句
    //发布状态 // 保留原作者写的行内注释
    private String publishStatus; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/SaveTeachplanDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import lombok.Data; // 导入 Data 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 新增大章节、小章节、修改章节信息 // 保留原作者写的块注释
 * @date 2023/2/14 14:54 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class SaveTeachplanDto { // 定义 class SaveTeachplanDto

    /*** // 保留原作者写的块注释
     * 教学计划id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pname; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划父级Id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long parentid; // 执行语句

    /** // 保留原作者写的块注释
     * 层级,分为1、2、3级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer grade; // 执行语句

    /** // 保留原作者写的块注释
     * 课程类型:1视频、2文档 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mediaType; // 执行语句


    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程发布标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long coursePubId; // 执行语句


    /** // 保留原作者写的块注释
     * 是否支持试学或预览(试看) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String isPreview; // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/dto/TeachplanDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.content.model.po.Teachplan; // 导入 Teachplan 类
import com.xuecheng.content.model.po.TeachplanMedia; // 导入 TeachplanMedia 类
import lombok.Data; // 导入 Data 类
import lombok.ToString; // 导入 ToString 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划信息模型类 // 保留原作者写的块注释
 * @date 2023/2/14 11:23 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@ToString // 使用注解 @ToString
public class TeachplanDto extends Teachplan { // 定义 class TeachplanDto
  //与媒资管理的信息 // 保留原作者写的行内注释
   private TeachplanMedia teachplanMedia; // 执行语句

  //小章节list // 保留原作者写的行内注释
   private List<TeachplanDto> teachPlanTreeNodes; // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/CourseBase.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程基本信息 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_base") // 使用注解 @TableName("course_base")
public class CourseBase implements Serializable { // 定义 class CourseBase

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 机构名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式(common普通,record 录播,live直播等) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 创建时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 修改时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT_UPDATE) // 使用注解 @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime changeDate; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String createPeople; // 执行语句

    /** // 保留原作者写的块注释
     * 更新人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String changePeople; // 执行语句

    /** // 保留原作者写的块注释
     * 审核状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String auditStatus; // 执行语句

    /** // 保留原作者写的块注释
     * 课程发布状态 未发布  已发布 下线 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/CourseCategory.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程分类 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_category") // 使用注解 @TableName("course_category")
public class CourseCategory implements Serializable { // 定义 class CourseCategory

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String id; // 执行语句

    /** // 保留原作者写的块注释
     * 分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 分类标签默认和名称一样 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String label; // 执行语句

    /** // 保留原作者写的块注释
     * 父结点id(第一级的父节点是0,自关联字段id) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String parentid; // 执行语句

    /** // 保留原作者写的块注释
     * 是否显示 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer isShow; // 执行语句

    /** // 保留原作者写的块注释
     * 排序字段 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer orderby; // 执行语句

    /** // 保留原作者写的块注释
     * 是否叶子 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer isLeaf; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/CourseMarket.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程营销信息 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_market") // 使用注解 @TableName("course_market")
public class CourseMarket implements Serializable { // 定义 class CourseMarket

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键,课程id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句

    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 咨询qq // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String qq; // 执行语句

    /** // 保留原作者写的块注释
     * 微信 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String wechat; // 执行语句

    /** // 保留原作者写的块注释
     * 电话 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String phone; // 执行语句

    /** // 保留原作者写的块注释
     * 有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/CoursePublish.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.FieldFill; // 导入 FieldFill 类
import com.baomidou.mybatisplus.annotation.TableField; // 导入 TableField 类
import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import com.fasterxml.jackson.annotation.JsonFormat; // 导入 JsonFormat 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_publish") // 使用注解 @TableName("course_publish")
public class CoursePublish implements Serializable { // 定义 class CoursePublish

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 公司名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String username; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mtName; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String stName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句

    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 课程营销信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String market; // 执行语句

    /** // 保留原作者写的块注释
     * 所有课程计划,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachplan; // 执行语句

    /** // 保留原作者写的块注释
     * 教师信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachers; // 执行语句

    /** // 保留原作者写的块注释
     * 发布时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 上架时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime onlineDate; // 执行语句

    /** // 保留原作者写的块注释
     * 下架时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime offlineDate; // 执行语句

    /** // 保留原作者写的块注释
     * 发布状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句

    /** // 保留原作者写的块注释
     * 备注 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String remark; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典--203 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句

    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 课程有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/CoursePublishPre.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.FieldFill; // 导入 FieldFill 类
import com.baomidou.mybatisplus.annotation.TableField; // 导入 TableField 类
import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_publish_pre") // 使用注解 @TableName("course_publish_pre")
public class CoursePublishPre implements Serializable { // 定义 class CoursePublishPre

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 公司名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String username; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mtName; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String stName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句

    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 课程营销信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String market; // 执行语句

    /** // 保留原作者写的块注释
     * 所有课程计划,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachplan; // 执行语句

    /** // 保留原作者写的块注释
     * 教师信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachers; // 执行语句

    /** // 保留原作者写的块注释
     * 提交时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 审核时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private LocalDateTime auditDate; // 执行语句

    /** // 保留原作者写的块注释
     * 状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句

    /** // 保留原作者写的块注释
     * 备注 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String remark; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典--203 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句

    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 课程有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/CourseTeacher.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程-教师关系表 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_teacher") // 使用注解 @TableName("course_teacher")
public class CourseTeacher implements Serializable { // 定义 class CourseTeacher

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 教师标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teacherName; // 执行语句

    /** // 保留原作者写的块注释
     * 教师职位 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String position; // 执行语句

    /** // 保留原作者写的块注释
     * 教师简介 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String introduction; // 执行语句

    /** // 保留原作者写的块注释
     * 照片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String photograph; // 执行语句

    /** // 保留原作者写的块注释
     * 创建时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/Teachplan.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程计划 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("teachplan") // 使用注解 @TableName("teachplan")
public class Teachplan implements Serializable { // 定义 class Teachplan

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pname; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划父级Id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long parentid; // 执行语句

    /** // 保留原作者写的块注释
     * 层级,分为1、2、3级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer grade; // 执行语句

    /** // 保留原作者写的块注释
     * 课程类型:1视频、2文档 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mediaType; // 执行语句

    /** // 保留原作者写的块注释
     * 开始直播时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private LocalDateTime startTime; // 执行语句

    /** // 保留原作者写的块注释
     * 直播结束时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private LocalDateTime endTime; // 执行语句

    /** // 保留原作者写的块注释
     * 章节及课程时介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 时长,单位时:分:秒 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String timelength; // 执行语句

    /** // 保留原作者写的块注释
     * 排序字段 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer orderby; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程发布标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long coursePubId; // 执行语句

    /** // 保留原作者写的块注释
     * 状态(1正常  0删除) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer status; // 执行语句

    /** // 保留原作者写的块注释
     * 是否支持试学或预览(试看) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String isPreview; // 执行语句

    /** // 保留原作者写的块注释
     * 创建时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 修改时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT_UPDATE) // 使用注解 @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime changeDate; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/com/xuecheng/content/model/po/TeachplanMedia.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 *  // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("teachplan_media") // 使用注解 @TableName("teachplan_media")
public class TeachplanMedia implements Serializable { // 定义 class TeachplanMedia

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 媒资文件id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mediaId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long teachplanId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 媒资文件原始名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField("media_fileName") // 使用注解 @TableField("media_fileName")
    private String mediaFilename; // 执行语句

    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String createPeople; // 执行语句

    /** // 保留原作者写的块注释
     * 修改人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String changePeople; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/model_pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>xuecheng-plus-content</artifactId> <!-- 继承内容聚合模块 -->
        <groupId>com.xuecheng</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>xuecheng-plus-content-model</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xuecheng-plus-base</artifactId> <!-- 复用基础工具包 -->
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-annotation</artifactId> <!-- 支持 MyBatis-Plus 注解 -->
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-core</artifactId> <!-- MyBatis-Plus 核心包 -->
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId> <!-- 模型类使用 Lombok 简化代码 -->
        </dependency>
    </dependencies>
</project>

文件:xuecheng-plus-content/pom.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- SQL 语句片段 -->
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- SQL 语句片段 -->
    <modelVersion>4.0.0</modelVersion> <!-- SQL 语句片段 -->
    <parent> <!-- SQL 语句片段 -->
        <artifactId>xuecheng-plus-parent</artifactId> <!-- SQL 语句片段 -->
        <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
        <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
        <relativePath>../xuecheng-plus-parent</relativePath> <!-- SQL 语句片段 -->
    </parent> <!-- SQL 语句片段 -->
    <packaging>pom</packaging> <!-- SQL 语句片段 -->
    <artifactId>xuecheng-plus-content</artifactId> <!-- SQL 语句片段 -->
    <modules> <!-- SQL 语句片段 -->
        <module>xuecheng-plus-content-api</module> <!-- SQL 语句片段 -->
        <module>xuecheng-plus-content-model</module> <!-- SQL 语句片段 -->
        <module>xuecheng-plus-content-service</module> <!-- SQL 语句片段 -->
    </modules> <!-- SQL 语句片段 -->

</project> <!-- SQL 语句片段 -->

文件:xuecheng-plus-content/root_pom.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- 聚合模块 POM 声明 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- Maven 命名空间 -->
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- 约束文件 -->
    <modelVersion>4.0.0</modelVersion> <!-- 使用 Maven4 模型 -->
    <parent> <!-- 继承平台父工程,复用统一版本与插件 -->
        <artifactId>xuecheng-plus-parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../xuecheng-plus-parent</relativePath>
    </parent>
    <packaging>pom</packaging> <!-- 聚合模块本身不打包为 jar -->
    <artifactId>xuecheng-plus-content</artifactId> <!-- 课程内容模块父工程 -->
    <modules> <!-- 声明子模块 -->
        <module>xuecheng-plus-content-api</module> <!-- 对外 REST API -->
        <module>xuecheng-plus-content-model</module> <!-- 公共 DTO/PO 模型 -->
        <module>xuecheng-plus-content-service</module> <!-- 业务实现与定时任务 -->
    </modules>
</project>

文件:xuecheng-plus-content/service/com/xuecheng/content/config/MultipartSupportConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import feign.codec.Encoder; // 导入 Encoder 类
import feign.form.spring.SpringFormEncoder; // 导入 SpringFormEncoder 类
import org.apache.commons.fileupload.FileItem; // 导入 FileItem 类
import org.apache.commons.fileupload.disk.DiskFileItemFactory; // 导入 DiskFileItemFactory 类
import org.apache.commons.io.IOUtils; // 导入 IOUtils 类
import org.springframework.beans.factory.ObjectFactory; // 导入 ObjectFactory 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; // 导入 HttpMessageConverters 类
import org.springframework.cloud.openfeign.support.SpringEncoder; // 导入 SpringEncoder 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.context.annotation.Primary; // 导入 Primary 类
import org.springframework.context.annotation.Scope; // 导入 Scope 类
import org.springframework.http.MediaType; // 导入 MediaType 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类
import org.springframework.web.multipart.commons.CommonsMultipartFile; // 导入 CommonsMultipartFile 类

import java.io.File; // 导入 File 类
import java.io.FileInputStream; // 导入 FileInputStream 类
import java.io.OutputStream; // 导入 OutputStream 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2022/10/15 22:13 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class MultipartSupportConfig { // 定义 class MultipartSupportConfig

    @Autowired // 使用注解 @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters; // 执行语句

    @Bean // 使用注解 @Bean
    @Primary//注入相同类型的bean时优先使用 // 使用注解 @Primary
    @Scope("prototype") // 使用注解 @Scope("prototype")
    public Encoder feignEncoder() { // 定义方法 feignEncoder(...)
        return new SpringFormEncoder(new SpringEncoder(messageConverters)); // 返回 new SpringFormEncoder(new SpringEncoder(messageConverters))
    } // 代码块结束

    //将file转为Multipart // 保留原作者写的行内注释
    public static MultipartFile getMultipartFile(File file) { // 定义方法 getMultipartFile(...)
        FileItem item = new DiskFileItemFactory().createItem("file", MediaType.MULTIPART_FORM_DATA_VALUE, true, file.getName()); // 创建 DiskFileItemFactory 对象并赋值给 item
        try (FileInputStream inputStream = new FileInputStream(file); // 尝试执行可能抛出异常的代码
             OutputStream outputStream = item.getOutputStream();) { // 定义方法 方法(...)
            IOUtils.copy(inputStream, outputStream); // 执行语句

        } catch (Exception e) { // 定义方法 catch(...)
            e.printStackTrace(); // 执行语句
        } // 代码块结束
        return new CommonsMultipartFile(item); // 返回 new CommonsMultipartFile(item)
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/config/MybatisPlusConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.DbType; // 导入 DbType 类
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; // 导入 MybatisPlusInterceptor 类
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; // 导入 PaginationInnerInterceptor 类
import org.mybatis.spring.annotation.MapperScan; // 导入 MapperScan 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:23 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
@MapperScan("com.xuecheng.content.mapper") // 使用注解 @MapperScan("com.xuecheng.content.mapper")
public class MybatisPlusConfig { // 定义 class MybatisPlusConfig
 /** // 保留原作者写的块注释
  * 定义分页拦截器 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 @Bean // 使用注解 @Bean
 public MybatisPlusInterceptor mybatisPlusInterceptor() { // 定义方法 mybatisPlusInterceptor(...)
  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 创建 MybatisPlusInterceptor 对象并赋值给 interceptor
  interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 执行语句
  return interceptor; // 返回 interceptor
 } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/config/XxlJobConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; // 导入 XxlJobSpringExecutor 类
import org.slf4j.Logger; // 导入 Logger 类
import org.slf4j.LoggerFactory; // 导入 LoggerFactory 类
import org.springframework.beans.factory.annotation.Value; // 导入 Value 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类

/** // 保留原作者写的块注释
 * xxl-job config // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author xuxueli 2017-04-28 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class XxlJobConfig { // 定义 class XxlJobConfig
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); // 把 LoggerFactory.getLogger(XxlJobConfig.class) 赋值给 logger

    @Value("${xxl.job.admin.addresses}") // 使用注解 @Value("${xxl.job.admin.addresses}")
    private String adminAddresses; // 执行语句

    @Value("${xxl.job.accessToken}") // 使用注解 @Value("${xxl.job.accessToken}")
    private String accessToken; // 执行语句

    @Value("${xxl.job.executor.appname}") // 使用注解 @Value("${xxl.job.executor.appname}")
    private String appname; // 执行语句

    @Value("${xxl.job.executor.address}") // 使用注解 @Value("${xxl.job.executor.address}")
    private String address; // 执行语句

    @Value("${xxl.job.executor.ip}") // 使用注解 @Value("${xxl.job.executor.ip}")
    private String ip; // 执行语句

    @Value("${xxl.job.executor.port}") // 使用注解 @Value("${xxl.job.executor.port}")
    private int port; // 执行语句

    @Value("${xxl.job.executor.logpath}") // 使用注解 @Value("${xxl.job.executor.logpath}")
    private String logPath; // 执行语句

    @Value("${xxl.job.executor.logretentiondays}") // 使用注解 @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays; // 执行语句


    @Bean // 使用注解 @Bean
    public XxlJobSpringExecutor xxlJobExecutor() { // 定义方法 xxlJobExecutor(...)
        logger.info(">>>>>>>>>>> xxl-job config init."); // 执行语句
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); // 创建 XxlJobSpringExecutor 对象并赋值给 xxlJobSpringExecutor
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses); // 执行语句
        xxlJobSpringExecutor.setAppname(appname); // 执行语句
        xxlJobSpringExecutor.setAddress(address); // 执行语句
        xxlJobSpringExecutor.setIp(ip); // 执行语句
        xxlJobSpringExecutor.setPort(port); // 执行语句
        xxlJobSpringExecutor.setAccessToken(accessToken); // 执行语句
        xxlJobSpringExecutor.setLogPath(logPath); // 执行语句
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); // 执行语句

        return xxlJobSpringExecutor; // 返回 xxlJobSpringExecutor
    } // 代码块结束

    /** // 保留原作者写的块注释
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; // 保留原作者写的块注释
     * // 保留原作者写的块注释
     *      1、引入依赖: // 保留原作者写的块注释
     *          <dependency> // 保留原作者写的块注释
     *             <groupId>org.springframework.cloud</groupId> // 保留原作者写的块注释
     *             <artifactId>spring-cloud-commons</artifactId> // 保留原作者写的块注释
     *             <version>${version}</version> // 保留原作者写的块注释
     *         </dependency> // 保留原作者写的块注释
     * // 保留原作者写的块注释
     *      2、配置文件,或者容器启动变量 // 保留原作者写的块注释
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' // 保留原作者写的块注释
     * // 保留原作者写的块注释
     *      3、获取IP // 保留原作者写的块注释
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); // 保留原作者写的块注释
     */ // 保留原作者写的块注释


} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/feignclient/CourseIndex.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import com.alibaba.fastjson.annotation.JSONField; // 导入 JSONField 类
import com.fasterxml.jackson.annotation.JsonFormat; // 导入 JsonFormat 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程索引信息 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CourseIndex implements Serializable { // 定义 class CourseIndex

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 公司名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句


    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mtName; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String stName; // 执行语句



    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句
    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句


    /** // 保留原作者写的块注释
     * 发布时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JSONField(format="yyyy-MM-dd HH:mm:ss") // 使用注解 @JSONField(format="yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句

    /** // 保留原作者写的块注释
     * 备注 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String remark; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典--203 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句
    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 课程有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/feignclient/MediaServiceClient.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import com.xuecheng.content.config.MultipartSupportConfig; // 导入 MultipartSupportConfig 类
import org.springframework.cloud.openfeign.FeignClient; // 导入 FeignClient 类
import org.springframework.http.MediaType; // 导入 MediaType 类
import org.springframework.web.bind.annotation.RequestMapping; // 导入 RequestMapping 类
import org.springframework.web.bind.annotation.RequestParam; // 导入 RequestParam 类
import org.springframework.web.bind.annotation.RequestPart; // 导入 RequestPart 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 远程调用媒资服务的接口 // 保留原作者写的块注释
 * @date 2023/2/22 10:26 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
//使用fallback定义降级类是无法拿到熔断异常,使用FallbackFactory可以拿到熔断的异常信息 // 保留原作者写的行内注释
@FeignClient(value = "media-api",configuration = {MultipartSupportConfig.class},fallbackFactory = MediaServiceClientFallbackFactory.class) // 使用注解 @FeignClient(value = "media-api",configuration = {MultipartSupportConfig.class},fallbackFactory = MediaServiceClientFallbackFactory.class)
public interface MediaServiceClient { // 定义 interface MediaServiceClient

    @RequestMapping(value = "/media/upload/coursefile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE) // 使用注解 @RequestMapping(value = "/media/upload/coursefile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String upload(@RequestPart("filedata") MultipartFile filedata, // 代码逻辑
                                      @RequestParam(value= "objectName",required=false) String objectName) throws IOException; // 使用注解 @RequestParam(value= "objectName",required=false) String objectName) throws IOException;

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/feignclient/MediaServiceClientFallback.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 11:06 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public class MediaServiceClientFallback implements MediaServiceClient{ // 定义 class MediaServiceClientFallback
 @Override // 使用注解 @Override
 public String upload(MultipartFile filedata, String objectName) throws IOException { // 定义方法 upload(...)

  return null; // 返回 null
 } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/feignclient/MediaServiceClientFallbackFactory.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import feign.hystrix.FallbackFactory; // 导入 FallbackFactory 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.stereotype.Component; // 导入 Component 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 11:09 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Component // 使用注解 @Component
public class MediaServiceClientFallbackFactory implements FallbackFactory<MediaServiceClient> { // 定义 class MediaServiceClientFallbackFactory
 //拿到了熔断的异常信息throwable // 保留原作者写的行内注释
 @Override // 使用注解 @Override
 public MediaServiceClient create(Throwable throwable) { // 定义方法 create(...)

  return new MediaServiceClient() { // 定义方法 MediaServiceClient(...)
   //发生熔断上传服务调用此方法执行降级逻辑 // 保留原作者写的行内注释
   @Override // 使用注解 @Override
   public String upload(MultipartFile filedata, String objectName) throws IOException { // 定义方法 upload(...)
     log.debug("远程调用上传文件的接口发生熔断:{}",throwable.toString(),throwable); // 执行语句
    return null; // 返回 null
   } // 代码块结束
  }; // 执行语句
 } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/feignclient/SearchServiceClient.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import org.springframework.cloud.openfeign.FeignClient; // 导入 FeignClient 类
import org.springframework.web.bind.annotation.PostMapping; // 导入 PostMapping 类
import org.springframework.web.bind.annotation.RequestBody; // 导入 RequestBody 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 14:47 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@FeignClient(value = "search",fallbackFactory = SearchServiceClientFallbackFactory.class) // 使用注解 @FeignClient(value = "search",fallbackFactory = SearchServiceClientFallbackFactory.class)
public interface SearchServiceClient { // 定义 interface SearchServiceClient

 @PostMapping("/search/index/course") // 使用注解 @PostMapping("/search/index/course")
 public Boolean add(@RequestBody CourseIndex courseIndex); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/feignclient/SearchServiceClientFallbackFactory.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import feign.hystrix.FallbackFactory; // 导入 FallbackFactory 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.stereotype.Component; // 导入 Component 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 14:50 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Component // 使用注解 @Component
public class SearchServiceClientFallbackFactory implements FallbackFactory<SearchServiceClient> { // 定义 class SearchServiceClientFallbackFactory
    @Override // 使用注解 @Override
    public SearchServiceClient create(Throwable throwable) { // 定义方法 create(...)
        return new SearchServiceClient() { // 定义方法 SearchServiceClient(...)
            @Override // 使用注解 @Override
            public Boolean add(CourseIndex courseIndex) { // 定义方法 add(...)
                log.error("添加课程索引发生熔断,索引信息:{},熔断异常:{}",courseIndex,throwable.toString(),throwable); // 执行语句
                //走降级了返回 false // 保留原作者写的行内注释
                return false; // 返回 false
            } // 代码块结束
        }; // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseBaseMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程基本信息 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
// 课程预览使用的dao层 // 保留原作者写的行内注释
public interface CourseBaseMapper extends BaseMapper<CourseBase> { // 定义 interface CourseBaseMapper

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseBaseMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseBaseMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseBase"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="company_id" property="companyId" /> <!-- 字段与属性的映射规则 -->
        <result column="company_name" property="companyName" /> <!-- 字段与属性的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="users" property="users" /> <!-- 字段与属性的映射规则 -->
        <result column="tags" property="tags" /> <!-- 字段与属性的映射规则 -->
        <result column="mt" property="mt" /> <!-- 字段与属性的映射规则 -->
        <result column="st" property="st" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="teachmode" property="teachmode" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="pic" property="pic" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="change_date" property="changeDate" /> <!-- 字段与属性的映射规则 -->
        <result column="create_people" property="createPeople" /> <!-- 字段与属性的映射规则 -->
        <result column="change_people" property="changePeople" /> <!-- 字段与属性的映射规则 -->
        <result column="audit_status" property="auditStatus" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, company_id, company_name, name, users, tags, mt, st, grade, teachmode, description, pic, create_date, change_date, create_people, change_people, audit_status, status <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseCategoryMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.po.CourseCategory; // 导入 CourseCategory 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程分类 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseCategoryMapper extends BaseMapper<CourseCategory> { // 定义 interface CourseCategoryMapper

    //使用递归查询分类 // 保留原作者写的行内注释
    public List<CourseCategoryTreeDto> selectTreeNodes(String id); // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseCategoryMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseCategoryMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseCategory"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="label" property="label" /> <!-- 字段与属性的映射规则 -->
        <result column="parentid" property="parentid" /> <!-- 字段与属性的映射规则 -->
        <result column="is_show" property="isShow" /> <!-- 字段与属性的映射规则 -->
        <result column="orderby" property="orderby" /> <!-- 字段与属性的映射规则 -->
        <result column="is_leaf" property="isLeaf" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, name, label, parentid, is_show, orderby, is_leaf <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

    <select id="selectTreeNodes"  parameterType="string" resultType="com.xuecheng.content.model.dto.CourseCategoryTreeDto"> <!-- 定义 select 查询语句 -->
        with recursive t1 as ( <!-- SQL 语句片段 -->
            select * from course_category where id=#{id} <!-- 占位符参数 -->
            union all <!-- SQL 语句片段 -->
            select t2.* from course_category t2 inner join t1 on t1.id = t2.parentid <!-- SQL 语句片段 -->

        ) <!-- SQL 语句片段 -->
        select * from t1 <!-- SQL 语句片段 -->
        order by t1.id <!-- SQL 语句片段 -->

    </select> <!-- SQL 语句片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseMarketMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CourseMarket; // 导入 CourseMarket 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程营销信息 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseMarketMapper extends BaseMapper<CourseMarket> { // 定义 interface CourseMarketMapper

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseMarketMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseMarketMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseMarket"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="charge" property="charge" /> <!-- 字段与属性的映射规则 -->
        <result column="price" property="price" /> <!-- 字段与属性的映射规则 -->
        <result column="original_price" property="originalPrice" /> <!-- 字段与属性的映射规则 -->
        <result column="qq" property="qq" /> <!-- 字段与属性的映射规则 -->
        <result column="wechat" property="wechat" /> <!-- 字段与属性的映射规则 -->
        <result column="phone" property="phone" /> <!-- 字段与属性的映射规则 -->
        <result column="valid_days" property="validDays" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, charge, price, original_price, qq, wechat, phone, valid_days <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CoursePublishMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CoursePublishMapper extends BaseMapper<CoursePublish> { // 定义 interface CoursePublishMapper

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CoursePublishMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CoursePublishMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CoursePublish"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="company_id" property="companyId" /> <!-- 字段与属性的映射规则 -->
        <result column="company_name" property="companyName" /> <!-- 字段与属性的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="users" property="users" /> <!-- 字段与属性的映射规则 -->
        <result column="tags" property="tags" /> <!-- 字段与属性的映射规则 -->
        <result column="username" property="username" /> <!-- 字段与属性的映射规则 -->
        <result column="mt" property="mt" /> <!-- 字段与属性的映射规则 -->
        <result column="mt_name" property="mtName" /> <!-- 字段与属性的映射规则 -->
        <result column="st" property="st" /> <!-- 字段与属性的映射规则 -->
        <result column="st_name" property="stName" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="teachmode" property="teachmode" /> <!-- 字段与属性的映射规则 -->
        <result column="pic" property="pic" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="market" property="market" /> <!-- 字段与属性的映射规则 -->
        <result column="teachplan" property="teachplan" /> <!-- 字段与属性的映射规则 -->
        <result column="teachers" property="teachers" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="online_date" property="onlineDate" /> <!-- 字段与属性的映射规则 -->
        <result column="offline_date" property="offlineDate" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
        <result column="remark" property="remark" /> <!-- 字段与属性的映射规则 -->
        <result column="charge" property="charge" /> <!-- 字段与属性的映射规则 -->
        <result column="price" property="price" /> <!-- 字段与属性的映射规则 -->
        <result column="original_price" property="originalPrice" /> <!-- 字段与属性的映射规则 -->
        <result column="valid_days" property="validDays" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, company_id, company_name, name, users, tags, username, mt, mt_name, st, st_name, grade, teachmode, pic, description, market, teachplan, teachers, create_date, online_date, offline_date, status, remark, charge, price, original_price, valid_days <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CoursePublishPreMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CoursePublishPre; // 导入 CoursePublishPre 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CoursePublishPreMapper extends BaseMapper<CoursePublishPre> { // 定义 interface CoursePublishPreMapper

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CoursePublishPreMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CoursePublishPreMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CoursePublishPre"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="company_id" property="companyId" /> <!-- 字段与属性的映射规则 -->
        <result column="company_name" property="companyName" /> <!-- 字段与属性的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="users" property="users" /> <!-- 字段与属性的映射规则 -->
        <result column="tags" property="tags" /> <!-- 字段与属性的映射规则 -->
        <result column="username" property="username" /> <!-- 字段与属性的映射规则 -->
        <result column="mt" property="mt" /> <!-- 字段与属性的映射规则 -->
        <result column="mt_name" property="mtName" /> <!-- 字段与属性的映射规则 -->
        <result column="st" property="st" /> <!-- 字段与属性的映射规则 -->
        <result column="st_name" property="stName" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="teachmode" property="teachmode" /> <!-- 字段与属性的映射规则 -->
        <result column="pic" property="pic" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="market" property="market" /> <!-- 字段与属性的映射规则 -->
        <result column="teachplan" property="teachplan" /> <!-- 字段与属性的映射规则 -->
        <result column="teachers" property="teachers" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="audit_date" property="auditDate" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
        <result column="remark" property="remark" /> <!-- 字段与属性的映射规则 -->
        <result column="charge" property="charge" /> <!-- 字段与属性的映射规则 -->
        <result column="price" property="price" /> <!-- 字段与属性的映射规则 -->
        <result column="original_price" property="originalPrice" /> <!-- 字段与属性的映射规则 -->
        <result column="valid_days" property="validDays" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, company_id, company_name, name, users, tags, username, mt, mt_name, st, st_name, grade, teachmode, pic, description, market, teachplan, teachers, create_date, audit_date, status, remark, charge, price, original_price, valid_days <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseTeacherMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CourseTeacher; // 导入 CourseTeacher 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程-教师关系表 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseTeacherMapper extends BaseMapper<CourseTeacher> { // 定义 interface CourseTeacherMapper

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/CourseTeacherMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseTeacherMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseTeacher"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="course_id" property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="teacher_name" property="teacherName" /> <!-- 字段与属性的映射规则 -->
        <result column="position" property="position" /> <!-- 字段与属性的映射规则 -->
        <result column="introduction" property="introduction" /> <!-- 字段与属性的映射规则 -->
        <result column="photograph" property="photograph" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, course_id, teacher_name, position, introduction, photograph, create_date <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/TeachplanMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.Teachplan; // 导入 Teachplan 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程计划 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface TeachplanMapper extends BaseMapper<Teachplan> { // 定义 interface TeachplanMapper

    //课程计划查询 // 保留原作者写的行内注释
    public List<TeachplanDto> selectTreeNodes(Long courseId); // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/TeachplanMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.TeachplanMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.Teachplan"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="pname" property="pname" /> <!-- 字段与属性的映射规则 -->
        <result column="parentid" property="parentid" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="media_type" property="mediaType" /> <!-- 字段与属性的映射规则 -->
        <result column="start_time" property="startTime" /> <!-- 字段与属性的映射规则 -->
        <result column="end_time" property="endTime" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="timelength" property="timelength" /> <!-- 字段与属性的映射规则 -->
        <result column="orderby" property="orderby" /> <!-- 字段与属性的映射规则 -->
        <result column="course_id" property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="course_pub_id" property="coursePubId" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
        <result column="is_preview" property="isPreview" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="change_date" property="changeDate" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, pname, parentid, grade, media_type, start_time, end_time, description, timelength, orderby, course_id, course_pub_id, status, is_preview, create_date, change_date <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

    <resultMap id="treeNodeResultMap" type="com.xuecheng.content.model.dto.TeachplanDto"> <!-- 定义结果映射 -->

        <id     column="one_id"        property="id" /> <!-- 主键列的映射规则 -->
        <result column="one_pname"      property="pname" /> <!-- 字段与属性的映射规则 -->
        <result column="one_parentid"     property="parentid" /> <!-- 字段与属性的映射规则 -->
        <result column="one_grade"  property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="one_mediaType"   property="mediaType" /> <!-- 字段与属性的映射规则 -->
        <result column="one_stratTime"   property="stratTime" /> <!-- 字段与属性的映射规则 -->
        <result column="one_endTime"   property="endTime" /> <!-- 字段与属性的映射规则 -->
        <result column="one_orderby"   property="orderby" /> <!-- 字段与属性的映射规则 -->
        <result column="one_courseId"   property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="one_coursePubId"   property="coursePubId" /> <!-- 字段与属性的映射规则 -->
        <!--映射子节点,一对多映射,ofType list中的对象类型-->
        <collection property="teachPlanTreeNodes" ofType="com.xuecheng.content.model.dto.TeachplanDto"> <!-- SQL 语句片段 -->
            <id     column="two_id"        property="id" /> <!-- 主键列的映射规则 -->
            <result column="two_pname"      property="pname" /> <!-- 字段与属性的映射规则 -->
            <result column="two_parentid"     property="parentid" /> <!-- 字段与属性的映射规则 -->
            <result column="two_grade"  property="grade" /> <!-- 字段与属性的映射规则 -->
            <result column="two_mediaType"   property="mediaType" /> <!-- 字段与属性的映射规则 -->
            <result column="two_stratTime"   property="stratTime" /> <!-- 字段与属性的映射规则 -->
            <result column="two_endTime"   property="endTime" /> <!-- 字段与属性的映射规则 -->
            <result column="two_orderby"   property="orderby" /> <!-- 字段与属性的映射规则 -->
            <result column="two_courseId"   property="courseId" /> <!-- 字段与属性的映射规则 -->
            <result column="two_coursePubId"   property="coursePubId" /> <!-- 字段与属性的映射规则 -->
            <!--一对一映射-->
            <association property="teachplanMedia" javaType="com.xuecheng.content.model.po.TeachplanMedia"> <!-- SQL 语句片段 -->
                <id column="teachplanMeidaId" property="id"/> <!-- 主键列的映射规则 -->
                <result column="mediaFilename" property="mediaFilename"/> <!-- 字段与属性的映射规则 -->
                <result column="mediaId" property="mediaId"/> <!-- 字段与属性的映射规则 -->

            </association> <!-- SQL 语句片段 -->
        </collection> <!-- SQL 语句片段 -->

    </resultMap> <!-- 结束 resultMap 区块 -->


    <select id="selectTreeNodes" parameterType="long" resultMap="treeNodeResultMap"> <!-- 定义 select 查询语句 -->

        select <!-- SQL 语句片段 -->
            one.id            one_id, <!-- SQL 语句片段 -->
            one.pname         one_pname, <!-- SQL 语句片段 -->
            one.parentid      one_parentid, <!-- SQL 语句片段 -->
            one.grade         one_grade, <!-- SQL 语句片段 -->
            one.media_type    one_mediaType, <!-- SQL 语句片段 -->
            one.start_time    one_stratTime, <!-- SQL 语句片段 -->
            one.end_time      one_endTime, <!-- SQL 语句片段 -->
            one.orderby       one_orderby, <!-- SQL 语句片段 -->
            one.course_id     one_courseId, <!-- SQL 语句片段 -->
            one.course_pub_id one_coursePubId, <!-- SQL 语句片段 -->
            two.id            two_id, <!-- SQL 语句片段 -->
            two.pname         two_pname, <!-- SQL 语句片段 -->
            two.parentid      two_parentid, <!-- SQL 语句片段 -->
            two.grade         two_grade, <!-- SQL 语句片段 -->
            two.media_type    two_mediaType, <!-- SQL 语句片段 -->
            two.start_time    two_stratTime, <!-- SQL 语句片段 -->
            two.end_time      two_endTime, <!-- SQL 语句片段 -->
            two.orderby       two_orderby, <!-- SQL 语句片段 -->
            two.course_id     two_courseId, <!-- SQL 语句片段 -->
            two.course_pub_id two_coursePubId, <!-- SQL 语句片段 -->
            m1.media_fileName mediaFilename, <!-- SQL 语句片段 -->
            m1.id             teachplanMeidaId, <!-- SQL 语句片段 -->
            m1.media_id       mediaId <!-- SQL 语句片段 -->
        from teachplan one <!-- SQL 语句片段 -->
                 left join teachplan two on two.parentid = one.id <!-- SQL 语句片段 -->
                 left join teachplan_media m1 on two.id = m1.teachplan_id <!-- SQL 语句片段 -->
        where one.parentid = 0 <!-- SQL 语句片段 -->
          and one.course_id = #{id} <!-- 占位符参数 -->
        order by one.orderby,two.orderby <!-- SQL 语句片段 -->

    </select> <!-- SQL 语句片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/TeachplanMediaMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.TeachplanMedia; // 导入 TeachplanMedia 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 *  Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface TeachplanMediaMapper extends BaseMapper<TeachplanMedia> { // 定义 interface TeachplanMediaMapper

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/mapper/TeachplanMediaMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.TeachplanMediaMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.TeachplanMedia"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="media_id" property="mediaId" /> <!-- 字段与属性的映射规则 -->
        <result column="teachplan_id" property="teachplanId" /> <!-- 字段与属性的映射规则 -->
        <result column="course_id" property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="media_fileName" property="mediaFilename" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="create_people" property="createPeople" /> <!-- 字段与属性的映射规则 -->
        <result column="change_people" property="changePeople" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, media_id, teachplan_id, course_id, media_fileName, create_date, create_people, change_people <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/service/com/xuecheng/content/service/CourseBaseInfoService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.model.dto.AddCourseDto; // 导入 AddCourseDto 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.EditCourseDto; // 导入 EditCourseDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程信息管理接口 // 保留原作者写的块注释
 * @date 2023/2/12 10:14 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseBaseInfoService { // 定义 interface CourseBaseInfoService

    /** // 保留原作者写的块注释
     * 课程分页查询 // 保留原作者写的块注释
     * @param pageParams 分页查询参数 // 保留原作者写的块注释
     * @param courseParamsDto 查询条件 // 保留原作者写的块注释
     * @param companyId 培训机构id // 保留原作者写的块注释
     * @return 查询结果 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public PageResult<CourseBase> queryCourseBaseList(Long companyId,PageParams pageParams, QueryCourseParamsDto courseParamsDto); // 执行语句

    /** // 保留原作者写的块注释
     * 新增课程 // 保留原作者写的块注释
     * @param companyId 机构id // 保留原作者写的块注释
     * @param addCourseDto 课程信息 // 保留原作者写的块注释
     * @return 课程详细信息 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CourseBaseInfoDto createCourseBase(Long companyId,AddCourseDto addCourseDto); // 执行语句

    /** // 保留原作者写的块注释
     * 根据课程id查询课程信息 // 保留原作者写的块注释
     * @param courseId 课程id // 保留原作者写的块注释
     * @return 课程详细信息 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CourseBaseInfoDto getCourseBaseInfo(Long courseId); // 执行语句

    /** // 保留原作者写的块注释
     * 修改课程 // 保留原作者写的块注释
     * @param companyId 机构id // 保留原作者写的块注释
     * @param editCourseDto 修改课程信息 // 保留原作者写的块注释
     * @return 课程详细信息 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CourseBaseInfoDto updateCourseBase(Long companyId,EditCourseDto editCourseDto); // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/CourseCategoryService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 14:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseCategoryService { // 定义 interface CourseCategoryService
 /** // 保留原作者写的块注释
  * 课程分类树形结构查询 // 保留原作者写的块注释
  * // 保留原作者写的块注释
  * @return // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public List<CourseCategoryTreeDto> queryTreeNodes(String id); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/CoursePublishService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类

import java.io.File; // 导入 File 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布相关的接口 // 保留原作者写的块注释
 * @date 2023/2/21 10:03 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CoursePublishService { // 定义 interface CoursePublishService


 /** // 保留原作者写的块注释
  * @description 获取课程预览信息 // 保留原作者写的块注释
  * @param courseId 课程id // 保留原作者写的块注释
  * @return com.xuecheng.content.model.dto.CoursePreviewDto // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/16 15:36 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 // service接口层只关注业务 // 保留原作者写的行内注释
 // controller层和service层的联系是controller层调用service实现类 // 保留原作者写的行内注释
 public CoursePreviewDto getCoursePreviewInfo(Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * @description 提交审核 // 保留原作者写的块注释
  * @param courseId  课程id // 保留原作者写的块注释
  * @return void // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/18 10:31 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void commitAudit(Long companyId,Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * @description 课程发布接口 // 保留原作者写的块注释
  * @param companyId 机构id // 保留原作者写的块注释
  * @param courseId 课程id // 保留原作者写的块注释
  * @return void // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/20 16:23 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void publish(Long companyId,Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * @description 课程静态化 // 保留原作者写的块注释
  * @param courseId  课程id // 保留原作者写的块注释
  * @return File 静态化文件 // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/23 16:59 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public File generateCourseHtml(Long courseId); // 执行语句
 /** // 保留原作者写的块注释
  * @description 上传课程静态化页面 // 保留原作者写的块注释
  * @param file  静态化文件 // 保留原作者写的块注释
  * @return void // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/23 16:59 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void  uploadCourseHtml(Long courseId, File file); // 执行语句

 /** // 保留原作者写的块注释
  * 根据课程 id查询课程发布信息 // 保留原作者写的块注释
  * @param courseId // 保留原作者写的块注释
  * @return // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public CoursePublish getCoursePublish(Long courseId); // 执行语句
 public CoursePublish getCoursePublishCache(Long courseId); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/impl/CourseBaseInfoServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.mapper.CourseMarketMapper; // 导入 CourseMarketMapper 类
import com.xuecheng.content.model.dto.AddCourseDto; // 导入 AddCourseDto 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.EditCourseDto; // 导入 EditCourseDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.model.po.CourseCategory; // 导入 CourseCategory 类
import com.xuecheng.content.model.po.CourseMarket; // 导入 CourseMarket 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Service; // 导入 Service 类
import org.springframework.transaction.annotation.Transactional; // 导入 Transactional 类

import javax.annotation.Resources; // 导入 Resources 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类
import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 10:16 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Service // 使用注解 @Service
public class CourseBaseInfoServiceImpl implements CourseBaseInfoService { // 定义 class CourseBaseInfoServiceImpl
    @Autowired // 使用注解 @Autowired
    CourseBaseMapper courseBaseMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseMarketMapper courseMarketMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseCategoryMapper courseCategoryMapper; // 执行语句


    @Override // 使用注解 @Override
    public PageResult<CourseBase> queryCourseBaseList(Long companyId,PageParams pageParams, QueryCourseParamsDto courseParamsDto) { // 定义方法 queryCourseBaseList(...)

        //拼装查询条件 // 保留原作者写的行内注释
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>(); // 创建 LambdaQueryWrapper<> 对象并赋值给 queryWrapper
        //根据名称模糊查询,在sql中拼接 course_base.name like '%值%' // 保留原作者写的行内注释
        queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName()),CourseBase::getName,courseParamsDto.getCourseName()); // 执行语句
        //根据课程审核状态查询 course_base.audit_status = ? // 保留原作者写的行内注释
        queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus()), CourseBase::getAuditStatus,courseParamsDto.getAuditStatus()); // 执行语句
        //todo:按课程发布状态查询 // 保留原作者写的行内注释
        //根据培训机构id拼装查询条件 // 保留原作者写的行内注释
        queryWrapper.eq(CourseBase::getCompanyId,companyId); // 执行语句

        //创建page分页参数对象,参数:当前页码,每页记录数 // 保留原作者写的行内注释
        Page<CourseBase> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize()); // 创建 Page<> 对象并赋值给 page
        //开始进行分页查询 // 保留原作者写的行内注释
        Page<CourseBase> pageResult = courseBaseMapper.selectPage(page, queryWrapper); // 把 courseBaseMapper.selectPage(page, queryWrapper) 赋值给 pageResult
        //数据列表 // 保留原作者写的行内注释
        List<CourseBase> items = pageResult.getRecords(); // 把 pageResult.getRecords() 的返回值赋给 items
        //总记录数 // 保留原作者写的行内注释
        long total = pageResult.getTotal(); // 把 pageResult.getTotal() 的返回值赋给 total

        //List<T> items, long counts, long page, long pageSize // 保留原作者写的行内注释
        PageResult<CourseBase> courseBasePageResult = new PageResult<CourseBase>(items,total,pageParams.getPageNo(), pageParams.getPageSize()); // 创建 PageResult<CourseBase> 对象并赋值给 courseBasePageResult
        return  courseBasePageResult; // 返回  courseBasePageResult
    } // 代码块结束

    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto dto){ // 定义方法 createCourseBase(...)

        //参数的合法性校验 // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getName())) { // 保留原作者写的行内注释
////            throw new RuntimeException("课程名称为空"); // 保留原作者写的行内注释
//            XueChengPlusException.cast("课程名称为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getMt())) { // 保留原作者写的行内注释
//            throw new RuntimeException("课程分类为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getSt())) { // 保留原作者写的行内注释
//            throw new RuntimeException("课程分类为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getGrade())) { // 保留原作者写的行内注释
//            throw new RuntimeException("课程等级为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getTeachmode())) { // 保留原作者写的行内注释
//            throw new RuntimeException("教育模式为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getUsers())) { // 保留原作者写的行内注释
//            throw new RuntimeException("适应人群为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getCharge())) { // 保留原作者写的行内注释
//            throw new RuntimeException("收费规则为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释

        //向课程基本信息表course_base写入数据 // 保留原作者写的行内注释
        CourseBase courseBaseNew = new CourseBase(); // 创建 CourseBase 对象并赋值给 courseBaseNew
        //将传入的页面的参数放到courseBaseNew对象 // 保留原作者写的行内注释
//        courseBaseNew.setName(dto.getName()); // 保留原作者写的行内注释
//        courseBaseNew.setDescription(dto.getDescription()); // 保留原作者写的行内注释
        //上边的从原始对象中get拿数据向新对象set,比较复杂 // 保留原作者写的行内注释
        BeanUtils.copyProperties(dto,courseBaseNew);//只要属性名称一致就可以拷贝 // 执行语句
        courseBaseNew.setCompanyId(companyId); // 执行语句
        courseBaseNew.setCreateDate(LocalDateTime.now()); // 执行语句
        //审核状态默认为未提交 // 保留原作者写的行内注释
        courseBaseNew.setAuditStatus("202002"); // 执行语句
        //发布状态为未发布 // 保留原作者写的行内注释
        courseBaseNew.setStatus("203001"); // 执行语句
        //插入数据库 // 保留原作者写的行内注释
        int insert = courseBaseMapper.insert(courseBaseNew); // 把 courseBaseMapper.insert(courseBaseNew) 赋值给 insert
        if(insert<=0){ // 定义方法 方法(...)
            throw new RuntimeException("添加课程失败"); // 主动抛出异常
        } // 代码块结束

        //向课程营销系courese_market写入数据 // 保留原作者写的行内注释
        CourseMarket courseMarketNew = new CourseMarket(); // 创建 CourseMarket 对象并赋值给 courseMarketNew
        //将页面输入的数据拷贝到courseMarketNew // 保留原作者写的行内注释
        BeanUtils.copyProperties(dto,courseMarketNew); // 执行语句
        //课程的id // 保留原作者写的行内注释
        Long courseId = courseBaseNew.getId(); // 把 courseBaseNew.getId() 的返回值赋给 courseId
        courseMarketNew.setId(courseId); // 执行语句
        //保存营销信息 // 保留原作者写的行内注释
        saveCourseMarket(courseMarketNew); // 执行语句
        //从数据库查询课程的详细信息,包括两部分 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfo = getCourseBaseInfo(courseId); // 把 getCourseBaseInfo(courseId) 赋值给 courseBaseInfo


        return courseBaseInfo; // 返回 courseBaseInfo
    } // 代码块结束

    //查询课程信息 // 保留原作者写的行内注释
    public CourseBaseInfoDto getCourseBaseInfo(Long courseId){ // 定义方法 getCourseBaseInfo(...)

        //从课程基本信息表查询 // 保留原作者写的行内注释
        CourseBase courseBase = courseBaseMapper.selectById(courseId); // 把 courseBaseMapper.selectById(courseId) 赋值给 courseBase
        if(courseBase==null){ // 定义方法 方法(...)
            return null; // 返回 null
        } // 代码块结束
        //从课程营销表查询 // 保留原作者写的行内注释
        CourseMarket courseMarket = courseMarketMapper.selectById(courseId); // 把 courseMarketMapper.selectById(courseId) 赋值给 courseMarket

        //组装在一起 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto(); // 创建 CourseBaseInfoDto 对象并赋值给 courseBaseInfoDto
        BeanUtils.copyProperties(courseBase,courseBaseInfoDto); // 执行语句
        if(courseMarket!=null){ // 定义方法 方法(...)
            BeanUtils.copyProperties(courseMarket,courseBaseInfoDto); // 执行语句
        } // 代码块结束

        //通过courseCategoryMapper查询分类信息,将分类名称放在courseBaseInfoDto对象 // 保留原作者写的行内注释
        CourseCategory mtObj = courseCategoryMapper.selectById(courseBase.getMt()); // 把 courseCategoryMapper.selectById(courseBase.getMt()) 赋值给 mtObj
        String mtName = mtObj.getName();//大分类名称 // 把 mtObj.getName() 的返回值赋给 mtName
        courseBaseInfoDto.setMtName(mtName); // 执行语句
        CourseCategory stObj = courseCategoryMapper.selectById(courseBase.getSt()); // 把 courseCategoryMapper.selectById(courseBase.getSt()) 赋值给 stObj
        String stName = stObj.getName();//小分类名称 // 把 stObj.getName() 的返回值赋给 stName
        courseBaseInfoDto.setStName(stName); // 执行语句

        return courseBaseInfoDto; // 返回 courseBaseInfoDto

    } // 代码块结束

    @Override // 使用注解 @Override
    public CourseBaseInfoDto updateCourseBase(Long companyId, EditCourseDto editCourseDto) { // 定义方法 updateCourseBase(...)

        //拿到课程id // 保留原作者写的行内注释
        Long courseId = editCourseDto.getId(); // 把 editCourseDto.getId() 的返回值赋给 courseId
        //查询课程信息 // 保留原作者写的行内注释
        CourseBase courseBase = courseBaseMapper.selectById(courseId); // 把 courseBaseMapper.selectById(courseId) 赋值给 courseBase
        if(courseBase == null){ // 定义方法 方法(...)
            XueChengPlusException.cast("课程不存在"); // 执行语句
        } // 代码块结束

        //数据合法性校验 // 保留原作者写的行内注释
        //根据具体的业务逻辑去校验 // 保留原作者写的行内注释
        //本机构只能修改本机构的课程 // 保留原作者写的行内注释
        if(!companyId.equals(courseBase.getCompanyId())){ // 定义方法 方法(...)
            XueChengPlusException.cast("本机构只能修改本机构的课程"); // 执行语句
        } // 代码块结束

        //封装数据 // 保留原作者写的行内注释
        BeanUtils.copyProperties(editCourseDto,courseBase); // 执行语句
        //修改时间 // 保留原作者写的行内注释
        courseBase.setChangeDate(LocalDateTime.now()); // 执行语句

        //更新数据库 // 保留原作者写的行内注释
        int i = courseBaseMapper.updateById(courseBase); // 把 courseBaseMapper.updateById(courseBase) 赋值给 i
        if(i<=0){ // 定义方法 方法(...)
            XueChengPlusException.cast("修改课程失败"); // 执行语句
        } // 代码块结束
        //更新营销信息 // 保留原作者写的行内注释
        CourseMarket courseMarket = new CourseMarket(); // 创建 CourseMarket 对象并赋值给 courseMarket
        BeanUtils.copyProperties(editCourseDto,courseMarket); // 执行语句

        saveCourseMarket(courseMarket); // 执行语句
        //查询课程信息 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfo = getCourseBaseInfo(courseId); // 把 getCourseBaseInfo(courseId) 赋值给 courseBaseInfo

        return courseBaseInfo; // 返回 courseBaseInfo
    } // 代码块结束

    //单独写一个方法保存营销信息,逻辑:存在则更新,不存在则添加 // 保留原作者写的行内注释
    private int saveCourseMarket(CourseMarket courseMarketNew){ // 定义方法 saveCourseMarket(...)

        //参数的合法性校验 // 保留原作者写的行内注释
        String charge = courseMarketNew.getCharge(); // 把 courseMarketNew.getCharge() 的返回值赋给 charge
        if(StringUtils.isEmpty(charge)){ // 定义方法 方法(...)
            throw new RuntimeException("收费规则为空"); // 主动抛出异常
        } // 代码块结束
        //如果课程收费,价格没有填写也需要抛出异常 // 保留原作者写的行内注释
        if(charge.equals("201001")){ // 定义方法 方法(...)
           if(courseMarketNew.getPrice() ==null || courseMarketNew.getPrice().floatValue()<=0){ // 定义方法 方法(...)
//               throw new RuntimeException("课程的价格不能为空并且必须大于0"); // 保留原作者写的行内注释
               XueChengPlusException.cast("课程的价格不能为空并且必须大于0"); // 执行语句
           } // 代码块结束
        } // 代码块结束

        //从数据库查询营销信息,存在则更新,不存在则添加 // 保留原作者写的行内注释
        Long id = courseMarketNew.getId();//主键 // 把 courseMarketNew.getId() 的返回值赋给 id
        CourseMarket courseMarket = courseMarketMapper.selectById(id); // 把 courseMarketMapper.selectById(id) 赋值给 courseMarket
        if(courseMarket == null){ // 定义方法 方法(...)
            //插入数据库 // 保留原作者写的行内注释
            int insert = courseMarketMapper.insert(courseMarketNew); // 把 courseMarketMapper.insert(courseMarketNew) 赋值给 insert
            return insert; // 返回 insert
        }else{ // 代码逻辑
            //将courseMarketNew拷贝到courseMarket // 保留原作者写的行内注释
            BeanUtils.copyProperties(courseMarketNew,courseMarket); // 执行语句
            courseMarket.setId(courseMarketNew.getId()); // 执行语句
            //更新 // 保留原作者写的行内注释
            int i = courseMarketMapper.updateById(courseMarket); // 把 courseMarketMapper.updateById(courseMarket) 赋值给 i
            return i; // 返回 i
        } // 代码块结束


    } // 代码块结束



} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/impl/CourseCategoryServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.service.CourseCategoryService; // 导入 CourseCategoryService 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Service; // 导入 Service 类

import java.util.ArrayList; // 导入 ArrayList 类
import java.util.List; // 导入 List 类
import java.util.Map; // 导入 Map 类
import java.util.stream.Collectors; // 导入 Collectors 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 14:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Service // 使用注解 @Service
public class CourseCategoryServiceImpl implements CourseCategoryService { // 定义 class CourseCategoryServiceImpl

    @Autowired // 使用注解 @Autowired
    CourseCategoryMapper courseCategoryMapper; // 执行语句

    @Override // 使用注解 @Override
    public List<CourseCategoryTreeDto> queryTreeNodes(String id) { // 定义方法 queryTreeNodes(...)
        //调用mapper递归查询出分类信息 // 保留原作者写的行内注释
        List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes(id); // 把 courseCategoryMapper.selectTreeNodes(id) 赋值给 courseCategoryTreeDtos

        //找到每个节点的子节点,最终封装成List<CourseCategoryTreeDto> // 保留原作者写的行内注释
        //先将list转成map,key就是结点的id,value就是CourseCategoryTreeDto对象,目的就是为了方便从map获取结点,filter(item->!id.equals(item.getId()))把根结点排除 // 保留原作者写的行内注释
        Map<String, CourseCategoryTreeDto> mapTemp = courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2)); // 把 courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2)) 赋值给 mapTemp
        //定义一个list作为最终返回的list // 保留原作者写的行内注释
        List<CourseCategoryTreeDto> courseCategoryList = new ArrayList<>(); // 创建 ArrayList<> 对象并赋值给 courseCategoryList
        //从头遍历 List<CourseCategoryTreeDto> ,一边遍历一边找子节点放在父节点的childrenTreeNodes // 保留原作者写的行内注释
        courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).forEach(item -> { // 定义方法 方法(...)
            if (item.getParentid().equals(id)) { // 定义方法 方法(...)
                courseCategoryList.add(item); // 执行语句
            } // 代码块结束
            //找到节点的父节点 // 保留原作者写的行内注释
            CourseCategoryTreeDto courseCategoryParent = mapTemp.get(item.getParentid()); // 把 mapTemp.get(item.getParentid()) 赋值给 courseCategoryParent
            if(courseCategoryParent!=null){ // 定义方法 方法(...)
                if(courseCategoryParent.getChildrenTreeNodes()==null){ // 定义方法 方法(...)
                    //如果该父节点的ChildrenTreeNodes属性为空要new一个集合,因为要向该集合中放它的子节点 // 保留原作者写的行内注释
                    courseCategoryParent.setChildrenTreeNodes(new ArrayList<CourseCategoryTreeDto>()); // 执行语句
                } // 代码块结束
                //到每个节点的子节点放在父节点的childrenTreeNodes属性中 // 保留原作者写的行内注释
                courseCategoryParent.getChildrenTreeNodes().add(item); // 执行语句
            } // 代码块结束



        }); // 执行语句

        return courseCategoryList; // 返回 courseCategoryList
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/impl/CoursePublishServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.alibaba.fastjson.JSON; // 导入 JSON 类
import com.xuecheng.base.exception.CommonError; // 导入 CommonError 类
import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.content.config.MultipartSupportConfig; // 导入 MultipartSupportConfig 类
import com.xuecheng.content.feignclient.MediaServiceClient; // 导入 MediaServiceClient 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.mapper.CourseMarketMapper; // 导入 CourseMarketMapper 类
import com.xuecheng.content.mapper.CoursePublishMapper; // 导入 CoursePublishMapper 类
import com.xuecheng.content.mapper.CoursePublishPreMapper; // 导入 CoursePublishPreMapper 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.model.po.CourseMarket; // 导入 CourseMarket 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类
import com.xuecheng.content.model.po.CoursePublishPre; // 导入 CoursePublishPre 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import com.xuecheng.content.service.TeachplanService; // 导入 TeachplanService 类
import com.xuecheng.messagesdk.model.po.MqMessage; // 导入 MqMessage 类
import com.xuecheng.messagesdk.service.MqMessageService; // 导入 MqMessageService 类
import freemarker.cache.ClassTemplateLoader; // 导入 ClassTemplateLoader 类
import freemarker.template.Configuration; // 导入 Configuration 类
import freemarker.template.Template; // 导入 Template 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.apache.commons.io.IOUtils; // 导入 IOUtils 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.redisson.api.RLock; // 导入 RLock 类
import org.redisson.api.RedissonClient; // 导入 RedissonClient 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.data.redis.core.RedisTemplate; // 导入 RedisTemplate 类
import org.springframework.stereotype.Service; // 导入 Service 类
import org.springframework.transaction.annotation.Transactional; // 导入 Transactional 类
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; // 导入 FreeMarkerTemplateUtils 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.File; // 导入 File 类
import java.io.FileOutputStream; // 导入 FileOutputStream 类
import java.io.InputStream; // 导入 InputStream 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类
import java.util.HashMap; // 导入 HashMap 类
import java.util.List; // 导入 List 类
import java.util.Random; // 导入 Random 类
import java.util.concurrent.TimeUnit; // 导入 TimeUnit 类
import java.util.concurrent.locks.ReentrantLock; // 导入 ReentrantLock 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布相关接口实现 // 保留原作者写的块注释
 * @date 2023/2/21 10:04 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Service // 使用注解 @Service
public class CoursePublishServiceImpl implements CoursePublishService { // 定义 class CoursePublishServiceImpl

    @Autowired // 使用注解 @Autowired
    CourseBaseInfoService courseBaseInfoService; // 执行语句

    @Autowired // 使用注解 @Autowired
    TeachplanService teachplanService; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseBaseMapper courseBaseMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseMarketMapper courseMarketMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CoursePublishPreMapper coursePublishPreMapper; // 执行语句
    @Autowired // 使用注解 @Autowired
    CoursePublishMapper coursePublishMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    MqMessageService mqMessageService; // 执行语句
    @Autowired // 使用注解 @Autowired
    MediaServiceClient mediaServiceClient; // 执行语句

    @Autowired // 使用注解 @Autowired
    RedisTemplate redisTemplate; // 执行语句

    @Autowired // 使用注解 @Autowired
    RedissonClient redissonClient; // 执行语句

    // 课程预览 // 保留原作者写的行内注释
    @Override // 使用注解 @Override
    public CoursePreviewDto getCoursePreviewInfo(Long courseId) { // 定义方法 getCoursePreviewInfo(...)
        CoursePreviewDto coursePreviewDto = new CoursePreviewDto(); // 创建 CoursePreviewDto 对象并赋值给 coursePreviewDto
        //课程基本信息,营销信息 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId); // 把 courseBaseInfoService.getCourseBaseInfo(courseId) 赋值给 courseBaseInfo
        coursePreviewDto.setCourseBase(courseBaseInfo); // 执行语句
        //课程计划信息 // 保留原作者写的行内注释
        List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId); // 把 teachplanService.findTeachplanTree(courseId) 赋值给 teachplanTree
        coursePreviewDto.setTeachplans(teachplanTree); // 执行语句

        return coursePreviewDto; // 返回 coursePreviewDto
    } // 代码块结束

    // 提交审核 // 保留原作者写的行内注释
    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public void commitAudit(Long companyId, Long courseId) { // 定义方法 commitAudit(...)

        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId); // 把 courseBaseInfoService.getCourseBaseInfo(courseId) 赋值给 courseBaseInfo
        if (courseBaseInfo == null) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程找不到"); // 执行语句
        } // 代码块结束
        //审核状态 // 保留原作者写的行内注释
        String auditStatus = courseBaseInfo.getAuditStatus(); // 把 courseBaseInfo.getAuditStatus() 的返回值赋给 auditStatus

        //如果课程的审核状态为已提交则不允许提交 // 保留原作者写的行内注释
        if (auditStatus.equals("202003")) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程已提交请等待审核"); // 执行语句
        } // 代码块结束
        //本机构只能提交本机构的课程 // 保留原作者写的行内注释
        //todo:本机构只能提交本机构的课程 // 保留原作者写的行内注释

        //课程的图片、计划信息没有填写也不允许提交 // 保留原作者写的行内注释
        String pic = courseBaseInfo.getPic(); // 把 courseBaseInfo.getPic() 的返回值赋给 pic
        if (StringUtils.isEmpty(pic)) { // 定义方法 方法(...)
            XueChengPlusException.cast("请求上传课程图片"); // 执行语句
        } // 代码块结束
        //查询课程计划 // 保留原作者写的行内注释
        //课程计划信息 // 保留原作者写的行内注释
        List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId); // 把 teachplanService.findTeachplanTree(courseId) 赋值给 teachplanTree
        if (teachplanTree == null || teachplanTree.size() == 0) { // 定义方法 方法(...)
            XueChengPlusException.cast("请编写课程计划"); // 执行语句
        } // 代码块结束

        //查询到课程基本信息、营销信息、计划等信息插入到课程预发布表 // 保留原作者写的行内注释
        CoursePublishPre coursePublishPre = new CoursePublishPre(); // 创建 CoursePublishPre 对象并赋值给 coursePublishPre
        BeanUtils.copyProperties(courseBaseInfo, coursePublishPre); // 执行语句
        //设置机构id // 保留原作者写的行内注释
        coursePublishPre.setCompanyId(companyId); // 执行语句
        //营销信息 // 保留原作者写的行内注释
        CourseMarket courseMarket = courseMarketMapper.selectById(courseId); // 把 courseMarketMapper.selectById(courseId) 赋值给 courseMarket
        //转json // 保留原作者写的行内注释
        String courseMarketJson = JSON.toJSONString(courseMarket); // 把 JSON.toJSONString(courseMarket) 赋值给 courseMarketJson
        coursePublishPre.setMarket(courseMarketJson); // 执行语句
        //计划信息 // 保留原作者写的行内注释
        //转json // 保留原作者写的行内注释
        String teachplanTreeJson = JSON.toJSONString(teachplanTree); // 把 JSON.toJSONString(teachplanTree) 赋值给 teachplanTreeJson
        coursePublishPre.setTeachplan(teachplanTreeJson); // 执行语句
        //状态为已提交 // 保留原作者写的行内注释
        coursePublishPre.setStatus("202003"); // 执行语句
        //提交时间 // 保留原作者写的行内注释
        coursePublishPre.setCreateDate(LocalDateTime.now()); // 执行语句
        //查询预发布表,如果有记录则更新,没有则插入 // 保留原作者写的行内注释
        CoursePublishPre coursePublishPreObj = coursePublishPreMapper.selectById(courseId); // 把 coursePublishPreMapper.selectById(courseId) 赋值给 coursePublishPreObj
        if (coursePublishPreObj == null) { // 定义方法 方法(...)
            //插入 // 保留原作者写的行内注释
            coursePublishPreMapper.insert(coursePublishPre); // 执行语句
        } else { // 代码逻辑
            //更新 // 保留原作者写的行内注释
            coursePublishPreMapper.updateById(coursePublishPre); // 执行语句
        } // 代码块结束

        //更新课程基本信息表的审核状态为已提交 // 保留原作者写的行内注释
        CourseBase courseBase = courseBaseMapper.selectById(courseId); // 把 courseBaseMapper.selectById(courseId) 赋值给 courseBase
        courseBase.setAuditStatus("202003");//审核状态为已提交 // 执行语句

        courseBaseMapper.updateById(courseBase); // 执行语句
    } // 代码块结束

    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public void publish(Long companyId, Long courseId) { // 定义方法 publish(...)

        //查询预发布表 // 保留原作者写的行内注释
        CoursePublishPre coursePublishPre = coursePublishPreMapper.selectById(courseId); // 把 coursePublishPreMapper.selectById(courseId) 赋值给 coursePublishPre
        if (coursePublishPre == null) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程没有审核记录,无法发布"); // 执行语句
        } // 代码块结束
        //状态 // 保留原作者写的行内注释
        String status = coursePublishPre.getStatus(); // 把 coursePublishPre.getStatus() 的返回值赋给 status
        //课程如果没有审核通过不允许发布 // 保留原作者写的行内注释
        if (!status.equals("202004")) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程没有审核通过不允许发布"); // 执行语句
        } // 代码块结束

        //向课程发布表写入数据 // 保留原作者写的行内注释
        CoursePublish coursePublish = new CoursePublish(); // 创建 CoursePublish 对象并赋值给 coursePublish
        BeanUtils.copyProperties(coursePublishPre, coursePublish); // 执行语句
        //先查询课程发布,如果有则更新,没有再添加 // 保留原作者写的行内注释
        CoursePublish coursePublishObj = coursePublishMapper.selectById(courseId); // 把 coursePublishMapper.selectById(courseId) 赋值给 coursePublishObj
        if (coursePublishObj == null) { // 定义方法 方法(...)
            coursePublishMapper.insert(coursePublish); // 执行语句
        } else { // 代码逻辑
            coursePublishMapper.updateById(coursePublish); // 执行语句
        } // 代码块结束

        //向消息表写入数据 // 保留原作者写的行内注释
//        mqMessageService.addMessage("course_publish",String.valueOf(courseId),null,null); // 保留原作者写的行内注释
        saveCoursePublishMessage(courseId); // 执行语句

        //将预发布表数据删除 // 保留原作者写的行内注释
        coursePublishPreMapper.deleteById(courseId); // 执行语句

    } // 代码块结束

    @Override // 使用注解 @Override
    public File generateCourseHtml(Long courseId) { // 定义方法 generateCourseHtml(...)

        Configuration configuration = new Configuration(Configuration.getVersion()); // 创建 Configuration 对象并赋值给 configuration
        //最终的静态文件 // 保留原作者写的行内注释
        File htmlFile = null; // 把变量 htmlFile 初始化为 null
        try { // 尝试执行可能抛出异常的代码
            //拿到classpath路径 // 保留原作者写的行内注释
//            String classpath = this.getClass().getResource("/").getPath(); // 保留原作者写的行内注释
//            //指定模板的目录 // 保留原作者写的行内注释
//            configuration.setDirectoryForTemplateLoading(new File(classpath+"/templates/")); // 保留原作者写的行内注释
            //更改为如下方式 // 保留原作者写的行内注释
            configuration.setTemplateLoader(new ClassTemplateLoader(this.getClass().getClassLoader(), "/templates")); // 执行语句
            //指定编码 // 保留原作者写的行内注释
            configuration.setDefaultEncoding("utf-8"); // 执行语句

            //得到模板 // 保留原作者写的行内注释
            Template template = configuration.getTemplate("course_template.ftl"); // 把 configuration.getTemplate("course_template.ftl") 赋值给 template
            //准备数据 // 保留原作者写的行内注释
            CoursePreviewDto coursePreviewInfo = this.getCoursePreviewInfo(courseId); // 把 this.getCoursePreviewInfo(courseId) 赋值给 coursePreviewInfo
            HashMap<String, Object> map = new HashMap<>(); // 创建 HashMap<> 对象并赋值给 map
            map.put("model", coursePreviewInfo); // 执行语句

            //Template template 模板, Object model 数据 // 保留原作者写的行内注释
            String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); // 把 FreeMarkerTemplateUtils.processTemplateIntoString(template, map) 赋值给 html
            //输入流 // 保留原作者写的行内注释
            InputStream inputStream = IOUtils.toInputStream(html, "utf-8"); // 把 IOUtils.toInputStream(html, "utf-8") 赋值给 inputStream
            htmlFile = File.createTempFile("coursepublish", ".html"); // 把 File.createTempFile("coursepublish", ".html") 赋值给 htmlFile
            //输出文件 // 保留原作者写的行内注释
            FileOutputStream outputStream = new FileOutputStream(htmlFile); // 创建 FileOutputStream 对象并赋值给 outputStream
            //使用流将html写入文件 // 保留原作者写的行内注释
            IOUtils.copy(inputStream, outputStream); // 执行语句
        } catch (Exception ex) { // 定义方法 catch(...)
            log.error("页面静态化出现问题,课程id:{}", courseId, ex); // 执行语句
            ex.printStackTrace(); // 执行语句
        } // 代码块结束

        return htmlFile; // 返回 htmlFile
    } // 代码块结束

    @Override // 使用注解 @Override
    public void uploadCourseHtml(Long courseId, File file) { // 定义方法 uploadCourseHtml(...)
        try { // 尝试执行可能抛出异常的代码
            //将file转成MultipartFile // 保留原作者写的行内注释
            MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(file); // 把 MultipartSupportConfig.getMultipartFile(file) 赋值给 multipartFile
            //远程调用得到返回值 // 保留原作者写的行内注释
            String upload = mediaServiceClient.upload(multipartFile, "course/" + courseId + ".html"); // 把 mediaServiceClient.upload(multipartFile, "course/" + courseId + ".html") 赋值给 upload
            if (upload == null) { // 定义方法 方法(...)
                log.debug("远程调用走降级逻辑得到上传的结果为null,课程id:{}", courseId); // 执行语句
                XueChengPlusException.cast("上传静态文件过程中存在异常"); // 执行语句
            } // 代码块结束
        } catch (Exception ex) { // 定义方法 catch(...)
            ex.printStackTrace(); // 执行语句
            XueChengPlusException.cast("上传静态文件过程中存在异常"); // 执行语句
        } // 代码块结束

    } // 代码块结束

    /** // 保留原作者写的块注释
     * @param courseId 课程id // 保留原作者写的块注释
     * @return void // 保留原作者写的块注释
     * @description 保存消息表记录 // 保留原作者写的块注释
     * @author Mr.M // 保留原作者写的块注释
     * @date 2022/9/20 16:32 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private void saveCoursePublishMessage(Long courseId) { // 定义方法 saveCoursePublishMessage(...)

        MqMessage mqMessage = mqMessageService.addMessage("course_publish", String.valueOf(courseId), null, null); // 把 mqMessageService.addMessage("course_publish", String.valueOf(courseId), null, null) 赋值给 mqMessage
        if (mqMessage == null) { // 定义方法 方法(...)
            XueChengPlusException.cast(CommonError.UNKOWN_ERROR); // 执行语句
        } // 代码块结束

    } // 代码块结束

    /** // 保留原作者写的块注释
     * 根据课程 id查询课程发布信息 // 保留原作者写的块注释
     * // 保留原作者写的块注释
     * @param courseId // 保留原作者写的块注释
     * @return // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CoursePublish getCoursePublish(Long courseId) { // 定义方法 getCoursePublish(...)
        CoursePublish coursePublish = coursePublishMapper.selectById(courseId); // 把 coursePublishMapper.selectById(courseId) 赋值给 coursePublish
        return coursePublish; // 返回 coursePublish
    } // 代码块结束

    //解决缓存穿透 // 保留原作者写的行内注释
//    @Override // 保留原作者写的行内注释
//    public CoursePublish getCoursePublishCache(Long courseId) { // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        //从缓存中查询 // 保留原作者写的行内注释
//        Object jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 保留原作者写的行内注释
//        //缓存中有 // 保留原作者写的行内注释
//        if(jsonObj!=null){ // 保留原作者写的行内注释
////            System.out.println("=============从缓存中查询============="); // 保留原作者写的行内注释
//            //缓存中有直接返回数据 // 保留原作者写的行内注释
//            String jsonString = jsonObj.toString(); // 保留原作者写的行内注释
//            if("null".equals(jsonString)){ // 保留原作者写的行内注释
//                return null; // 保留原作者写的行内注释
//            } // 保留原作者写的行内注释
//            CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 保留原作者写的行内注释
//            return coursePublish; // 保留原作者写的行内注释
//        }else{ // 保留原作者写的行内注释
//            System.out.println("==查询数据库=="); // 保留原作者写的行内注释
//            //从数据库查询 // 保留原作者写的行内注释
//            CoursePublish coursePublish = getCoursePublish(courseId); // 保留原作者写的行内注释
////            if(coursePublish!=null){ // 保留原作者写的行内注释
//                //查询完成再存储到redis // 保留原作者写的行内注释
//                redisTemplate.opsForValue().set("course:"+courseId,JSON.toJSONString(coursePublish),30, TimeUnit.SECONDS); // 保留原作者写的行内注释
////            } // 保留原作者写的行内注释
//            return coursePublish; // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
//    } // 保留原作者写的行内注释

    //使用同步锁解决缓存击穿 // 保留原作者写的行内注释
//    @Override // 保留原作者写的行内注释
//    public CoursePublish getCoursePublishCache(Long courseId) { // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//            //从缓存中查询 // 保留原作者写的行内注释
//            Object jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 保留原作者写的行内注释
//            //缓存中有 // 保留原作者写的行内注释
//            if(jsonObj!=null){ // 保留原作者写的行内注释
////            System.out.println("=============从缓存中查询============="); // 保留原作者写的行内注释
//                //缓存中有直接返回数据 // 保留原作者写的行内注释
//                String jsonString = jsonObj.toString(); // 保留原作者写的行内注释
//                if("null".equals(jsonString)){ // 保留原作者写的行内注释
//                    return null; // 保留原作者写的行内注释
//                } // 保留原作者写的行内注释
//                CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 保留原作者写的行内注释
//                return coursePublish; // 保留原作者写的行内注释
//            }else{ // 保留原作者写的行内注释
//                synchronized (this){ // 保留原作者写的行内注释
//                    //再次查询一下缓存 // 保留原作者写的行内注释
//                    //从缓存中查询 // 保留原作者写的行内注释
//                    jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 保留原作者写的行内注释
//                    //缓存中有 // 保留原作者写的行内注释
//                    if(jsonObj!=null) { // 保留原作者写的行内注释
//                        //缓存中有直接返回数据 // 保留原作者写的行内注释
//                        String jsonString = jsonObj.toString(); // 保留原作者写的行内注释
//                        if("null".equals(jsonString)){ // 保留原作者写的行内注释
//                            return null; // 保留原作者写的行内注释
//                        } // 保留原作者写的行内注释
//                        CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 保留原作者写的行内注释
//                        return coursePublish; // 保留原作者写的行内注释
//                    } // 保留原作者写的行内注释
//                    System.out.println("==查询数据库=="); // 保留原作者写的行内注释
//                    //从数据库查询 // 保留原作者写的行内注释
//                    CoursePublish coursePublish = getCoursePublish(courseId); // 保留原作者写的行内注释
//                    //查询完成再存储到redis // 保留原作者写的行内注释
//                    redisTemplate.opsForValue().set("course:"+courseId,JSON.toJSONString(coursePublish),300, TimeUnit.SECONDS); // 保留原作者写的行内注释
//                    return coursePublish; // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//                } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//            } // 保留原作者写的行内注释

    //使用redisson实现分布式锁 // 保留原作者写的行内注释
    @Override // 使用注解 @Override
    public CoursePublish getCoursePublishCache(Long courseId) { // 定义方法 getCoursePublishCache(...)


        //从缓存中查询 // 保留原作者写的行内注释
        Object jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 把 redisTemplate.opsForValue().get("course:" + courseId) 赋值给 jsonObj
        //缓存中有 // 保留原作者写的行内注释
        if (jsonObj != null) { // 定义方法 方法(...)
//            System.out.println("=============从缓存中查询============="); // 保留原作者写的行内注释
            //缓存中有直接返回数据 // 保留原作者写的行内注释
            String jsonString = jsonObj.toString(); // 把 jsonObj.toString() 的返回值赋给 jsonString
            if ("null".equals(jsonString)) { // 定义方法 方法(...)
                return null; // 返回 null
            } // 代码块结束
            CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 把 JSON.parseObject(jsonString, CoursePublish.class) 赋值给 coursePublish
            return coursePublish; // 返回 coursePublish
        } else { // 代码逻辑
            RLock lock = redissonClient.getLock("coursequerylock:" + courseId); // 把 redissonClient.getLock("coursequerylock:" + courseId) 赋值给 lock
            //获取分布式锁 // 保留原作者写的行内注释
            lock.lock(); // 执行语句
            try { // 尝试执行可能抛出异常的代码
                //再次查询一下缓存 // 保留原作者写的行内注释
                //从缓存中查询 // 保留原作者写的行内注释
                jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 把 redisTemplate.opsForValue().get("course:" + courseId) 赋值给 jsonObj
                //缓存中有 // 保留原作者写的行内注释
                if (jsonObj != null) { // 定义方法 方法(...)
                    //缓存中有直接返回数据 // 保留原作者写的行内注释
                    String jsonString = jsonObj.toString(); // 把 jsonObj.toString() 的返回值赋给 jsonString
                    if ("null".equals(jsonString)) { // 定义方法 方法(...)
                        return null; // 返回 null
                    } // 代码块结束
                    CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 把 JSON.parseObject(jsonString, CoursePublish.class) 赋值给 coursePublish
                    return coursePublish; // 返回 coursePublish
                } // 代码块结束
                System.out.println("==查询数据库=="); // 执行语句
//                try { // 保留原作者写的行内注释
//                    //手动延迟,测试锁的续期功能 // 保留原作者写的行内注释
//                    Thread.sleep(60000); // 保留原作者写的行内注释
//                } catch (InterruptedException e) { // 保留原作者写的行内注释
//                    throw new RuntimeException(e); // 保留原作者写的行内注释
//                } // 保留原作者写的行内注释
                //从数据库查询 // 保留原作者写的行内注释
                CoursePublish coursePublish = getCoursePublish(courseId); // 把 getCoursePublish(courseId) 赋值给 coursePublish
                //查询完成再存储到redis // 保留原作者写的行内注释
                redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish), 300, TimeUnit.SECONDS); // 执行语句
                return coursePublish; // 返回 coursePublish

            } finally { // 代码逻辑
                //手动释放锁 // 保留原作者写的行内注释
                lock.unlock(); // 执行语句
            } // 代码块结束
        } // 代码块结束


    } // 代码块结束

} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/impl/TeachplanServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.content.mapper.TeachplanMapper; // 导入 TeachplanMapper 类
import com.xuecheng.content.mapper.TeachplanMediaMapper; // 导入 TeachplanMediaMapper 类
import com.xuecheng.content.model.dto.BindTeachplanMediaDto; // 导入 BindTeachplanMediaDto 类
import com.xuecheng.content.model.dto.SaveTeachplanDto; // 导入 SaveTeachplanDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.Teachplan; // 导入 Teachplan 类
import com.xuecheng.content.model.po.TeachplanMedia; // 导入 TeachplanMedia 类
import com.xuecheng.content.service.TeachplanService; // 导入 TeachplanService 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Service; // 导入 Service 类
import org.springframework.transaction.annotation.Transactional; // 导入 Transactional 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/14 12:11 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Service // 使用注解 @Service
public class TeachplanServiceImpl implements TeachplanService { // 定义 class TeachplanServiceImpl

    @Autowired // 使用注解 @Autowired
    TeachplanMapper teachplanMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    TeachplanMediaMapper teachplanMediaMapper; // 执行语句

    @Override // 使用注解 @Override
    public List<TeachplanDto> findTeachplanTree(Long courseId) { // 定义方法 findTeachplanTree(...)
        List<TeachplanDto> teachplanDtos = teachplanMapper.selectTreeNodes(courseId); // 把 teachplanMapper.selectTreeNodes(courseId) 赋值给 teachplanDtos
        return teachplanDtos; // 返回 teachplanDtos
    } // 代码块结束

    private int getTeachplanCount(Long courseId,Long parentId){ // 定义方法 getTeachplanCount(...)
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>(); // 创建 LambdaQueryWrapper<> 对象并赋值给 queryWrapper
        queryWrapper = queryWrapper.eq(Teachplan::getCourseId, courseId).eq(Teachplan::getParentid, parentId); // 把 queryWrapper.eq(Teachplan::getCourseId, courseId).eq(Teachplan::getParentid, parentId) 赋值给 queryWrapper
        Integer count = teachplanMapper.selectCount(queryWrapper); // 把 teachplanMapper.selectCount(queryWrapper) 赋值给 count
        return  count+1; // 返回  count+1
    } // 代码块结束
    @Override // 使用注解 @Override
    public void saveTeachplan(SaveTeachplanDto saveTeachplanDto) { // 定义方法 saveTeachplan(...)
        //通过课程计划id判断是新增和修改 // 保留原作者写的行内注释
        Long teachplanId = saveTeachplanDto.getId(); // 把 saveTeachplanDto.getId() 的返回值赋给 teachplanId
        if(teachplanId ==null){ // 定义方法 方法(...)
            //新增 // 保留原作者写的行内注释
            Teachplan teachplan = new Teachplan(); // 创建 Teachplan 对象并赋值给 teachplan
            BeanUtils.copyProperties(saveTeachplanDto,teachplan); // 执行语句
            //确定排序字段,找到它的同级节点个数,排序字段就是个数加1  select count(1) from teachplan where course_id=117 and parentid=268 // 保留原作者写的行内注释
            Long parentid = saveTeachplanDto.getParentid(); // 把 saveTeachplanDto.getParentid() 的返回值赋给 parentid
            Long courseId = saveTeachplanDto.getCourseId(); // 把 saveTeachplanDto.getCourseId() 的返回值赋给 courseId
            int teachplanCount = getTeachplanCount(courseId, parentid); // 把 getTeachplanCount(courseId, parentid) 赋值给 teachplanCount
            teachplan.setOrderby(teachplanCount); // 执行语句
            teachplanMapper.insert(teachplan); // 执行语句

        }else{ // 代码逻辑
            //修改 // 保留原作者写的行内注释
            Teachplan teachplan = teachplanMapper.selectById(teachplanId); // 把 teachplanMapper.selectById(teachplanId) 赋值给 teachplan
            //将参数复制到teachplan // 保留原作者写的行内注释
            BeanUtils.copyProperties(saveTeachplanDto,teachplan); // 执行语句
            teachplanMapper.updateById(teachplan); // 执行语句
        } // 代码块结束

    } // 代码块结束

    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public void associationMedia(BindTeachplanMediaDto bindTeachplanMediaDto) { // 定义方法 associationMedia(...)
        //课程计划id // 保留原作者写的行内注释
        Long teachplanId = bindTeachplanMediaDto.getTeachplanId(); // 把 bindTeachplanMediaDto.getTeachplanId() 的返回值赋给 teachplanId
        Teachplan teachplan = teachplanMapper.selectById(teachplanId); // 把 teachplanMapper.selectById(teachplanId) 赋值给 teachplan
        if(teachplan == null){ // 定义方法 方法(...)
            XueChengPlusException.cast("课程计划不存在"); // 执行语句
        } // 代码块结束

        //先删除原有记录,根据课程计划id删除它所绑定的媒资 // 保留原作者写的行内注释
        int delete = teachplanMediaMapper.delete(new LambdaQueryWrapper<TeachplanMedia>().eq(TeachplanMedia::getTeachplanId, bindTeachplanMediaDto.getTeachplanId())); // 把 teachplanMediaMapper.delete(new LambdaQueryWrapper<TeachplanMedia>().eq(TeachplanMedia::getTeachplanId, bindTeachplanMediaDto.getTeachplanId())) 赋值给 delete

        //再添加新记录 // 保留原作者写的行内注释
        TeachplanMedia teachplanMedia = new TeachplanMedia(); // 创建 TeachplanMedia 对象并赋值给 teachplanMedia
        BeanUtils.copyProperties(bindTeachplanMediaDto,teachplanMedia); // 执行语句
        teachplanMedia.setCourseId(teachplan.getCourseId()); // 执行语句
        teachplanMedia.setMediaFilename(bindTeachplanMediaDto.getFileName()); // 执行语句
        teachplanMediaMapper.insert(teachplanMedia); // 执行语句

    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/jobhandler/CoursePublishTask.java

package com.xuecheng.content.service.jobhandler; // 声明类所在的包路径

import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.content.feignclient.CourseIndex; // 导入 CourseIndex 类
import com.xuecheng.content.feignclient.SearchServiceClient; // 导入 SearchServiceClient 类
import com.xuecheng.content.mapper.CoursePublishMapper; // 导入 CoursePublishMapper 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import com.xuecheng.messagesdk.model.po.MqMessage; // 导入 MqMessage 类
import com.xuecheng.messagesdk.service.MessageProcessAbstract; // 导入 MessageProcessAbstract 类
import com.xuecheng.messagesdk.service.MqMessageService; // 导入 MqMessageService 类
import com.xxl.job.core.context.XxlJobHelper; // 导入 XxlJobHelper 类
import com.xxl.job.core.handler.annotation.XxlJob; // 导入 XxlJob 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Component; // 导入 Component 类

import java.io.File; // 导入 File 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布的任务类 // 保留原作者写的块注释
 * @date 2023/2/21 16:15 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Component // 使用注解 @Component
public class CoursePublishTask extends MessageProcessAbstract { // 定义 class CoursePublishTask

    @Autowired // 使用注解 @Autowired
    CoursePublishService coursePublishService; // 执行语句

    @Autowired // 使用注解 @Autowired
    SearchServiceClient searchServiceClient; // 执行语句

    @Autowired // 使用注解 @Autowired
    CoursePublishMapper coursePublishMapper; // 执行语句

    //任务调度入口 // 保留原作者写的行内注释
    @XxlJob("CoursePublishJobHandler") // 使用注解 @XxlJob("CoursePublishJobHandler")
    public void coursePublishJobHandler() throws Exception { // 定义方法 coursePublishJobHandler(...)

        // 分片参数 // 保留原作者写的行内注释
        int shardIndex = XxlJobHelper.getShardIndex();//执行器的序号,从0开始 // 把 XxlJobHelper.getShardIndex() 的返回值赋给 shardIndex
        int shardTotal = XxlJobHelper.getShardTotal();//执行器总数 // 把 XxlJobHelper.getShardTotal() 的返回值赋给 shardTotal
        //调用抽象类的方法执行任务 // 保留原作者写的行内注释
        process(shardIndex,shardTotal, "course_publish",30,60); // 执行语句


    } // 代码块结束

    //课程发布任务处理 // 保留原作者写的行内注释
    //执行课程发布任务的逻辑,如果此方法抛出异常说明任务执行失败 // 保留原作者写的行内注释
    @Override // 使用注解 @Override
    public boolean execute(MqMessage mqMessage) { // 定义方法 execute(...)
        //从mqMessage拿到课程id // 保留原作者写的行内注释
        Long courseId = Long.parseLong(mqMessage.getBusinessKey1()); // 把 Long.parseLong(mqMessage.getBusinessKey1()) 赋值给 courseId

        //课程静态化上传到minio // 保留原作者写的行内注释
        generateCourseHtml(mqMessage,courseId); // 执行语句


        //向elasticsearch写索引数据 // 保留原作者写的行内注释
        saveCourseIndex(mqMessage,courseId); // 执行语句

        //向redis写缓存 // 保留原作者写的行内注释

        //返回true表示任务完成 // 保留原作者写的行内注释
        return true; // 返回 true
    } // 代码块结束
    //生成课程静态化页面并上传至文件系统 // 保留原作者写的行内注释
    private void generateCourseHtml(MqMessage mqMessage,long courseId){ // 定义方法 generateCourseHtml(...)
        //消息id // 保留原作者写的行内注释
        Long taskId = mqMessage.getId(); // 把 mqMessage.getId() 的返回值赋给 taskId
        MqMessageService mqMessageService = this.getMqMessageService(); // 把 this.getMqMessageService() 的返回值赋给 mqMessageService

        //做任务幂等性处理 // 保留原作者写的行内注释
        //查询数据库取出该阶段执行状态 // 保留原作者写的行内注释
        int stageOne = mqMessageService.getStageOne(taskId); // 把 mqMessageService.getStageOne(taskId) 赋值给 stageOne
        if(stageOne>0){ // 定义方法 方法(...)
           log.debug("课程静态化任务完成,无需处理..."); // 执行语句
           return ; // 返回 
        } // 代码块结束


        //开始进行课程静态化 生成html页面 // 保留原作者写的行内注释
        File file = coursePublishService.generateCourseHtml(courseId); // 把 coursePublishService.generateCourseHtml(courseId) 赋值给 file
        if(file == null){ // 定义方法 方法(...)
            XueChengPlusException.cast("生成的静态页面为空"); // 执行语句
        } // 代码块结束
        // 将html上传到minio // 保留原作者写的行内注释
        coursePublishService.uploadCourseHtml(courseId,file); // 执行语句

        //..任务处理完成写任务状态为完成 // 保留原作者写的行内注释
        mqMessageService.completedStageOne(taskId); // 执行语句


    } // 代码块结束

    //保存课程索引信息 第二个阶段任务 // 保留原作者写的行内注释
    private void saveCourseIndex(MqMessage mqMessage,long courseId){ // 定义方法 saveCourseIndex(...)
        //任务id // 保留原作者写的行内注释
        Long taskId = mqMessage.getId(); // 把 mqMessage.getId() 的返回值赋给 taskId
        MqMessageService mqMessageService = this.getMqMessageService(); // 把 this.getMqMessageService() 的返回值赋给 mqMessageService
        //取出第二个阶段状态 // 保留原作者写的行内注释
        int stageTwo = mqMessageService.getStageTwo(taskId); // 把 mqMessageService.getStageTwo(taskId) 赋值给 stageTwo

        //任务幂等性处理 // 保留原作者写的行内注释
        if(stageTwo>0){ // 定义方法 方法(...)
            log.debug("课程索引信息已写入,无需执行..."); // 执行语句
            return; // 直接返回不带结果
        } // 代码块结束
        //查询课程信息,调用搜索服务添加索引接口 // 保留原作者写的行内注释
        //从课程发布表查询课程信息 // 保留原作者写的行内注释
        CoursePublish coursePublish = coursePublishMapper.selectById(courseId); // 把 coursePublishMapper.selectById(courseId) 赋值给 coursePublish

        CourseIndex courseIndex = new CourseIndex(); // 创建 CourseIndex 对象并赋值给 courseIndex
        BeanUtils.copyProperties(coursePublish,courseIndex); // 执行语句
        //远程调用 // 保留原作者写的行内注释
        Boolean add = searchServiceClient.add(courseIndex); // 把 searchServiceClient.add(courseIndex) 赋值给 add
        if(!add){ // 定义方法 方法(...)
            XueChengPlusException.cast("远程调用搜索服务添加课程索引失败"); // 执行语句
        } // 代码块结束

        //完成本阶段的任务 // 保留原作者写的行内注释
        mqMessageService.completedStageTwo(taskId); // 执行语句


    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/service/com/xuecheng/content/service/TeachplanService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.content.model.dto.BindTeachplanMediaDto; // 导入 BindTeachplanMediaDto 类
import com.xuecheng.content.model.dto.SaveTeachplanDto; // 导入 SaveTeachplanDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划管理相关接口 // 保留原作者写的块注释
 * @date 2023/2/14 12:10 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface TeachplanService { // 定义 interface TeachplanService
 /** // 保留原作者写的块注释
  * 根据课程id查询课程计划 // 保留原作者写的块注释
  * @param courseId 课程计划 // 保留原作者写的块注释
  * @return // 保留原作者写的块注释
  */ // 保留原作者写的块注释
  public List<TeachplanDto> findTeachplanTree(Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * 新增/修改/保存课程计划 // 保留原作者写的块注释
  * @param saveTeachplanDto // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void saveTeachplan(SaveTeachplanDto saveTeachplanDto); // 执行语句


 /** // 保留原作者写的块注释
  * @description 教学计划绑定媒资 // 保留原作者写的块注释
  * @param bindTeachplanMediaDto // 保留原作者写的块注释
  * @return com.xuecheng.content.model.po.TeachplanMedia // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/14 22:20 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void associationMedia(BindTeachplanMediaDto bindTeachplanMediaDto); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/service_pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>xuecheng-plus-content</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>xuecheng-plus-content-service</artifactId>
    <dependencies>
       <dependency>
           <groupId>com.alibaba.cloud</groupId>
           <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <!-- 读取配置中心配置 -->
       </dependency>
       <dependency>
           <groupId>com.xuecheng</groupId>
           <artifactId>xuecheng-plus-content-model</artifactId> <!-- 复用 DTO/PO -->
           <version>0.0.1-SNAPSHOT</version>
       </dependency>
       <dependency>
           <groupId>com.xuecheng</groupId>
           <artifactId>xuecheng-plus-message-sdk</artifactId> <!-- 发送消息到 MQ -->
           <version>0.0.1-SNAPSHOT</version>
       </dependency>
       <dependency>
           <groupId>com.xuxueli</groupId>
           <artifactId>xxl-job-core</artifactId> <!-- 定时任务框架 -->
       </dependency>
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <scope>runtime</scope> <!-- 运行时才需要数据库驱动 -->
       </dependency>
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId> <!-- MyBatis-Plus 自动配置 -->
       </dependency>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-context</artifactId> <!-- Spring Cloud 上下文支持 -->
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope> <!-- 单元测试依赖 -->
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
           <exclusions>
               <exclusion>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-logging</artifactId>
               </exclusion> <!-- 排除默认日志 -->
           </exclusions>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-redis</artifactId> <!-- Redis 客户端 -->
       </dependency>
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-pool2</artifactId>
           <version>2.6.2</version> <!-- Redis 连接池 -->
       </dependency>
       <dependency>
           <groupId>org.redisson</groupId>
           <artifactId>redisson-spring-boot-starter</artifactId>
           <version>3.11.2</version> <!-- 分布式锁 -->
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-log4j2</artifactId> <!-- 使用 log4j2 -->
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-freemarker</artifactId> <!-- 页面静态化 -->
       </dependency>
       <dependency>
           <groupId>com.alibaba.cloud</groupId>
           <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <!-- 注册中心 -->
       </dependency>
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-openfeign</artifactId> <!-- 远程调用 -->
       </dependency>
       <dependency>
           <groupId>io.github.openfeign</groupId>
           <artifactId>feign-httpclient</artifactId> <!-- 使用 Apache HttpClient 实现 -->
       </dependency>
       <dependency>
           <groupId>io.github.openfeign.form</groupId>
           <artifactId>feign-form</artifactId>
           <version>3.8.0</version> <!-- Feign 表单支持 -->
       </dependency>
       <dependency>
           <groupId>io.github.openfeign.form</groupId>
           <artifactId>feign-form-spring</artifactId>
           <version>3.8.0</version> <!-- Spring 集成的表单支持 -->
       </dependency>
   </dependencies>
</project>

文件:xuecheng-plus-content/test_service/com/xuecheng/content/CourseBaseInfoServiceTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.junit.jupiter.api.Assertions; // 导入 Assertions 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseBaseInfoServiceTests { // 定义 class CourseBaseInfoServiceTests

    @Autowired // 使用注解 @Autowired
    CourseBaseInfoService courseBaseInfoService; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseBaseInfoService() { // 定义方法 testCourseBaseInfoService(...)

        //查询条件 // 保留原作者写的行内注释
        QueryCourseParamsDto courseParamsDto = new QueryCourseParamsDto(); // 创建 QueryCourseParamsDto 对象并赋值给 courseParamsDto
        courseParamsDto.setCourseName("java");//课程名称查询条件 // 执行语句
        courseParamsDto.setAuditStatus("202004");//202004表示课程审核通过 // 执行语句
        //分页参数对象 // 保留原作者写的行内注释
        PageParams pageParams = new PageParams(); // 创建 PageParams 对象并赋值给 pageParams
        pageParams.setPageNo(2L); // 执行语句
        pageParams.setPageSize(2L); // 执行语句

        PageResult<CourseBase> courseBasePageResult = courseBaseInfoService.queryCourseBaseList(null,pageParams, courseParamsDto); // 把 courseBaseInfoService.queryCourseBaseList(null,pageParams, courseParamsDto) 赋值给 courseBasePageResult
        System.out.println(courseBasePageResult); // 执行语句

    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/com/xuecheng/content/CourseBaseMapperTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.junit.jupiter.api.Assertions; // 导入 Assertions 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseBaseMapperTests { // 定义 class CourseBaseMapperTests

    @Autowired // 使用注解 @Autowired
    CourseBaseMapper courseBaseMapper; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseBaseMapper() { // 定义方法 testCourseBaseMapper(...)
        CourseBase courseBase = courseBaseMapper.selectById(18); // 把 courseBaseMapper.selectById(18) 赋值给 courseBase
        Assertions.assertNotNull(courseBase); // 执行语句

        //详细进行分页查询的单元测试 // 保留原作者写的行内注释
        //查询条件 // 保留原作者写的行内注释
        QueryCourseParamsDto courseParamsDto = new QueryCourseParamsDto(); // 创建 QueryCourseParamsDto 对象并赋值给 courseParamsDto
        courseParamsDto.setCourseName("java");//课程名称查询条件 // 执行语句

        //拼装查询条件 // 保留原作者写的行内注释
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>(); // 创建 LambdaQueryWrapper<> 对象并赋值给 queryWrapper
        //根据名称模糊查询,在sql中拼接 course_base.name like '%值%' // 保留原作者写的行内注释
        queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName()),CourseBase::getName,courseParamsDto.getCourseName()); // 执行语句
        //根据课程审核状态查询 course_base.audit_status = ? // 保留原作者写的行内注释
        queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus()), CourseBase::getAuditStatus,courseParamsDto.getAuditStatus()); // 执行语句
        //todo:按课程发布状态查询 // 保留原作者写的行内注释
        //分页参数对象 // 保留原作者写的行内注释
        PageParams pageParams = new PageParams(); // 创建 PageParams 对象并赋值给 pageParams
        pageParams.setPageNo(1L); // 执行语句
        pageParams.setPageSize(2L); // 执行语句

        //创建page分页参数对象,参数:当前页码,每页记录数 // 保留原作者写的行内注释
        Page<CourseBase> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize()); // 创建 Page<> 对象并赋值给 page
        //开始进行分页查询 // 保留原作者写的行内注释
        Page<CourseBase> pageResult = courseBaseMapper.selectPage(page, queryWrapper); // 把 courseBaseMapper.selectPage(page, queryWrapper) 赋值给 pageResult
        //数据列表 // 保留原作者写的行内注释
        List<CourseBase> items = pageResult.getRecords(); // 把 pageResult.getRecords() 的返回值赋给 items
        //总记录数 // 保留原作者写的行内注释
        long total = pageResult.getTotal(); // 把 pageResult.getTotal() 的返回值赋给 total

        //List<T> items, long counts, long page, long pageSize // 保留原作者写的行内注释
        PageResult<CourseBase> courseBasePageResult = new PageResult<CourseBase>(items,total,pageParams.getPageNo(), pageParams.getPageSize()); // 创建 PageResult<CourseBase> 对象并赋值给 courseBasePageResult
        System.out.println(courseBasePageResult); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/com/xuecheng/content/CourseCategoryMapperTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.junit.jupiter.api.Assertions; // 导入 Assertions 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseCategoryMapperTests { // 定义 class CourseCategoryMapperTests

    @Autowired // 使用注解 @Autowired
    CourseCategoryMapper courseCategoryMapper; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseCategoryMapper() { // 定义方法 testCourseCategoryMapper(...)
        List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes("1"); // 把 courseCategoryMapper.selectTreeNodes("1") 赋值给 courseCategoryTreeDtos
        System.out.println(courseCategoryTreeDtos); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/com/xuecheng/content/CourseCategoryServiceTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import com.xuecheng.content.service.CourseCategoryService; // 导入 CourseCategoryService 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseCategoryServiceTests { // 定义 class CourseCategoryServiceTests

    @Autowired // 使用注解 @Autowired
    CourseCategoryService courseCategoryService; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseCategoryService() { // 定义方法 testCourseCategoryService(...)

        List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryService.queryTreeNodes("1"); // 把 courseCategoryService.queryTreeNodes("1") 赋值给 courseCategoryTreeDtos
        System.out.println(courseCategoryTreeDtos); // 执行语句

    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/com/xuecheng/content/FeignUploadTest.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.content.config.MultipartSupportConfig; // 导入 MultipartSupportConfig 类
import com.xuecheng.content.feignclient.MediaServiceClient; // 导入 MediaServiceClient 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.File; // 导入 File 类
import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 测试远程调用媒资服务 // 保留原作者写的块注释
 * @date 2023/2/22 10:30 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class FeignUploadTest { // 定义 class FeignUploadTest

    @Autowired // 使用注解 @Autowired
    MediaServiceClient mediaServiceClient; // 执行语句

    @Test // 使用注解 @Test
    public void test() throws IOException { // 定义方法 test(...)

        //将file转成MultipartFile // 保留原作者写的行内注释
        File file = new File("D:\\develop\\upload\\120.html"); // 创建 File 对象并赋值给 file
        MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(file); // 把 MultipartSupportConfig.getMultipartFile(file) 赋值给 multipartFile
        //远程调用得到返回值 // 保留原作者写的行内注释
        String upload = mediaServiceClient.upload(multipartFile, "course/120.html"); // 把 mediaServiceClient.upload(multipartFile, "course/120.html") 赋值给 upload
        if(upload==null){ // 定义方法 方法(...)
            System.out.println("走了降级逻辑"); // 执行语句
        } // 代码块结束
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/com/xuecheng/content/FreemarkerTest.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.content.mapper.TeachplanMapper; // 导入 TeachplanMapper 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import freemarker.template.Configuration; // 导入 Configuration 类
import freemarker.template.Template; // 导入 Template 类
import freemarker.template.TemplateException; // 导入 TemplateException 类
import org.apache.commons.io.IOUtils; // 导入 IOUtils 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; // 导入 FreeMarkerTemplateUtils 类

import java.io.File; // 导入 File 类
import java.io.FileOutputStream; // 导入 FileOutputStream 类
import java.io.IOException; // 导入 IOException 类
import java.io.InputStream; // 导入 InputStream 类
import java.util.HashMap; // 导入 HashMap 类
import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 测试freemarker页面静态化方法 // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class FreemarkerTest { // 定义 class FreemarkerTest

    @Autowired // 使用注解 @Autowired
    CoursePublishService coursePublishService; // 执行语句

    @Test // 使用注解 @Test
    public void testGenerateHtmlByTemplate() throws IOException, TemplateException { // 定义方法 testGenerateHtmlByTemplate(...)

        Configuration configuration = new Configuration(Configuration.getVersion()); // 创建 Configuration 对象并赋值给 configuration

        //拿到classpath路径 // 保留原作者写的行内注释
        String classpath = this.getClass().getResource("/").getPath(); // 把 this.getClass().getResource("/").getPath() 的返回值赋给 classpath
        //指定模板的目录 // 保留原作者写的行内注释
        configuration.setDirectoryForTemplateLoading(new File(classpath+"/templates/")); // 执行语句
        //指定编码 // 保留原作者写的行内注释
        configuration.setDefaultEncoding("utf-8"); // 执行语句

        //得到模板 // 保留原作者写的行内注释
        Template template = configuration.getTemplate("course_template.ftl"); // 把 configuration.getTemplate("course_template.ftl") 赋值给 template
        //准备数据 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(120L); // 把 coursePublishService.getCoursePreviewInfo(120L) 赋值给 coursePreviewInfo
        HashMap<String, Object> map = new HashMap<>(); // 创建 HashMap<> 对象并赋值给 map
        map.put("model",coursePreviewInfo); // 执行语句

        //Template template 模板, Object model 数据 // 保留原作者写的行内注释
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); // 把 FreeMarkerTemplateUtils.processTemplateIntoString(template, map) 赋值给 html
        //输入流 // 保留原作者写的行内注释
        InputStream inputStream = IOUtils.toInputStream(html, "utf-8"); // 把 IOUtils.toInputStream(html, "utf-8") 赋值给 inputStream
        //输出文件 // 保留原作者写的行内注释
        FileOutputStream outputStream = new FileOutputStream(new File("D:\\develop\\upload\\120.html")); // 创建 FileOutputStream 对象并赋值给 outputStream
        //使用流将html写入文件 // 保留原作者写的行内注释
        IOUtils.copy(inputStream,outputStream); // 执行语句


    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/com/xuecheng/content/TeachplanMapperTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.mapper.TeachplanMapper; // 导入 TeachplanMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划mapper测试 // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class TeachplanMapperTests { // 定义 class TeachplanMapperTests

    @Autowired // 使用注解 @Autowired
    TeachplanMapper teachplanMapper; // 执行语句

    @Test // 使用注解 @Test
    public void testSelectTreeNodes() { // 定义方法 testSelectTreeNodes(...)
        List<TeachplanDto> teachplanDtos = teachplanMapper.selectTreeNodes(117L); // 把 teachplanMapper.selectTreeNodes(117L) 赋值给 teachplanDtos
        System.out.println(teachplanDtos); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/com/xuecheng/ContentApplication.java

package com.xuecheng; // 声明类所在的包路径

import org.springframework.boot.SpringApplication; // 导入 SpringApplication 类
import org.springframework.boot.autoconfigure.SpringBootApplication; // 导入 SpringBootApplication 类
import org.springframework.cloud.openfeign.EnableFeignClients; // 导入 EnableFeignClients 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 内容管理服务启动类 // 保留原作者写的块注释
 * @date 2023/2/11 15:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"}) // 使用注解 @EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"})
@SpringBootApplication // 使用注解 @SpringBootApplication
public class ContentApplication { // 定义 class ContentApplication
    public static void main(String[] args) { // 定义方法 main(...)
        SpringApplication.run(ContentApplication.class, args); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/test_service/resources/bootstrap.yml

spring: # Spring Boot 顶层配置
  application:
    name: content-service # 测试环境注册的服务名
  cloud:
    nacos:
      server-addr: 192.168.101.65:8848 # Nacos 地址
      discovery: # 注册中心设置
        namespace: dev402 # 使用 dev402 命名空间
        group: xuecheng-plus-project # 服务分组
      config: # 配置中心设置
        namespace: dev402
        group: xuecheng-plus-project
        file-extension: yaml # 使用 yaml 配置格式
        refresh-enabled: true # 支持动态刷新
        shared-configs: # 引入公共配置
          - data-id: feign-${spring.profiles.active}.yaml # Feign 统一配置
            group: xuecheng-plus-common
            refresh: true
  profiles:
    active: dev # 激活 dev 环境
logging:
  config: classpath:log4j2-dev.xml # 指定测试环境的 log4j2 配置

文件:xuecheng-plus-content/test_service/resources/log4j2-dev.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- 测试环境 log4j2 配置入口 -->
<Configuration monitorInterval="180" packages=""> <!-- 每 180 秒检测配置变更 -->
    <properties>
        <property name="logdir">logs</property> <!-- 日志目录 -->
        <property name="PATTERN">%date{YYYY-MM-dd HH:mm:ss,SSS} %level [%thread][%file:%line] - %msg%n%throwable</property> <!-- 日志格式 -->
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT"> <!-- 控制台输出 -->
            <PatternLayout pattern="${PATTERN}"/>
        </Console>

        <RollingFile name="ErrorAppender" fileName="${logdir}/error.log" <!-- 错误日志 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/error.%d{yyyy-MM-dd-HH}.log" append="true">
            <PatternLayout pattern="${PATTERN}"/>
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <!-- 仅记录 ERROR -->
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- 每小时滚动 -->
            </Policies>
        </RollingFile>

        <RollingFile name="DebugAppender" fileName="${logdir}/info.log" <!-- 常规日志 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/info.%d{yyyy-MM-dd-HH}.log" append="true">
            <PatternLayout pattern="${PATTERN}"/>
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
            </Policies>
        </RollingFile>
        
         <Async name="AsyncAppender" includeLocation="true"> <!-- 异步包装 -->
            <AppenderRef ref="ErrorAppender"/>
            <AppenderRef ref="DebugAppender"/>
        </Async>
    </Appenders>
    
    <Loggers>
        <logger name="cn.itcast.wanxinp2p.consumer.mapper" level="DEBUG"/> <!-- 示例保留 -->
        <logger name="springfox" level="INFO"/>
        <logger name="org.apache.http" level="INFO"/>
        <logger name="com.netflix.discovery" level="INFO"/>
        <logger name="RocketmqCommon"  level="INFO" />
        <logger name="RocketmqRemoting" level="INFO"  />
        <logger name="RocketmqClient" level="WARN"/>
        <logger name="org.dromara.hmily" level="WARN"/>
        <logger name="org.dromara.hmily.lottery" level="WARN"/>
        <logger name="org.dromara.hmily.bonuspoint" level="WARN"/>
        <Root level="DEBUG" includeLocation="true"> <!-- 根记录器 -->
            <AppenderRef ref="AsyncAppender"/>
            <AppenderRef ref="Console"/>
            <AppenderRef ref="DebugAppender"/>
        </Root>
    </Loggers>
</Configuration>

文件:xuecheng-plus-content/test_service/resources/templates/course_template.ftl

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="/static/img/asset-favicon.ico">
    <title>学成在线-${model.courseBase.name}</title>

    <link rel="stylesheet" href="/static/plugins/normalize-css/normalize.css" />
    <link rel="stylesheet" href="/static/plugins/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="/static/css/page-learing-article.css" />
</head>

<body data-spy="scroll" data-target="#articleNavbar" data-offset="150">
<!-- 页面头部 -->
<!--#include virtual="/include/header.html"-->
<!--页面头部结束sss-->
<div id="learningArea">
<div class="article-banner">
    <div class="banner-bg"></div>
    <div class="banner-info">
        <div class="banner-left">
            <p>${model.courseBase.mtName!''}<span>\ ${model.courseBase.stName!''}</span></p>
            <p class="tit">${model.courseBase.name}</p>
            <p class="pic">
                <#if model.courseBase.charge=='201000'>
                    <span class="new-pic">免费</span>
                <#else>
                    <span class="new-pic">特惠价格¥${model.courseBase.price!''}</span>
                    <span class="old-pic">原价¥${model.courseBase.originalPrice!''}</span>
                </#if>
            </p>
            <p class="info">
                <a href="#" @click.prevent="startLearning()">马上学习</a>
                <span><em>难度等级</em>
                <#if model.courseBase.grade=='204001'>
                    初级
                 <#elseif model.courseBase.grade=='204002'>
                    中级
                <#elseif model.courseBase.grade=='204003'>
                    高级
                </#if>
                </span>
                <span><em>课程时长</em>2小时27分</span>
                <span><em>评分</em>4.7分</span>
                <span><em>授课模式</em>
                 <#if model.courseBase.teachmode=='200002'>
                     录播
                 <#elseif model.courseBase.teachmode=='200003'>
                     直播
                 </#if>
                </span>
            </p>
        </div>
        <div class="banner-rit">
            <p>
                <a href="http://www.51xuecheng.cn/course/preview/learning.html?id=${model.courseBase.id}" target="_blank">
                    <#if model.courseBase.pic??>
                        <img src="http://file.51xuecheng.cn${model.courseBase.pic}" alt="" width="270" height="156">
                    <#else>
                        <img src="/static/img/widget-video.png" alt="" width="270" height="156">
                    </#if>

                </a>
            </p>
            <p class="vid-act"><span> <i class="i-heart"></i>收藏 23 </span> <span>分享 <i class="i-weixin"></i><i class="i-qq"></i></span></p>
        </div>
    </div>
</div>
<div class="article-cont">
    <div class="tit-list">
        <a href="javascript:;" id="articleClass" class="active">课程介绍</a>
        <a href="javascript:;" id="articleItem">目录</a>
        <a href="javascript:;" id="artcleAsk">问答</a>
        <a href="javascript:;" id="artcleNot">笔记</a>
        <a href="javascript:;" id="artcleCod">评价</a>
        <!--<div class="down-fill">
            <span>资料下载</span>
            <ul>
                <li>java视频资料</li>
                <li>java视频资料</li>
                <li>java视频资料</li>
            </ul>
        </div>-->
    </div>
    <div class="article-box">
        <div class="articleClass" style="display: block">
            <!--<div class="rit-title">评价</div>-->
            <div class="article-cont">
                <div class="article-left-box">
                    <div class="content">

                        <div class="content-com suit">
                            <div class="title"><span>适用人群</span></div>
                            <div class="cont cktop">
                                <div >
                                    <p>${model.courseBase.users!""}</p>
                                </div>
                                <!--<span class="on-off">更多 <i class="i-chevron-bot"></i></span>-->
                            </div>
                        </div>
                        <div class="content-com course">
                            <div class="title"><span>课程制作</span></div>
                            <div class="cont">
                                <div class="img-box"><img src="/static/img/widget-myImg.jpg" alt=""></div>
                                <div class="info-box">
                                    <p class="name">教学方:<em>XX老师</em></p>
                                    <!-- <p class="lab">高级前端开发工程师 10年开发经验</p>-->
                                    <p class="info">JavaEE开发与教学多年,精通JavaEE技术体系,对流行框架JQuery、DWR、Struts1/2,Hibernate,Spring,MyBatis、JBPM、Lucene等有深入研究。授课逻辑严谨,条理清晰,注重学生独立解决问题的能力。</p>
                                    <!-- <p><span>难度等级</span>中级</p>
                                     <p><span>课程时长</span>8-16小时/周,共4周</p>
                                     <p><span>如何通过</span>通过所有的作业及考核,作业共4份,考核为一次终极考核</p>
                                     <p><span>用户评分</span>平均用户评分 <em>4.9</em> <a href="#">查看全部评价</a></p>
                                     <p><span>课程价格</span>特惠价格<em>¥999</em> <i> 原价1999 </i></p>-->
                                </div>
                            </div>

                        </div>
                        <div class="content-com about">
                            <div class="title"><span>课程介绍</span></div>
                            <div class="cont cktop">
                                <div >
                                    <p>${model.courseBase.description!""}</p>
                                </div>
                                <!--<span class="on-off">更多 <i class="i-chevron-bot"></i></span>-->
                            </div>
                        </div>
                        <div class="content-com prob">
                            <div class="title"><span>常见问题</span></div>
                            <div class="cont">
                                <ul>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 我什么时候能够访问课程视频与作业?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 如何需要额外的时间来完成课程会怎么样?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 我支付次课程之后会得到什么?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 退款条例是如何规定的?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                    <li class="item"><span class="on-off"><i class="i-chevron-bot"></i> 有助学金?</span>
                                        <div class="drop-down">
                                            <p>课程安排灵活,课程费用支付提供180天全程准入和资格证书。自定进度课程建议的最后期限,但你不会受到惩罚错过期限,只要你赚你的证书在180天内。以会话为基础的课程可能要求你在截止日期前保持正轨,但如果你落后了,你可以切换到以后的会议,你完成的任何工作将与你转移。</p>
                                        </div>
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>

                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->

            </div>
        </div>
        <div class="articleItem" style="display: none">
            <div class="article-cont-catalog">
                <div class="article-left-box">
                    <div class="content">
                        <#list model.teachplans as firstNode>
                            <div class="item">
                                <div class="title act"><i class="i-chevron-top"></i>${firstNode.pname}<span class="time">x小时</span></div>
                                <div class="drop-down" style="height: 260px;">
                                    <ul class="list-box">
                                        <#list firstNode.teachPlanTreeNodes as secondNode>
                                            <li><a href="http://www.51xuecheng.cn/course/preview/learning.html?id=${model.courseBase.id}&chapter=${secondNode.teachplanMedia.teachplanId!''}" target="_blank">${secondNode.pname}</a></li>
                                        </#list>
                                    </ul>
                                </div>
                            </div>
                        </#list>
                       <#-- <div class="item">
                            <div class="title act"><i class="i-chevron-top"></i>第一阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down" style="height: 260px;">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>-->


                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
        <#--<div class="articleItem" style="display: none">
            <div class="article-cont-catalog">
                <div class="article-left-box">
                    <div class="content">
                        <div class="item">
                            <div class="title act"><i class="i-chevron-top"></i>第一阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down" style="height: 260px;">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第二阶段 HTTP协议基础详解<span class="time">8小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第三阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第四阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>
                        </div>
                        <div class="item">
                            <div class="title"><i class="i-chevron-bot"></i>第五阶段 HTTP协议基础详解<span class="time">3小时</span></div>
                            <div class="about">使用Java消息中间件处理异步消息成为了分布式系统中的必修课,通过本门课程可以深入浅出的学习如何在Java中使用消息中间件并且一步一步打造更优雅的最佳实践方案。</div>
                            <div class="drop-down">
                                <ul class="list-box">
                                    <li class="active">1.1 阅读:分级政策细节 <span>97’33”</span></li>
                                    <li>1.2 视频:为什么分为 A 部分、B 部分、C 部分 <span>66’15”</span></li>
                                    <li>1.3 视频:软件安装介绍 <span>86’42”</span></li>
                                    <li>1.4 阅读:Emacs安装 <span>59’00”</span></li>
                                    <li>1.5 作业1:Emacs安装 <span>93’29”</span></li>
                                    <li>阶段测试</li>
                                </ul>
                            </div>

                        </div>
                        <div class="item">
                            <a href="#" class="overwrite">毕业考核</a>
                        </div>
                    </div>
                </div>
                <!--侧边栏&ndash;&gt;
                <!--#include virtual="/include/course_detail_side.html"&ndash;&gt;
                <!--侧边栏&ndash;&gt;
            </div>
        </div>-->
        <div class="artcleAsk" style="display: none">
            <div class="article-cont-ask">
                <div class="article-left-box">
                    <div class="content">
                        <div class="content-title">
                            <p><a class="all">全部</a><a>精选</a><a>我的</a></p>
                            <p><a class="all">全部</a><span><a>1.1</a><a>1.2</a><a>1.3</a><a>1.4</a><a>1.5</a></span><a href="$" class="more">更多 <i class="i-chevron-bot"></i></a></p>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p><span>我来回答</span></p>
                                <p>2017-3-20 <span><i></i>回答2</span><span><i></i>浏览2</span></p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p class="title">如何用微服务重构应用程序?</p>
                                <p>在讨论如何将重构转化为微服务之前,退后一步,仔细观察微服务的内容和时间是很重要的。以下两个要点将会对任何微服务重构策略产生重大影响。 【最新 <i class="new">心跳347890</i> 的回答】</p>
                                <p>2017-3-20 <span class="action-box"><span><i class="i-answer"></i>回答 2</span><span><i class="i-browse"></i>浏览 12</span></span>
                                </p>
                            </div>
                        </div>

                        <div class="itemlast">
                            <a href="#" class="overwrite">显示更多问题</a>
                        </div>
                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
        <div class="artcleNot" style="display: none;">
            <div class="article-cont-note">
                <div class="article-left-box">
                    <div class="content">
                        <div class="content-title">
                            <p><a class="all">全部</a><a>精选</a><a>我的</a></p>
                            <p><a class="all">全部</a><span><a>1.1</a><a>1.2</a><a>1.3</a><a>1.4</a><a>1.5</a></span><a href="$" class="more">更多 <i class="i-chevron-bot"></i></a></p>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <span class="video-time"><i class="i-play"></i>2`10`</span>
                                <p><img src="/static/img/widget-demo.png" width="221" alt=""></p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-coll"></i>采集</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-edt"></i>编辑</span><span><i class="i-del"></i>删除</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-edt"></i>编辑</span><span><i class="i-del"></i>删除</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                        <div class="item">
                            <div class="item-left">
                                <p><img src="/static/img/widget-myImg.jpg" width="60px" alt=""></p>
                                <p>毛老师</p>
                            </div>
                            <div class="item-right">
                                <p>在讨论如何将重构转化为微服务之前,退后一步,<br>仔细观察微服务的内容和时间是很重要的。<br>以下两个要点将会对任何微服务重构策略产生重大影响。 </p>
                                <p class="action-box">4小时前 <span class="active-box"><span><i class="i-edt"></i>编辑</span><span><i class="i-del"></i>删除</span><span><i class="i-laud"></i>赞</span></span>
                                </p>
                            </div>
                        </div>
                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
        <div class="artcleCod" style="display: none;">
            <div class="article-cont">
                <div class="article-left-box">
                    <div class="comment-box">
                        <div class="evaluate">
                            <div class="eva-top">
                                <div class="tit">课程评分 </div>
                                <div class="star">
                                    <div class="score"><i>5</i></div>
                                </div><span class="star-score"> <i>5</i> 分</span></div>
                            <div class="eva-cont">
                                <div class="tit">学员评语 </div>
                                <div class="text-box">
                                    <textarea class="form-control" rows="5" placeholder="扯淡、吐槽、表扬、鼓励......想说啥说啥!"></textarea>
                                    <div class="text-right"><span>发表评论</span></div>
                                </div>
                            </div>
                        </div>
                        <div class="course-evaluate">
                            <div class="top-tit">评论
                                <span>
                        <label><input name="eval" type="radio" value="" checked /> 所有学生 </label>
                        <label><input name="eval" type="radio" value="" /> 完成者 </label>
                    </span>
                            </div>
                            <div class="top-cont">
                                <div class="cont-top-left">
                                    <div class="star-scor">
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        <div class="scor">4.9分</div>
                                    </div>
                                    <div class="all-scor">总评分:12343</div>
                                </div>
                                <div class="cont-top-right">
                                    <div class="star-grade">五星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>95</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">四星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>5</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">三星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>0</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">二星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>2</i>%</div>
                                        </div>
                                    </div>
                                    <div class="star-grade">一星
                                        <div class="grade">
                                            <div class="grade-percent"><span></span></div>
                                            <div class="percent-num"><i>1</i>%</div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="comment-item-box">
                                <div class="title">评论 <span>12453条评论</span></div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>4</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="item">
                                    <div class="item-left">
                                        <p><img src="/static/img/widget-pic.png" width="60px" alt=""></p>
                                        <p>毛老师</p>
                                    </div>
                                    <div class="item-cent">
                                        <p>很受用,如果再深入下就更好了。虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!虽然都是入门级别的,但是也很使用,后续就需要自己发挥了!</p>
                                        <p class="time">2017-2-43</p>
                                    </div>
                                    <div class="item-rit">
                                        <p>
                                        <div class="star-show">
                                            <div class="score"><i>5</i></div>
                                        </div>
                                        </p>
                                        <p>评分 <span>5星</span></p>
                                    </div>
                                </div>
                                <div class="get-more">页面加载中...</div>
                            </div>
                        </div>
                    </div>
                </div>
                <!--侧边栏-->
                <!--#include virtual="/include/course_detail_side.html"-->
                <!--侧边栏-->
            </div>
        </div>
    </div>
</div>
    <div class="popup-course">
        <div class="mask"></div>
        <!--欢迎访问课程弹窗- start -->
        <div class="popup-course-box">
            <div class="title">${model.courseBase.name} <span class="close-popup-course-box">×</span></div>
            <div class="content">
                <p>欢迎学习本课程,本课程免费您可以立即学习,也可加入我的课程表享受更优质的服务。</p>
                <p><a href="#" @click.prevent="addCourseTable()">加入我的课程表</a>  <a href="#" @click.prevent="startLearngin()">立即学习</a></p>
            </div>
        </div>
    </div>
    <div class="popup-box">
        <div class="mask"></div>
        <!--支付弹窗- start -->
        <div class="popup-pay-box">
            <div class="title">${model.courseBase.name} <span class="close-popup-pay-box">×</span></div>
            <div class="content">
                <img :src="qrcode" width="200" height="200" alt="请点击支付宝支付按钮,并完成扫码支付。"/>

                <div class="info">
                    <p class="info-tit">${model.courseBase.name}<span>课程有效期:${model.courseBase.validDays}天</span></p>
                    <p class="info-pic">课程价格 : <span>¥${model.courseBase.originalPrice!''}元</span></p>
                    <p class="info-new-pic">优惠价格 : <span>¥${model.courseBase.price!''}元</span></p>
                </div>
            </div>
            <div class="fact-pic">实际支付: <span>¥${model.courseBase.price!''}元</span></div>
            <div class="go-pay"><a href="#" @click.prevent="wxPay()">微信支付</a><a href="#" @click.prevent="aliPay()">支付宝支付</a><a href="#" @click.prevent="startLearngin()">试学</a></div>
        </div>
        <!--支付弹窗- end -->
        <div class="popup-comment-box">

        </div>
    </div>
    <!-- 页面底部 -->
    <!--底部版权-->
    <!--#include virtual="/include/footer.html"-->
    <!--底部版权-->
</div>
<script>var courseId = "${model.courseBase.id}";var courseCharge = "${model.courseBase.charge}"</script>
<!--#include virtual="/include/course_detail_dynamic.html"-->
</body>

文件:xuecheng-plus-content/test_service/resources/templates/test.ftl

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
Hello ${name}!
</body>
</html>

文件:xuecheng-plus-content/xuecheng-plus-content-api/pom.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- SQL 语句片段 -->
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- SQL 语句片段 -->
    <modelVersion>4.0.0</modelVersion> <!-- SQL 语句片段 -->
    <parent> <!-- SQL 语句片段 -->
        <artifactId>xuecheng-plus-content</artifactId> <!-- SQL 语句片段 -->
        <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
        <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
    </parent> <!-- SQL 语句片段 -->
    <artifactId>xuecheng-plus-content-api</artifactId> <!-- SQL 语句片段 -->
    <dependencies> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
            <artifactId>xuecheng-plus-content-service</artifactId> <!-- SQL 语句片段 -->
            <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <!--认证相关-->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-cloud-starter-security</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-cloud-starter-oauth2</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>com.alibaba.cloud</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>com.alibaba.cloud</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <!--cloud的基础环境包-->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-cloud-context</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <!-- Spring Boot 的 Spring Web MVC 集成 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-boot-starter-web</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->


        <!-- 排除 Spring Boot 依赖的日志包冲突 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-boot-starter</artifactId> <!-- SQL 语句片段 -->
            <exclusions> <!-- SQL 语句片段 -->
                <exclusion> <!-- SQL 语句片段 -->
                    <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
                    <artifactId>spring-boot-starter-logging</artifactId> <!-- SQL 语句片段 -->
                </exclusion> <!-- SQL 语句片段 -->
            </exclusions> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-boot-starter-validation</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <!-- Spring Boot 集成 log4j2 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-boot-starter-log4j2</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->

        <!-- Spring Boot 集成 swagger -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>com.spring4all</groupId> <!-- SQL 语句片段 -->
            <artifactId>swagger-spring-boot-starter</artifactId> <!-- SQL 语句片段 -->
            <version>1.9.0.RELEASE</version> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->

        <!-- Spring Boot 对结果视图 Freemarker 集成 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
            <artifactId>spring-boot-starter-freemarker</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
    </dependencies> <!-- SQL 语句片段 -->

    <build> <!-- SQL 语句片段 -->
        <finalName>${project.artifactId}-${project.version}</finalName> <!-- SQL 语句片段 -->
        <plugins> <!-- SQL 语句片段 -->
            <plugin> <!-- SQL 语句片段 -->
                <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
                <artifactId>spring-boot-maven-plugin</artifactId> <!-- SQL 语句片段 -->
                <version>${spring-boot.version}</version> <!-- SQL 语句片段 -->
                <executions> <!-- SQL 语句片段 -->
                    <execution> <!-- SQL 语句片段 -->
                        <goals> <!-- SQL 语句片段 -->
                            <goal>repackage</goal> <!-- SQL 语句片段 -->
                        </goals> <!-- SQL 语句片段 -->
                    </execution> <!-- SQL 语句片段 -->
                </executions> <!-- SQL 语句片段 -->
            </plugin> <!-- SQL 语句片段 -->
            <plugin> <!-- SQL 语句片段 -->
                <groupId>com.spotify</groupId> <!-- SQL 语句片段 -->
                <artifactId>docker-maven-plugin</artifactId> <!-- SQL 语句片段 -->
                <version>1.2.2</version> <!-- SQL 语句片段 -->
                <configuration> <!-- SQL 语句片段 -->
                    <!--修改imageName节点的内容,改为私有仓库地址和端口,再加上镜像id和 TAG,我们要直接传到私服-->
                    <!--配置最后生成的镜像名,docker images里的,我们这边取项目名:版本-->
                    <!--<imageName>${project.artifactId}:${project.version}</imageName>-->
                    <imageName>192.168.101.65:5000/${project.artifactId}:${project.version}</imageName> <!-- SQL 语句片段 -->
                    <!--也可以通过以下方式定义image的tag信息。 -->
                    <!-- <imageTags>
                         <imageTag>${project.version}</imageTag> <!-- SQL 语句片段 -->
                         &lt;!&ndash;build 时强制覆盖 tag,配合 imageTags 使用&ndash;&gt; <!-- SQL 语句片段 -->
                         <forceTags>true</forceTags> <!-- SQL 语句片段 -->
                         &lt;!&ndash;build 完成后,push 指定 tag 的镜像,配合 imageTags 使用&ndash;&gt; <!-- SQL 语句片段 -->
                         <pushImageTag>true</pushImageTag> <!-- SQL 语句片段 -->
                     </imageTags>--> <!-- SQL 语句片段 -->
                    <baseImage>java:8u20</baseImage> <!-- SQL 语句片段 -->
                    <maintainer>docker_maven [email protected]</maintainer> <!-- SQL 语句片段 -->
                    <workdir>/root</workdir> <!-- SQL 语句片段 -->
                    <cmd>["java", "-version"]</cmd> <!-- SQL 语句片段 -->
                    <!--来指明Dockerfile文件的所在目录,如果配置了dockerDirectory则忽略baseImage,maintainer等配置-->
                    <!--<dockerDirectory>./</dockerDirectory>-->
                    <!--2375是docker的远程端口,插件生成镜像时连接docker,这里需要指定docker远程端口-->
                    <dockerHost>http://192.168.101.65:2375</dockerHost> <!-- SQL 语句片段 -->
                    <!--入口点,project.build.finalName就是project标签下的build标签下 的filename标签内容,testDocker-->
                    <!--相当于启动容器后,会自动执行java -jar ...-->
                    <entryPoint>["java", "-Dfile.encoding=utf-8","-jar", "/root/${project.build.finalName}.jar"]</entryPoint> <!-- SQL 语句片段 -->
                    <!--是否推送到docker私有仓库,旧版本插件要配置maven的settings文件。 -->
                    <pushImage>true</pushImage> <!-- SQL 语句片段 -->
                    <registryUrl>192.168.101.65:5000</registryUrl>  <!-- 这里是复制 jar 包到 docker 容器指定目录配置 --> <!-- SQL 语句片段 -->
                    <resources> <!-- SQL 语句片段 -->
                        <resource> <!-- SQL 语句片段 -->
                            <targetPath>/root</targetPath> <!-- SQL 语句片段 -->
                            <directory>${project.build.directory}</directory> <!-- SQL 语句片段 -->
                            <!--把哪个文件上传到docker,相当于Dockerfile里的add app.jar /-->
                            <include>${project.build.finalName}.jar</include> <!-- 引用公共 SQL 片段 -->
                        </resource> <!-- SQL 语句片段 -->
                    </resources> <!-- SQL 语句片段 -->
                </configuration> <!-- SQL 语句片段 -->
            </plugin> <!-- SQL 语句片段 -->
        </plugins> <!-- SQL 语句片段 -->
    </build> <!-- SQL 语句片段 -->
</project> <!-- SQL 语句片段 -->

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api/CourseBaseInfoController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.base.exception.ValidationGroups; // 导入 ValidationGroups 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.model.dto.AddCourseDto; // 导入 AddCourseDto 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.EditCourseDto; // 导入 EditCourseDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import io.swagger.annotations.Api; // 导入 Api 类
import io.swagger.annotations.ApiOperation; // 导入 ApiOperation 类
import org.apache.commons.lang.StringUtils; // 导入 StringUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.security.access.prepost.PreAuthorize; // 导入 PreAuthorize 类
import org.springframework.security.core.context.SecurityContextHolder; // 导入 SecurityContextHolder 类
import org.springframework.validation.annotation.Validated; // 导入 Validated 类
import org.springframework.web.bind.annotation.*; // 导入 * 类

import java.time.LocalDateTime; // 导入 LocalDateTime 类
import java.util.ArrayList; // 导入 ArrayList 类
import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/11 15:44 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Api(value = "课程信息管理接口",tags = "课程信息管理接口") // 使用注解 @Api(value = "课程信息管理接口",tags = "课程信息管理接口")
@RestController // 使用注解 @RestController
public class CourseBaseInfoController { // 定义 class CourseBaseInfoController

    @Autowired // 使用注解 @Autowired
    CourseBaseInfoService courseBaseInfoService; // 执行语句

    @ApiOperation("课程分页查询接口") // 使用注解 @ApiOperation("课程分页查询接口")
    @PreAuthorize("hasAuthority('xc_teachmanager_course_list')")//指定权限标识符,拥有此权限才可以访问此方法 // 使用注解 @PreAuthorize("hasAuthority('xc_teachmanager_course_list')")
    @PostMapping("/course/list") // 使用注解 @PostMapping("/course/list")
    public PageResult<CourseBase> list(PageParams pageParams, @RequestBody(required=false) QueryCourseParamsDto queryCourseParamsDto) { // 定义方法 list(...)

        //当前登录用户 // 保留原作者写的行内注释
        SecurityUtil.XcUser user = SecurityUtil.getUser(); // 把 SecurityUtil.getUser() 的返回值赋给 user
        //用户所属机构id // 保留原作者写的行内注释
        Long companyId = null; // 把变量 companyId 初始化为 null
        if(StringUtils.isNotEmpty(user.getCompanyId())){ // 定义方法 方法(...)
            companyId = Long.parseLong(user.getCompanyId()); // 把 Long.parseLong(user.getCompanyId()) 赋值给 companyId
        } // 代码块结束
        PageResult<CourseBase> courseBasePageResult = courseBaseInfoService.queryCourseBaseList(companyId,pageParams, queryCourseParamsDto); // 把 courseBaseInfoService.queryCourseBaseList(companyId,pageParams, queryCourseParamsDto) 赋值给 courseBasePageResult
        return courseBasePageResult; // 返回 courseBasePageResult

    } // 代码块结束
    @ApiOperation("新增课程") // 使用注解 @ApiOperation("新增课程")
    @PostMapping("/course") // 使用注解 @PostMapping("/course")
    public CourseBaseInfoDto createCourseBase(@RequestBody @Validated(ValidationGroups.Inster.class) AddCourseDto addCourseDto){ // 定义方法 createCourseBase(...)

        //获取到用户所属机构的id // 保留原作者写的行内注释
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
//        int i = 1/0; // 保留原作者写的行内注释
        CourseBaseInfoDto courseBase = courseBaseInfoService.createCourseBase(companyId, addCourseDto); // 把 courseBaseInfoService.createCourseBase(companyId, addCourseDto) 赋值给 courseBase
        return courseBase; // 返回 courseBase
    } // 代码块结束

    @ApiOperation("根据课程id查询接口") // 使用注解 @ApiOperation("根据课程id查询接口")
    @GetMapping("/course/{courseId}") // 使用注解 @GetMapping("/course/{courseId}")
    public CourseBaseInfoDto getCourseBaseById(@PathVariable Long courseId){ // 定义方法 getCourseBaseById(...)
        //获取当前用户的身份 // 保留原作者写的行内注释
//        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); // 保留原作者写的行内注释
//        System.out.println(principal); // 保留原作者写的行内注释
        SecurityUtil.XcUser user = SecurityUtil.getUser(); // 把 SecurityUtil.getUser() 的返回值赋给 user
        System.out.println(user.getUsername()); // 执行语句
        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId); // 把 courseBaseInfoService.getCourseBaseInfo(courseId) 赋值给 courseBaseInfo
        return courseBaseInfo; // 返回 courseBaseInfo
    } // 代码块结束


    @ApiOperation("修改课程") // 使用注解 @ApiOperation("修改课程")
    @PutMapping("/course") // 使用注解 @PutMapping("/course")
    public CourseBaseInfoDto modifyCourseBase(@RequestBody @Validated(ValidationGroups.Update.class) EditCourseDto editCourseDto){ // 定义方法 modifyCourseBase(...)
        //获取到用户所属机构的id // 保留原作者写的行内注释
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
        CourseBaseInfoDto courseBaseInfoDto = courseBaseInfoService.updateCourseBase(companyId, editCourseDto); // 把 courseBaseInfoService.updateCourseBase(companyId, editCourseDto) 赋值给 courseBaseInfoDto
        return courseBaseInfoDto; // 返回 courseBaseInfoDto
    } // 代码块结束



} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api/CourseCategoryController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.service.CourseCategoryService; // 导入 CourseCategoryService 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.bind.annotation.RestController; // 导入 RestController 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程分类相关接口 // 保留原作者写的块注释
 * @date 2023/2/12 11:54 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@RestController // 使用注解 @RestController
public class CourseCategoryController { // 定义 class CourseCategoryController

    @Autowired // 使用注解 @Autowired
    CourseCategoryService courseCategoryService; // 执行语句


    @GetMapping("/course-category/tree-nodes") // 使用注解 @GetMapping("/course-category/tree-nodes")
    public List<CourseCategoryTreeDto> queryTreeNodes() { // 定义方法 queryTreeNodes(...)
        return courseCategoryService.queryTreeNodes("1"); // 返回 courseCategoryService.queryTreeNodes("1")
    } // 代码块结束

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api/CourseOpenController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.bind.annotation.PathVariable; // 导入 PathVariable 类
import org.springframework.web.bind.annotation.RequestMapping; // 导入 RequestMapping 类
import org.springframework.web.bind.annotation.RestController; // 导入 RestController 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/21 10:33 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
// 获取课程信息接口 // 保留原作者写的行内注释
@RestController // 使用注解 @RestController
@RequestMapping("/open") // 使用注解 @RequestMapping("/open")
public class CourseOpenController { // 定义 class CourseOpenController

    @Autowired // 使用注解 @Autowired
    private CourseBaseInfoService courseBaseInfoService; // 执行语句

    @Autowired // 使用注解 @Autowired
    private CoursePublishService coursePublishService; // 执行语句

    //根据课程id查询课程信息 // 保留原作者写的行内注释
    @GetMapping("/course/whole/{courseId}") // 使用注解 @GetMapping("/course/whole/{courseId}")
    public CoursePreviewDto getPreviewInfo(@PathVariable("courseId") Long courseId) { // 定义方法 getPreviewInfo(...)
        //获取课程预览信息 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId); // 把 coursePublishService.getCoursePreviewInfo(courseId) 赋值给 coursePreviewInfo
        return coursePreviewInfo; // 返回 coursePreviewInfo
    } // 代码块结束



} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api/CoursePublishController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.alibaba.fastjson.JSON; // 导入 JSON 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import io.swagger.annotations.ApiOperation; // 导入 ApiOperation 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Controller; // 导入 Controller 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.bind.annotation.PathVariable; // 导入 PathVariable 类
import org.springframework.web.bind.annotation.PostMapping; // 导入 PostMapping 类
import org.springframework.web.bind.annotation.ResponseBody; // 导入 ResponseBody 类
import org.springframework.web.servlet.ModelAndView; // 导入 ModelAndView 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布相关接口 // 保留原作者写的块注释
 * @date 2023/2/21 9:28 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Controller // 使用注解 @Controller
public class CoursePublishController { // 定义 class CoursePublishController

    @Autowired // 使用注解 @Autowired
    CoursePublishService coursePublishService; // 执行语句


    @ApiOperation("获取课程发布信息") // 使用注解 @ApiOperation("获取课程发布信息")
    @ResponseBody // 使用注解 @ResponseBody
    @GetMapping("/course/whole/{courseId}") // 使用注解 @GetMapping("/course/whole/{courseId}")
    public CoursePreviewDto getCoursePublish(@PathVariable("courseId") Long courseId) { // 定义方法 getCoursePublish(...)
        //封装数据 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewDto = new CoursePreviewDto(); // 创建 CoursePreviewDto 对象并赋值给 coursePreviewDto

        //查询课程发布表 // 保留原作者写的行内注释
//        CoursePublish coursePublish = coursePublishService.getCoursePublish(courseId); // 保留原作者写的行内注释
        //先从缓存查询,缓存中有直接返回,没有再查询数据库 // 保留原作者写的行内注释
        CoursePublish coursePublish = coursePublishService.getCoursePublishCache(courseId); // 把 coursePublishService.getCoursePublishCache(courseId) 赋值给 coursePublish
        if(coursePublish == null){ // 定义方法 方法(...)
            return coursePreviewDto; // 返回 coursePreviewDto
        } // 代码块结束
        //开始向coursePreviewDto填充数据 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto(); // 创建 CourseBaseInfoDto 对象并赋值给 courseBaseInfoDto
        BeanUtils.copyProperties(coursePublish,courseBaseInfoDto); // 执行语句
        //课程计划信息 // 保留原作者写的行内注释
        String teachplanJson = coursePublish.getTeachplan(); // 把 coursePublish.getTeachplan() 的返回值赋给 teachplanJson
        //转成List<TeachplanDto> // 保留原作者写的行内注释
        List<TeachplanDto> teachplanDtos = JSON.parseArray(teachplanJson, TeachplanDto.class); // 把 JSON.parseArray(teachplanJson, TeachplanDto.class) 赋值给 teachplanDtos
        coursePreviewDto.setCourseBase(courseBaseInfoDto); // 执行语句
        coursePreviewDto.setTeachplans(teachplanDtos); // 执行语句
        return coursePreviewDto; // 返回 coursePreviewDto

    } // 代码块结束


    // 课程预览接口 // 保留原作者写的行内注释
    // 传入课程id,表示预览哪门课程 // 保留原作者写的行内注释
    @GetMapping("/coursepreview/{courseId}") // 使用注解 @GetMapping("/coursepreview/{courseId}")
    public ModelAndView preview(@PathVariable("courseId") Long courseId) { // 定义方法 preview(...)

        ModelAndView modelAndView = new ModelAndView(); // 创建 ModelAndView 对象并赋值给 modelAndView
        //查询课程的信息作为模型数据 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(courseId); // 把 coursePublishService.getCoursePreviewInfo(courseId) 赋值给 coursePreviewInfo
        //指定模型 // 保留原作者写的行内注释
        modelAndView.addObject("model", coursePreviewInfo); // 执行语句
        //指定模板 // 保留原作者写的行内注释
        modelAndView.setViewName("course_template");//根据视图名称加.ftl找到模板 // 执行语句
        return modelAndView; // 返回 modelAndView
    } // 代码块结束

    // 提交审核 // 保留原作者写的行内注释
    @ResponseBody // 使用注解 @ResponseBody
    @PostMapping("/courseaudit/commit/{courseId}") // 使用注解 @PostMapping("/courseaudit/commit/{courseId}")
    public void commitAudit(@PathVariable("courseId") Long courseId) { // 定义方法 commitAudit(...)
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
        coursePublishService.commitAudit(companyId, courseId); // 执行语句
    } // 代码块结束

    @ApiOperation("课程发布") // 使用注解 @ApiOperation("课程发布")
    @ResponseBody // 使用注解 @ResponseBody
    @PostMapping ("/coursepublish/{courseId}") // 使用注解 @PostMapping ("/coursepublish/{courseId}")
    public void coursepublish(@PathVariable("courseId") Long courseId){ // 定义方法 coursepublish(...)
        Long companyId = 1232141425L; // 把 1232141425L 赋值给 companyId
        coursePublishService.publish(companyId,courseId); // 执行语句
    } // 代码块结束

    @ApiOperation("查询课程发布信息") // 使用注解 @ApiOperation("查询课程发布信息")
    @ResponseBody // 使用注解 @ResponseBody
    @GetMapping("/r/coursepublish/{courseId}") // 使用注解 @GetMapping("/r/coursepublish/{courseId}")
    public CoursePublish getCoursepublish(@PathVariable("courseId") Long courseId) { // 定义方法 getCoursepublish(...)
        //查询课程发布信息 // 保留原作者写的行内注释
        CoursePublish coursePublish = coursePublishService.getCoursePublish(courseId); // 把 coursePublishService.getCoursePublish(courseId) 赋值给 coursePublish
        return coursePublish; // 返回 coursePublish
    } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api/FreemarkerController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import org.springframework.stereotype.Controller; // 导入 Controller 类
import org.springframework.web.bind.annotation.GetMapping; // 导入 GetMapping 类
import org.springframework.web.servlet.ModelAndView; // 导入 ModelAndView 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description Freemarker入门程序 // 保留原作者写的块注释
 * @date 2023/2/19 16:12 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Controller // 使用注解 @Controller
public class FreemarkerController { // 定义 class FreemarkerController

    @GetMapping("/testfreemarker") // 使用注解 @GetMapping("/testfreemarker")
    public ModelAndView test() { // 定义方法 test(...)

        ModelAndView modelAndView = new ModelAndView(); // 创建 ModelAndView 对象并赋值给 modelAndView
        //指定模型 // 保留原作者写的行内注释
        modelAndView.addObject("name","小明"); // 执行语句
        //指定模板 // 保留原作者写的行内注释
        modelAndView.setViewName("test");//根据视图名称加.ftl找到模板 // 执行语句
        return modelAndView; // 返回 modelAndView


    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api/SecurityUtil.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.alibaba.fastjson.JSON; // 导入 JSON 类
import lombok.Data; // 导入 Data 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.security.core.context.SecurityContextHolder; // 导入 SecurityContextHolder 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/24 11:26 // 保留原作者写的块注释
 * 获取当前用户身份工具类 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
public class SecurityUtil { // 定义 class SecurityUtil

    public static XcUser getUser() { // 定义方法 getUser(...)
        try { // 尝试执行可能抛出异常的代码
            //拿到当前用户身份 // 保留原作者写的行内注释
            Object principalObj = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); // 把 SecurityContextHolder.getContext().getAuthentication().getPrincipal() 的返回值赋给 principalObj
            if (principalObj instanceof String) { // 定义方法 方法(...)
                //取出用户身份信息 // 保留原作者写的行内注释
                String principal = principalObj.toString(); // 把 principalObj.toString() 的返回值赋给 principal
                //将json转成对象 // 保留原作者写的行内注释
                XcUser user = JSON.parseObject(principal, XcUser.class); // 把 JSON.parseObject(principal, XcUser.class) 赋值给 user
                return user; // 返回 user
            } // 代码块结束
        } catch (Exception e) { // 定义方法 catch(...)
            log.error("获取当前登录用户身份出错:{}", e.getMessage()); // 执行语句
            e.printStackTrace(); // 执行语句
        } // 代码块结束
        return null; // 返回 null
    } // 代码块结束


    @Data // 使用注解 @Data
    public static class XcUser implements Serializable { // 代码逻辑

        private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

        private String id; // 执行语句

        private String username; // 执行语句

        private String password; // 执行语句

        private String salt; // 执行语句

        private String name; // 执行语句
        private String nickname; // 执行语句
        private String wxUnionid; // 执行语句
        private String companyId; // 执行语句
        /** // 保留原作者写的块注释
         * 头像 // 保留原作者写的块注释
         */ // 保留原作者写的块注释
        private String userpic; // 执行语句

        private String utype; // 执行语句

        private LocalDateTime birthday; // 执行语句

        private String sex; // 执行语句

        private String email; // 执行语句

        private String cellphone; // 执行语句

        private String qq; // 执行语句

        /** // 保留原作者写的块注释
         * 用户状态 // 保留原作者写的块注释
         */ // 保留原作者写的块注释
        private String status; // 执行语句

        private LocalDateTime createTime; // 执行语句

        private LocalDateTime updateTime; // 执行语句


    } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/api/TeachplanController.java

package com.xuecheng.content.api; // 声明类所在的包路径

import com.xuecheng.content.model.dto.BindTeachplanMediaDto; // 导入 BindTeachplanMediaDto 类
import com.xuecheng.content.model.dto.SaveTeachplanDto; // 导入 SaveTeachplanDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.service.TeachplanService; // 导入 TeachplanService 类
import io.swagger.annotations.Api; // 导入 Api 类
import io.swagger.annotations.ApiOperation; // 导入 ApiOperation 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.web.bind.annotation.*; // 导入 * 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划管理相关的接口 // 保留原作者写的块注释
 * @date 2023/2/14 11:25 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Api(value = "课程计划编辑接口",tags = "课程计划编辑接口") // 使用注解 @Api(value = "课程计划编辑接口",tags = "课程计划编辑接口")
@RestController // 使用注解 @RestController
public class TeachplanController { // 定义 class TeachplanController

    @Autowired // 使用注解 @Autowired
    TeachplanService teachplanService; // 执行语句

   @ApiOperation("查询课程计划树形结构") // 使用注解 @ApiOperation("查询课程计划树形结构")
   //查询课程计划  GET /teachplan/22/tree-nodes // 保留原作者写的行内注释
   @GetMapping("/teachplan/{courseId}/tree-nodes") // 使用注解 @GetMapping("/teachplan/{courseId}/tree-nodes")
 public List<TeachplanDto> getTreeNodes(@PathVariable Long courseId){ // 定义方法 getTreeNodes(...)
       List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId); // 把 teachplanService.findTeachplanTree(courseId) 赋值给 teachplanTree

       return teachplanTree; // 返回 teachplanTree
   } // 代码块结束

    @ApiOperation("课程计划创建或修改") // 使用注解 @ApiOperation("课程计划创建或修改")
    @PostMapping("/teachplan") // 使用注解 @PostMapping("/teachplan")
    public void saveTeachplan( @RequestBody SaveTeachplanDto teachplan){ // 定义方法 saveTeachplan(...)
        teachplanService.saveTeachplan(teachplan); // 执行语句
    } // 代码块结束

    @ApiOperation(value = "课程计划和媒资信息绑定") // 使用注解 @ApiOperation(value = "课程计划和媒资信息绑定")
    @PostMapping("/teachplan/association/media") // 使用注解 @PostMapping("/teachplan/association/media")
    public void associationMedia(@RequestBody BindTeachplanMediaDto bindTeachplanMediaDto){ // 定义方法 associationMedia(...)
        teachplanService.associationMedia(bindTeachplanMediaDto); // 执行语句
    } // 代码块结束

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/config/ResouceServerConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; // 导入 EnableGlobalMethodSecurity 类
import org.springframework.security.config.annotation.web.builders.HttpSecurity; // 导入 HttpSecurity 类
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; // 导入 EnableResourceServer 类
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; // 导入 ResourceServerConfigurerAdapter 类
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; // 导入 ResourceServerSecurityConfigurer 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类

/** // 保留原作者写的块注释
 * @description 资源服务配置 // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @date 2022/10/18 16:33 // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
 @Configuration // 使用注解 @Configuration
 @EnableResourceServer // 使用注解 @EnableResourceServer
 @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) // 使用注解 @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
 public class ResouceServerConfig extends ResourceServerConfigurerAdapter { // 定义 class ResouceServerConfig


  //资源服务标识 // 保留原作者写的行内注释
  public static final String RESOURCE_ID = "xuecheng-plus"; // 把 "xuecheng-plus" 赋值给 RESOURCE_ID

  @Autowired // 使用注解 @Autowired
  TokenStore tokenStore; // 执行语句

  @Override // 使用注解 @Override
  public void configure(ResourceServerSecurityConfigurer resources) { // 定义方法 configure(...)
   resources.resourceId(RESOURCE_ID)//资源 id // 代码逻辑
           .tokenStore(tokenStore) // 代码逻辑
           .stateless(true); // 执行语句
  } // 代码块结束

 @Override // 使用注解 @Override
 public void configure(HttpSecurity http) throws Exception { // 定义方法 configure(...)
  http.csrf().disable() // 代码逻辑
          .authorizeRequests() // 代码逻辑
//                .antMatchers("/r/**","/course/**").authenticated()//所有/r/**的请求必须认证通过 // 保留原作者写的行内注释
          .anyRequest().permitAll() // 代码逻辑
  ; // 执行语句
 } // 代码块结束

 } // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/content/config/TokenConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; // 导入 AuthorizationServerTokenServices 类
import org.springframework.security.oauth2.provider.token.DefaultTokenServices; // 导入 DefaultTokenServices 类
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; // 导入 TokenEnhancerChain 类
import org.springframework.security.oauth2.provider.token.TokenStore; // 导入 TokenStore 类
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; // 导入 JwtAccessTokenConverter 类
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; // 导入 JwtTokenStore 类

import java.util.Arrays; // 导入 Arrays 类

/** // 保留原作者写的块注释
 * @author Administrator // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 **/ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class TokenConfig { // 定义 class TokenConfig

    String SIGNING_KEY = "mq123"; // 把 "mq123" 赋值给 SIGNING_KEY


//    @Bean // 保留原作者写的行内注释
//    public TokenStore tokenStore() { // 保留原作者写的行内注释
//        //使用内存存储令牌(普通令牌) // 保留原作者写的行内注释
//        return new InMemoryTokenStore(); // 保留原作者写的行内注释
//    } // 保留原作者写的行内注释

    @Autowired // 使用注解 @Autowired
    private JwtAccessTokenConverter accessTokenConverter; // 执行语句

    @Bean // 使用注解 @Bean
    public TokenStore tokenStore() { // 定义方法 tokenStore(...)
        return new JwtTokenStore(accessTokenConverter()); // 返回 new JwtTokenStore(accessTokenConverter())
    } // 代码块结束

    @Bean // 使用注解 @Bean
    public JwtAccessTokenConverter accessTokenConverter() { // 定义方法 accessTokenConverter(...)
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); // 创建 JwtAccessTokenConverter 对象并赋值给 converter
        converter.setSigningKey(SIGNING_KEY); // 执行语句
        return converter; // 返回 converter
    } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/java/com/xuecheng/ContentApplication.java

package com.xuecheng; // 声明类所在的包路径

import com.spring4all.swagger.EnableSwagger2Doc; // 导入 EnableSwagger2Doc 类
import org.springframework.boot.SpringApplication; // 导入 SpringApplication 类
import org.springframework.boot.autoconfigure.SpringBootApplication; // 导入 SpringBootApplication 类
import org.springframework.cloud.openfeign.EnableFeignClients; // 导入 EnableFeignClients 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 内容管理服务启动类 // 保留原作者写的块注释
 * @date 2023/2/11 15:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"}) // 使用注解 @EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"})
@EnableSwagger2Doc // 使用注解 @EnableSwagger2Doc
@SpringBootApplication // 使用注解 @SpringBootApplication
public class ContentApplication { // 定义 class ContentApplication
    public static void main(String[] args) { // 定义方法 main(...)
        SpringApplication.run(ContentApplication.class, args); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/resources/bootstrap.yml

#server:
#  servlet:
#    context-path: /content
#  port: 63041
#微服务配置
spring:
  application:
    name: content-api #服务名content-api-dev.yaml
  cloud:
    nacos:
      server-addr: 192.168.101.65:8848
      discovery: #服务注册相关配置
        namespace: dev402
        group: xuecheng-plus-project
      config: #配置文件相关配置
        namespace: dev402
        group: xuecheng-plus-project
        file-extension: yaml
        refresh-enabled: true
        extension-configs:
          - data-id: content-service-${spring.profiles.active}.yaml
            group: xuecheng-plus-project
            refresh: true
        shared-configs:
          - data-id: swagger-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: logging-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: freemarker-config-dev.yaml
            group: xuecheng-plus-common
            refresh: true
          - data-id: feign-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true  #profiles默认为dev
          - data-id: redis-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true


#  datasource:
#    driver-class-name: com.mysql.cj.jdbc.Driver
#    url: jdbc:mysql://192.168.101.65:3306/xc402_content?serverTimezone=UTC&userUnicode=true&useSSL=false&
#    username: root
#    password: mysql

  profiles:
    active: dev   #环境名

#logging:
#  config: classpath:log4j2-dev.xml  # 日志文件配置路径


#swagger:
#  title: "学成在线内容管理系统"
#  description: "内容系统管理系统对课程相关信息进行管理"
#  base-package: com.xuecheng.content
#  enabled: true
#  version: 1.0.0

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/resources/log4j2-dev.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<Configuration monitorInterval="180" packages=""> <!-- SQL 语句片段 -->
    <properties> <!-- SQL 语句片段 -->
        <property name="logdir">logs</property> <!-- SQL 语句片段 -->
        <property name="PATTERN">%date{YYYY-MM-dd HH:mm:ss,SSS} %level [%thread][%file:%line] - %msg%n%throwable</property> <!-- SQL 语句片段 -->
    </properties> <!-- SQL 语句片段 -->
    <Appenders> <!-- SQL 语句片段 -->
        <Console name="Console" target="SYSTEM_OUT"> <!-- SQL 语句片段 -->
            <PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
        </Console> <!-- SQL 语句片段 -->

        <RollingFile name="ErrorAppender" fileName="${logdir}/error.log" <!-- SQL 语句片段 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/error.%d{yyyy-MM-dd-HH}.log" append="true"> <!-- SQL 语句片段 -->
            <PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <!-- SQL 语句片段 -->
            <Policies> <!-- SQL 语句片段 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- SQL 语句片段 -->
            </Policies> <!-- SQL 语句片段 -->
        </RollingFile> <!-- SQL 语句片段 -->

        <RollingFile name="DebugAppender" fileName="${logdir}/info.log" <!-- SQL 语句片段 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/info.%d{yyyy-MM-dd-HH}.log" append="true"> <!-- SQL 语句片段 -->
            <PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- SQL 语句片段 -->
            <Policies> <!-- SQL 语句片段 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- SQL 语句片段 -->
            </Policies> <!-- SQL 语句片段 -->
        </RollingFile> <!-- SQL 语句片段 -->
        
        <!--异步appender-->
         <Async name="AsyncAppender" includeLocation="true"> <!-- SQL 语句片段 -->
            <AppenderRef ref="ErrorAppender"/> <!-- SQL 语句片段 -->
            <AppenderRef ref="DebugAppender"/> <!-- SQL 语句片段 -->
        </Async> <!-- SQL 语句片段 -->
    </Appenders> <!-- SQL 语句片段 -->
    
    <Loggers> <!-- SQL 语句片段 -->
         <!--过滤掉spring和mybatis的一些无用的debug信息
        <logger name="org.springframework" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
        <logger name="org.mybatis" level="INFO"> <!-- SQL 语句片段 -->
        </logger>--> <!-- SQL 语句片段 -->
        <logger name="cn.itcast.wanxinp2p.consumer.mapper" level="DEBUG"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->

        <logger name="springfox" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
		<logger name="org.apache.http" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
        <logger name="com.netflix.discovery" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
        
        <logger name="RocketmqCommon"  level="INFO" > <!-- SQL 语句片段 -->
		</logger> <!-- SQL 语句片段 -->
		
		<logger name="RocketmqRemoting" level="INFO"  > <!-- SQL 语句片段 -->
		</logger> <!-- SQL 语句片段 -->
		
		<logger name="RocketmqClient" level="WARN"> <!-- SQL 语句片段 -->
		</logger> <!-- SQL 语句片段 -->

        <logger name="org.dromara.hmily" level="WARN"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->

        <logger name="org.dromara.hmily.lottery" level="WARN"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->

        <logger name="org.dromara.hmily.bonuspoint" level="WARN"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
		
        <!--OFF   0-->
        <!--FATAL   100-->
        <!--ERROR   200-->
        <!--WARN   300-->
        <!--INFO   400-->
        <!--DEBUG   500-->
        <!--TRACE   600-->
        <!--ALL   Integer.MAX_VALUE-->
        <Root level="info" includeLocation="true"> <!-- SQL 语句片段 -->
            <AppenderRef ref="AsyncAppender"/> <!-- SQL 语句片段 -->
            <AppenderRef ref="Console"/> <!-- SQL 语句片段 -->
            <AppenderRef ref="DebugAppender"/> <!-- SQL 语句片段 -->
        </Root> <!-- SQL 语句片段 -->
    </Loggers> <!-- SQL 语句片段 -->
</Configuration> <!-- SQL 语句片段 -->

文件:xuecheng-plus-content/xuecheng-plus-content-api/src/main/resources/singleServerConfig.yaml

---
singleServerConfig:
  #如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,
  #那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒。
  #默认值:10000
  idleConnectionTimeout: 10000
  pingTimeout: 1000
  #同任何节点建立连接时的等待超时。时间单位是毫秒。
  #默认值:10000
  connectTimeout: 10000
  #等待节点回复命令的时间。该时间从命令发送成功时开始计时。
  #默认值:3000
  timeout: 3000
  #如果尝试达到 retryAttempts(命令失败重试次数)
  #仍然不能将命令发送至某个指定的节点时,将抛出错误。如果尝试在此限制之内发送成功,
  #则开始启用 timeout(命令等待超时) 计时。
  #默认值:3
  retryAttempts: 3
  #在某个节点执行相同或不同命令时,连续失败failedAttempts(执行失败最大次数)时,
  #该节点将被从可用节点列表里清除,直到 reconnectionTimeout(重新连接时间间隔) 超时以后再次尝试。
  #默认值:1500
  retryInterval: 1500
  #重新连接时间间隔
  reconnectionTimeout: 3000
  #执行失败最大次数
  failedAttempts: 3
  #密码
  password: redis
  #数据库选择 select 4
  database: 0
  #每个连接的最大订阅数量。
  #默认值:5
  subscriptionsPerConnection: 5
  #在Redis节点里显示的客户端名称。
  clientName: null
  #在Redis节点
  address: "redis://192.168.101.65:6379"
  #从节点发布和订阅连接的最小空闲连接数
  #默认值:1
  subscriptionConnectionMinimumIdleSize: 1
  #用于发布和订阅连接的连接池最大容量。连接池的连接数量自动弹性伸缩。
  #默认值:50
  subscriptionConnectionPoolSize: 50
  #节点最小空闲连接数
  #默认值:32
  connectionMinimumIdleSize: 32
  #节点连接池大小
  #默认值:64
  connectionPoolSize: 64
#这个线程池数量被所有RTopic对象监听器,RRemoteService调用者和RExecutorService任务共同共享。
#默认值: 当前处理核数量 * 2
threads: 8
#这个线程池数量是在一个Redisson实例内,被其创建的所有分布式数据类型和服务,
#以及底层客户端所一同共享的线程池里保存的线程数量。
#默认值: 当前处理核数量 * 2
nettyThreads: 8
#Redisson的对象编码类是用于将对象进行序列化和反序列化,以实现对该对象在Redis里的读取和存储。
#默认值: org.redisson.codec.JsonJacksonCodec
codec: !<org.redisson.codec.JsonJacksonCodec> {}
#传输模式
#默认值:TransportMode.NIO
transportMode: "NIO"

文件:xuecheng-plus-content/xuecheng-plus-content-model/pom.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- SQL 语句片段 -->
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- SQL 语句片段 -->
    <modelVersion>4.0.0</modelVersion> <!-- SQL 语句片段 -->
    <parent> <!-- SQL 语句片段 -->
        <artifactId>xuecheng-plus-content</artifactId> <!-- SQL 语句片段 -->
        <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
        <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
    </parent> <!-- SQL 语句片段 -->
    <artifactId>xuecheng-plus-content-model</artifactId> <!-- SQL 语句片段 -->
    <dependencies> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
            <artifactId>xuecheng-plus-base</artifactId> <!-- SQL 语句片段 -->
            <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <!--存在mybatisplus注解添加相关注解保证不报错-->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>com.baomidou</groupId> <!-- SQL 语句片段 -->
            <artifactId>mybatis-plus-annotation</artifactId> <!-- SQL 语句片段 -->
            <version>${mybatis-plus-boot-starter.version}</version> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>com.baomidou</groupId> <!-- SQL 语句片段 -->
            <artifactId>mybatis-plus-core</artifactId> <!-- SQL 语句片段 -->
            <version>${mybatis-plus-boot-starter.version}</version> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
        <dependency> <!-- SQL 语句片段 -->
            <groupId>org.projectlombok</groupId> <!-- SQL 语句片段 -->
            <artifactId>lombok</artifactId> <!-- SQL 语句片段 -->
        </dependency> <!-- SQL 语句片段 -->
    </dependencies> <!-- SQL 语句片段 -->

</project> <!-- SQL 语句片段 -->

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/AddCourseDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.base.exception.ValidationGroups; // 导入 ValidationGroups 类
import io.swagger.annotations.ApiModel; // 导入 ApiModel 类
import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

import javax.validation.constraints.NotEmpty; // 导入 NotEmpty 类
import javax.validation.constraints.Size; // 导入 Size 类
import java.math.BigDecimal; // 导入 BigDecimal 类

/** // 保留原作者写的块注释
 * @description 添加课程dto // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @date 2022/9/7 17:40 // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@ApiModel(value="AddCourseDto", description="新增课程基本信息") // 使用注解 @ApiModel(value="AddCourseDto", description="新增课程基本信息")
public class AddCourseDto { // 定义 class AddCourseDto

 @NotEmpty(message = "新增课程名称不能为空",groups={ValidationGroups.Inster.class}) // 使用注解 @NotEmpty(message = "新增课程名称不能为空",groups={ValidationGroups.Inster.class})
 @NotEmpty(message = "修改课程名称不能为空",groups={ValidationGroups.Update.class}) // 使用注解 @NotEmpty(message = "修改课程名称不能为空",groups={ValidationGroups.Update.class})
// @NotEmpty(message = "课程名称不能为空") // 保留原作者写的行内注释
 @ApiModelProperty(value = "课程名称", required = true) // 使用注解 @ApiModelProperty(value = "课程名称", required = true)
 private String name; // 执行语句

 @NotEmpty(message = "适用人群不能为空") // 使用注解 @NotEmpty(message = "适用人群不能为空")
 @Size(message = "适用人群内容过少",min = 10) // 使用注解 @Size(message = "适用人群内容过少",min = 10)
 @ApiModelProperty(value = "适用人群", required = true) // 使用注解 @ApiModelProperty(value = "适用人群", required = true)
 private String users; // 执行语句

 @ApiModelProperty(value = "课程标签") // 使用注解 @ApiModelProperty(value = "课程标签")
 private String tags; // 执行语句

 @NotEmpty(message = "课程分类不能为空") // 使用注解 @NotEmpty(message = "课程分类不能为空")
 @ApiModelProperty(value = "大分类", required = true) // 使用注解 @ApiModelProperty(value = "大分类", required = true)
 private String mt; // 执行语句

 @NotEmpty(message = "课程分类不能为空") // 使用注解 @NotEmpty(message = "课程分类不能为空")
 @ApiModelProperty(value = "小分类", required = true) // 使用注解 @ApiModelProperty(value = "小分类", required = true)
 private String st; // 执行语句

 @NotEmpty(message = "课程等级不能为空") // 使用注解 @NotEmpty(message = "课程等级不能为空")
 @ApiModelProperty(value = "课程等级", required = true) // 使用注解 @ApiModelProperty(value = "课程等级", required = true)
 private String grade; // 执行语句

 @ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true) // 使用注解 @ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true)
 private String teachmode; // 执行语句

 @ApiModelProperty(value = "课程介绍") // 使用注解 @ApiModelProperty(value = "课程介绍")
 @Size(message = "课程描述内容过少",min = 10) // 使用注解 @Size(message = "课程描述内容过少",min = 10)
 private String description; // 执行语句

 @ApiModelProperty(value = "课程图片", required = true) // 使用注解 @ApiModelProperty(value = "课程图片", required = true)
 private String pic; // 执行语句

 @NotEmpty(message = "收费规则不能为空") // 使用注解 @NotEmpty(message = "收费规则不能为空")
 @ApiModelProperty(value = "收费规则,对应数据字典", required = true) // 使用注解 @ApiModelProperty(value = "收费规则,对应数据字典", required = true)
 private String charge; // 执行语句

 @ApiModelProperty(value = "价格") // 使用注解 @ApiModelProperty(value = "价格")
 private Float price; // 执行语句
 @ApiModelProperty(value = "原价") // 使用注解 @ApiModelProperty(value = "原价")
 private Float originalPrice; // 执行语句


 @ApiModelProperty(value = "qq") // 使用注解 @ApiModelProperty(value = "qq")
 private String qq; // 执行语句

 @ApiModelProperty(value = "微信") // 使用注解 @ApiModelProperty(value = "微信")
 private String wechat; // 执行语句
 @ApiModelProperty(value = "电话") // 使用注解 @ApiModelProperty(value = "电话")
 private String phone; // 执行语句

 @ApiModelProperty(value = "有效期") // 使用注解 @ApiModelProperty(value = "有效期")
 private Integer validDays; // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/BindTeachplanMediaDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import io.swagger.annotations.ApiModel; // 导入 ApiModel 类
import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 绑定媒资和课程计划的模型类 // 保留原作者写的块注释
 * @date 2023/2/19 15:37 // 保留原作者写的块注释
 */ // 保留原作者写的块注释

@Data // 使用注解 @Data
@ApiModel(value="BindTeachplanMediaDto", description="教学计划-媒资绑定提交数据") // 使用注解 @ApiModel(value="BindTeachplanMediaDto", description="教学计划-媒资绑定提交数据")
public class BindTeachplanMediaDto { // 定义 class BindTeachplanMediaDto

 @ApiModelProperty(value = "媒资文件id", required = true) // 使用注解 @ApiModelProperty(value = "媒资文件id", required = true)
 private String mediaId; // 执行语句

 @ApiModelProperty(value = "媒资文件名称", required = true) // 使用注解 @ApiModelProperty(value = "媒资文件名称", required = true)
 private String fileName; // 执行语句

 @ApiModelProperty(value = "课程计划标识", required = true) // 使用注解 @ApiModelProperty(value = "课程计划标识", required = true)
 private Long teachplanId; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/CourseBaseInfoDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

import java.math.BigDecimal; // 导入 BigDecimal 类

/** // 保留原作者写的块注释
 * @description 课程基本信息dto // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @date 2022/9/7 17:44 // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CourseBaseInfoDto extends CourseBase { // 定义 class CourseBaseInfoDto


 /** // 保留原作者写的块注释
  * 收费规则,对应数据字典 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String charge; // 执行语句

 /** // 保留原作者写的块注释
  * 价格 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private Float price; // 执行语句


 /** // 保留原作者写的块注释
  * 原价 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private Float originalPrice; // 执行语句

 /** // 保留原作者写的块注释
  * 咨询qq // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String qq; // 执行语句

 /** // 保留原作者写的块注释
  * 微信 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String wechat; // 执行语句

 /** // 保留原作者写的块注释
  * 电话 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String phone; // 执行语句

 /** // 保留原作者写的块注释
  * 有效期天数 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private Integer validDays; // 执行语句

 /** // 保留原作者写的块注释
  * 大分类名称 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String mtName; // 执行语句

 /** // 保留原作者写的块注释
  * 小分类名称 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 private String stName; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/CourseCategoryTreeDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.content.model.po.CourseCategory; // 导入 CourseCategory 类
import lombok.Data; // 导入 Data 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 11:51 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CourseCategoryTreeDto extends CourseCategory implements java.io.Serializable { // 定义 class CourseCategoryTreeDto

   //子节点 // 保留原作者写的行内注释
   List<CourseCategoryTreeDto> childrenTreeNodes; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/CoursePreviewDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import lombok.Data; // 导入 Data 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程预览模型类 // 保留原作者写的块注释
 * @date 2023/2/21 10:00 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CoursePreviewDto { // 定义 class CoursePreviewDto

 //课程基本信息,营销信息 // 保留原作者写的行内注释
 private CourseBaseInfoDto courseBase; // 执行语句

 //课程计划信息 // 保留原作者写的行内注释
 private List<TeachplanDto> teachplans; // 执行语句

 //课程师资信息... // 保留原作者写的行内注释

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/EditCourseDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import io.swagger.annotations.ApiModelProperty; // 导入 ApiModelProperty 类
import lombok.Data; // 导入 Data 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/14 10:22 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class EditCourseDto extends AddCourseDto { // 定义 class EditCourseDto

 @ApiModelProperty(value = "课程id", required = true) // 使用注解 @ApiModelProperty(value = "课程id", required = true)
 private Long id; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/QueryCourseParamsDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import lombok.Data; // 导入 Data 类
import lombok.ToString; // 导入 ToString 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程查询条件模型类 // 保留原作者写的块注释
 * @date 2023/2/11 15:37 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@ToString // 使用注解 @ToString
public class QueryCourseParamsDto { // 定义 class QueryCourseParamsDto

    //审核状态 // 保留原作者写的行内注释
    private String auditStatus; // 执行语句
    //课程名称 // 保留原作者写的行内注释
    private String courseName; // 执行语句
    //发布状态 // 保留原作者写的行内注释
    private String publishStatus; // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/SaveTeachplanDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import lombok.Data; // 导入 Data 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 新增大章节、小章节、修改章节信息 // 保留原作者写的块注释
 * @date 2023/2/14 14:54 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class SaveTeachplanDto { // 定义 class SaveTeachplanDto

    /*** // 保留原作者写的块注释
     * 教学计划id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pname; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划父级Id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long parentid; // 执行语句

    /** // 保留原作者写的块注释
     * 层级,分为1、2、3级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer grade; // 执行语句

    /** // 保留原作者写的块注释
     * 课程类型:1视频、2文档 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mediaType; // 执行语句


    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程发布标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long coursePubId; // 执行语句


    /** // 保留原作者写的块注释
     * 是否支持试学或预览(试看) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String isPreview; // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/dto/TeachplanDto.java

package com.xuecheng.content.model.dto; // 声明类所在的包路径

import com.xuecheng.content.model.po.Teachplan; // 导入 Teachplan 类
import com.xuecheng.content.model.po.TeachplanMedia; // 导入 TeachplanMedia 类
import lombok.Data; // 导入 Data 类
import lombok.ToString; // 导入 ToString 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划信息模型类 // 保留原作者写的块注释
 * @date 2023/2/14 11:23 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@ToString // 使用注解 @ToString
public class TeachplanDto extends Teachplan { // 定义 class TeachplanDto
  //与媒资管理的信息 // 保留原作者写的行内注释
   private TeachplanMedia teachplanMedia; // 执行语句

  //小章节list // 保留原作者写的行内注释
   private List<TeachplanDto> teachPlanTreeNodes; // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/CourseBase.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程基本信息 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_base") // 使用注解 @TableName("course_base")
public class CourseBase implements Serializable { // 定义 class CourseBase

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 机构名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式(common普通,record 录播,live直播等) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 创建时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 修改时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT_UPDATE) // 使用注解 @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime changeDate; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String createPeople; // 执行语句

    /** // 保留原作者写的块注释
     * 更新人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String changePeople; // 执行语句

    /** // 保留原作者写的块注释
     * 审核状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String auditStatus; // 执行语句

    /** // 保留原作者写的块注释
     * 课程发布状态 未发布  已发布 下线 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/CourseCategory.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程分类 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_category") // 使用注解 @TableName("course_category")
public class CourseCategory implements Serializable { // 定义 class CourseCategory

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String id; // 执行语句

    /** // 保留原作者写的块注释
     * 分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 分类标签默认和名称一样 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String label; // 执行语句

    /** // 保留原作者写的块注释
     * 父结点id(第一级的父节点是0,自关联字段id) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String parentid; // 执行语句

    /** // 保留原作者写的块注释
     * 是否显示 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer isShow; // 执行语句

    /** // 保留原作者写的块注释
     * 排序字段 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer orderby; // 执行语句

    /** // 保留原作者写的块注释
     * 是否叶子 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer isLeaf; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/CourseMarket.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程营销信息 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_market") // 使用注解 @TableName("course_market")
public class CourseMarket implements Serializable { // 定义 class CourseMarket

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键,课程id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句

    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 咨询qq // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String qq; // 执行语句

    /** // 保留原作者写的块注释
     * 微信 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String wechat; // 执行语句

    /** // 保留原作者写的块注释
     * 电话 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String phone; // 执行语句

    /** // 保留原作者写的块注释
     * 有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/CoursePublish.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.FieldFill; // 导入 FieldFill 类
import com.baomidou.mybatisplus.annotation.TableField; // 导入 TableField 类
import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import com.fasterxml.jackson.annotation.JsonFormat; // 导入 JsonFormat 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_publish") // 使用注解 @TableName("course_publish")
public class CoursePublish implements Serializable { // 定义 class CoursePublish

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 公司名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String username; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mtName; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String stName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句

    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 课程营销信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String market; // 执行语句

    /** // 保留原作者写的块注释
     * 所有课程计划,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachplan; // 执行语句

    /** // 保留原作者写的块注释
     * 教师信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachers; // 执行语句

    /** // 保留原作者写的块注释
     * 发布时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 上架时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime onlineDate; // 执行语句

    /** // 保留原作者写的块注释
     * 下架时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime offlineDate; // 执行语句

    /** // 保留原作者写的块注释
     * 发布状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句

    /** // 保留原作者写的块注释
     * 备注 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String remark; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典--203 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句

    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 课程有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/CoursePublishPre.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.FieldFill; // 导入 FieldFill 类
import com.baomidou.mybatisplus.annotation.TableField; // 导入 TableField 类
import com.baomidou.mybatisplus.annotation.TableName; // 导入 TableName 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_publish_pre") // 使用注解 @TableName("course_publish_pre")
public class CoursePublishPre implements Serializable { // 定义 class CoursePublishPre

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 公司名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String username; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mtName; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String stName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句

    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 课程营销信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String market; // 执行语句

    /** // 保留原作者写的块注释
     * 所有课程计划,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachplan; // 执行语句

    /** // 保留原作者写的块注释
     * 教师信息,json格式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachers; // 执行语句

    /** // 保留原作者写的块注释
     * 提交时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 审核时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private LocalDateTime auditDate; // 执行语句

    /** // 保留原作者写的块注释
     * 状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句

    /** // 保留原作者写的块注释
     * 备注 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String remark; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典--203 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句

    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 课程有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/CourseTeacher.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程-教师关系表 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("course_teacher") // 使用注解 @TableName("course_teacher")
public class CourseTeacher implements Serializable { // 定义 class CourseTeacher

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 教师标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teacherName; // 执行语句

    /** // 保留原作者写的块注释
     * 教师职位 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String position; // 执行语句

    /** // 保留原作者写的块注释
     * 教师简介 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String introduction; // 执行语句

    /** // 保留原作者写的块注释
     * 照片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String photograph; // 执行语句

    /** // 保留原作者写的块注释
     * 创建时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/Teachplan.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程计划 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("teachplan") // 使用注解 @TableName("teachplan")
public class Teachplan implements Serializable { // 定义 class Teachplan

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pname; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划父级Id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long parentid; // 执行语句

    /** // 保留原作者写的块注释
     * 层级,分为1、2、3级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer grade; // 执行语句

    /** // 保留原作者写的块注释
     * 课程类型:1视频、2文档 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mediaType; // 执行语句

    /** // 保留原作者写的块注释
     * 开始直播时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private LocalDateTime startTime; // 执行语句

    /** // 保留原作者写的块注释
     * 直播结束时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private LocalDateTime endTime; // 执行语句

    /** // 保留原作者写的块注释
     * 章节及课程时介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句

    /** // 保留原作者写的块注释
     * 时长,单位时:分:秒 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String timelength; // 执行语句

    /** // 保留原作者写的块注释
     * 排序字段 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer orderby; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程发布标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long coursePubId; // 执行语句

    /** // 保留原作者写的块注释
     * 状态(1正常  0删除) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer status; // 执行语句

    /** // 保留原作者写的块注释
     * 是否支持试学或预览(试看) // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String isPreview; // 执行语句

    /** // 保留原作者写的块注释
     * 创建时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 修改时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField(fill = FieldFill.INSERT_UPDATE) // 使用注解 @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime changeDate; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-model/src/main/java/com/xuecheng/content/model/po/TeachplanMedia.java

package com.xuecheng.content.model.po; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.*; // 导入 * 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 *  // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
@TableName("teachplan_media") // 使用注解 @TableName("teachplan_media")
public class TeachplanMedia implements Serializable { // 定义 class TeachplanMedia

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableId(value = "id", type = IdType.AUTO) // 使用注解 @TableId(value = "id", type = IdType.AUTO)
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 媒资文件id // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mediaId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程计划标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long teachplanId; // 执行语句

    /** // 保留原作者写的块注释
     * 课程标识 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long courseId; // 执行语句

    /** // 保留原作者写的块注释
     * 媒资文件原始名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @TableField("media_fileName") // 使用注解 @TableField("media_fileName")
    private String mediaFilename; // 执行语句

    @TableField(fill = FieldFill.INSERT) // 使用注解 @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 创建人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String createPeople; // 执行语句

    /** // 保留原作者写的块注释
     * 修改人 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String changePeople; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/pom.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- SQL 语句片段 -->
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <!-- SQL 语句片段 -->
    <modelVersion>4.0.0</modelVersion> <!-- SQL 语句片段 -->
    <parent> <!-- SQL 语句片段 -->
        <artifactId>xuecheng-plus-content</artifactId> <!-- SQL 语句片段 -->
        <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
        <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
    </parent> <!-- SQL 语句片段 -->
    <artifactId>xuecheng-plus-content-service</artifactId> <!-- SQL 语句片段 -->
   <dependencies> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>com.alibaba.cloud</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
           <artifactId>xuecheng-plus-content-model</artifactId> <!-- SQL 语句片段 -->
           <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>com.xuecheng</groupId> <!-- SQL 语句片段 -->
           <artifactId>xuecheng-plus-message-sdk</artifactId> <!-- SQL 语句片段 -->
           <version>0.0.1-SNAPSHOT</version> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>com.xuxueli</groupId> <!-- SQL 语句片段 -->
           <artifactId>xxl-job-core</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <!-- MySQL 驱动 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>mysql</groupId> <!-- SQL 语句片段 -->
           <artifactId>mysql-connector-java</artifactId> <!-- SQL 语句片段 -->
           <scope>runtime</scope> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->

       <!-- mybatis plus的依赖 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>com.baomidou</groupId> <!-- SQL 语句片段 -->
           <artifactId>mybatis-plus-boot-starter</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-cloud-context</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <!-- Spring Boot 集成 Junit -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-boot-starter-test</artifactId> <!-- SQL 语句片段 -->
           <scope>test</scope> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <!-- 排除 Spring Boot 依赖的日志包冲突 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-boot-starter</artifactId> <!-- SQL 语句片段 -->
           <exclusions> <!-- SQL 语句片段 -->
               <exclusion> <!-- SQL 语句片段 -->
                   <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
                   <artifactId>spring-boot-starter-logging</artifactId> <!-- SQL 语句片段 -->
               </exclusion> <!-- SQL 语句片段 -->
           </exclusions> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-boot-starter-data-redis</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.apache.commons</groupId> <!-- SQL 语句片段 -->
           <artifactId>commons-pool2</artifactId> <!-- SQL 语句片段 -->
           <version>2.6.2</version> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->

       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.redisson</groupId> <!-- SQL 语句片段 -->
           <artifactId>redisson-spring-boot-starter</artifactId> <!-- SQL 语句片段 -->
           <version>3.11.2</version> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->

       <!-- Spring Boot 集成 log4j2 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-boot-starter-log4j2</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.springframework.boot</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-boot-starter-freemarker</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>com.alibaba.cloud</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <!-- Spring Cloud 微服务远程调用 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>org.springframework.cloud</groupId> <!-- SQL 语句片段 -->
           <artifactId>spring-cloud-starter-openfeign</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>io.github.openfeign</groupId> <!-- SQL 语句片段 -->
           <artifactId>feign-httpclient</artifactId> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <!--feign支持Multipart格式传参-->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>io.github.openfeign.form</groupId> <!-- SQL 语句片段 -->
           <artifactId>feign-form</artifactId> <!-- SQL 语句片段 -->
           <version>3.8.0</version> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
       <dependency> <!-- SQL 语句片段 -->
           <groupId>io.github.openfeign.form</groupId> <!-- SQL 语句片段 -->
           <artifactId>feign-form-spring</artifactId> <!-- SQL 语句片段 -->
           <version>3.8.0</version> <!-- SQL 语句片段 -->
       </dependency> <!-- SQL 语句片段 -->
   </dependencies> <!-- SQL 语句片段 -->

</project> <!-- SQL 语句片段 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/config/MultipartSupportConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import feign.codec.Encoder; // 导入 Encoder 类
import feign.form.spring.SpringFormEncoder; // 导入 SpringFormEncoder 类
import org.apache.commons.fileupload.FileItem; // 导入 FileItem 类
import org.apache.commons.fileupload.disk.DiskFileItemFactory; // 导入 DiskFileItemFactory 类
import org.apache.commons.io.IOUtils; // 导入 IOUtils 类
import org.springframework.beans.factory.ObjectFactory; // 导入 ObjectFactory 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; // 导入 HttpMessageConverters 类
import org.springframework.cloud.openfeign.support.SpringEncoder; // 导入 SpringEncoder 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类
import org.springframework.context.annotation.Primary; // 导入 Primary 类
import org.springframework.context.annotation.Scope; // 导入 Scope 类
import org.springframework.http.MediaType; // 导入 MediaType 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类
import org.springframework.web.multipart.commons.CommonsMultipartFile; // 导入 CommonsMultipartFile 类

import java.io.File; // 导入 File 类
import java.io.FileInputStream; // 导入 FileInputStream 类
import java.io.OutputStream; // 导入 OutputStream 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2022/10/15 22:13 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class MultipartSupportConfig { // 定义 class MultipartSupportConfig

    @Autowired // 使用注解 @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters; // 执行语句

    @Bean // 使用注解 @Bean
    @Primary//注入相同类型的bean时优先使用 // 使用注解 @Primary
    @Scope("prototype") // 使用注解 @Scope("prototype")
    public Encoder feignEncoder() { // 定义方法 feignEncoder(...)
        return new SpringFormEncoder(new SpringEncoder(messageConverters)); // 返回 new SpringFormEncoder(new SpringEncoder(messageConverters))
    } // 代码块结束

    //将file转为Multipart // 保留原作者写的行内注释
    public static MultipartFile getMultipartFile(File file) { // 定义方法 getMultipartFile(...)
        FileItem item = new DiskFileItemFactory().createItem("file", MediaType.MULTIPART_FORM_DATA_VALUE, true, file.getName()); // 创建 DiskFileItemFactory 对象并赋值给 item
        try (FileInputStream inputStream = new FileInputStream(file); // 尝试执行可能抛出异常的代码
             OutputStream outputStream = item.getOutputStream();) { // 定义方法 方法(...)
            IOUtils.copy(inputStream, outputStream); // 执行语句

        } catch (Exception e) { // 定义方法 catch(...)
            e.printStackTrace(); // 执行语句
        } // 代码块结束
        return new CommonsMultipartFile(item); // 返回 new CommonsMultipartFile(item)
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/config/MybatisPlusConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import com.baomidou.mybatisplus.annotation.DbType; // 导入 DbType 类
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; // 导入 MybatisPlusInterceptor 类
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; // 导入 PaginationInnerInterceptor 类
import org.mybatis.spring.annotation.MapperScan; // 导入 MapperScan 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:23 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
@MapperScan("com.xuecheng.content.mapper") // 使用注解 @MapperScan("com.xuecheng.content.mapper")
public class MybatisPlusConfig { // 定义 class MybatisPlusConfig
 /** // 保留原作者写的块注释
  * 定义分页拦截器 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 @Bean // 使用注解 @Bean
 public MybatisPlusInterceptor mybatisPlusInterceptor() { // 定义方法 mybatisPlusInterceptor(...)
  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 创建 MybatisPlusInterceptor 对象并赋值给 interceptor
  interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 执行语句
  return interceptor; // 返回 interceptor
 } // 代码块结束


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/config/XxlJobConfig.java

package com.xuecheng.content.config; // 声明类所在的包路径

import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; // 导入 XxlJobSpringExecutor 类
import org.slf4j.Logger; // 导入 Logger 类
import org.slf4j.LoggerFactory; // 导入 LoggerFactory 类
import org.springframework.beans.factory.annotation.Value; // 导入 Value 类
import org.springframework.context.annotation.Bean; // 导入 Bean 类
import org.springframework.context.annotation.Configuration; // 导入 Configuration 类

/** // 保留原作者写的块注释
 * xxl-job config // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author xuxueli 2017-04-28 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Configuration // 使用注解 @Configuration
public class XxlJobConfig { // 定义 class XxlJobConfig
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); // 把 LoggerFactory.getLogger(XxlJobConfig.class) 赋值给 logger

    @Value("${xxl.job.admin.addresses}") // 使用注解 @Value("${xxl.job.admin.addresses}")
    private String adminAddresses; // 执行语句

    @Value("${xxl.job.accessToken}") // 使用注解 @Value("${xxl.job.accessToken}")
    private String accessToken; // 执行语句

    @Value("${xxl.job.executor.appname}") // 使用注解 @Value("${xxl.job.executor.appname}")
    private String appname; // 执行语句

    @Value("${xxl.job.executor.address}") // 使用注解 @Value("${xxl.job.executor.address}")
    private String address; // 执行语句

    @Value("${xxl.job.executor.ip}") // 使用注解 @Value("${xxl.job.executor.ip}")
    private String ip; // 执行语句

    @Value("${xxl.job.executor.port}") // 使用注解 @Value("${xxl.job.executor.port}")
    private int port; // 执行语句

    @Value("${xxl.job.executor.logpath}") // 使用注解 @Value("${xxl.job.executor.logpath}")
    private String logPath; // 执行语句

    @Value("${xxl.job.executor.logretentiondays}") // 使用注解 @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays; // 执行语句


    @Bean // 使用注解 @Bean
    public XxlJobSpringExecutor xxlJobExecutor() { // 定义方法 xxlJobExecutor(...)
        logger.info(">>>>>>>>>>> xxl-job config init."); // 执行语句
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); // 创建 XxlJobSpringExecutor 对象并赋值给 xxlJobSpringExecutor
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses); // 执行语句
        xxlJobSpringExecutor.setAppname(appname); // 执行语句
        xxlJobSpringExecutor.setAddress(address); // 执行语句
        xxlJobSpringExecutor.setIp(ip); // 执行语句
        xxlJobSpringExecutor.setPort(port); // 执行语句
        xxlJobSpringExecutor.setAccessToken(accessToken); // 执行语句
        xxlJobSpringExecutor.setLogPath(logPath); // 执行语句
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); // 执行语句

        return xxlJobSpringExecutor; // 返回 xxlJobSpringExecutor
    } // 代码块结束

    /** // 保留原作者写的块注释
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; // 保留原作者写的块注释
     * // 保留原作者写的块注释
     *      1、引入依赖: // 保留原作者写的块注释
     *          <dependency> // 保留原作者写的块注释
     *             <groupId>org.springframework.cloud</groupId> // 保留原作者写的块注释
     *             <artifactId>spring-cloud-commons</artifactId> // 保留原作者写的块注释
     *             <version>${version}</version> // 保留原作者写的块注释
     *         </dependency> // 保留原作者写的块注释
     * // 保留原作者写的块注释
     *      2、配置文件,或者容器启动变量 // 保留原作者写的块注释
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' // 保留原作者写的块注释
     * // 保留原作者写的块注释
     *      3、获取IP // 保留原作者写的块注释
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); // 保留原作者写的块注释
     */ // 保留原作者写的块注释


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/feignclient/CourseIndex.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import com.alibaba.fastjson.annotation.JSONField; // 导入 JSONField 类
import com.fasterxml.jackson.annotation.JsonFormat; // 导入 JsonFormat 类
import lombok.Data; // 导入 Data 类

import java.io.Serializable; // 导入 Serializable 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程索引信息 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Data // 使用注解 @Data
public class CourseIndex implements Serializable { // 定义 class CourseIndex

    private static final long serialVersionUID = 1L; // 把 1L 赋值给 serialVersionUID

    /** // 保留原作者写的块注释
     * 主键 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long id; // 执行语句

    /** // 保留原作者写的块注释
     * 机构ID // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Long companyId; // 执行语句

    /** // 保留原作者写的块注释
     * 公司名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String companyName; // 执行语句

    /** // 保留原作者写的块注释
     * 课程名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String name; // 执行语句

    /** // 保留原作者写的块注释
     * 适用人群 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String users; // 执行语句

    /** // 保留原作者写的块注释
     * 标签 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String tags; // 执行语句


    /** // 保留原作者写的块注释
     * 大分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mt; // 执行语句

    /** // 保留原作者写的块注释
     * 大分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String mtName; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String st; // 执行语句

    /** // 保留原作者写的块注释
     * 小分类名称 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String stName; // 执行语句



    /** // 保留原作者写的块注释
     * 课程等级 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String grade; // 执行语句

    /** // 保留原作者写的块注释
     * 教育模式 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String teachmode; // 执行语句
    /** // 保留原作者写的块注释
     * 课程图片 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String pic; // 执行语句

    /** // 保留原作者写的块注释
     * 课程介绍 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String description; // 执行语句


    /** // 保留原作者写的块注释
     * 发布时间 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    @JSONField(format="yyyy-MM-dd HH:mm:ss") // 使用注解 @JSONField(format="yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") // 使用注解 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createDate; // 执行语句

    /** // 保留原作者写的块注释
     * 状态 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String status; // 执行语句

    /** // 保留原作者写的块注释
     * 备注 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String remark; // 执行语句

    /** // 保留原作者写的块注释
     * 收费规则,对应数据字典--203 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private String charge; // 执行语句

    /** // 保留原作者写的块注释
     * 现价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float price; // 执行语句
    /** // 保留原作者写的块注释
     * 原价 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Float originalPrice; // 执行语句

    /** // 保留原作者写的块注释
     * 课程有效期天数 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private Integer validDays; // 执行语句


} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/feignclient/MediaServiceClient.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import com.xuecheng.content.config.MultipartSupportConfig; // 导入 MultipartSupportConfig 类
import org.springframework.cloud.openfeign.FeignClient; // 导入 FeignClient 类
import org.springframework.http.MediaType; // 导入 MediaType 类
import org.springframework.web.bind.annotation.RequestMapping; // 导入 RequestMapping 类
import org.springframework.web.bind.annotation.RequestParam; // 导入 RequestParam 类
import org.springframework.web.bind.annotation.RequestPart; // 导入 RequestPart 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 远程调用媒资服务的接口 // 保留原作者写的块注释
 * @date 2023/2/22 10:26 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
//使用fallback定义降级类是无法拿到熔断异常,使用FallbackFactory可以拿到熔断的异常信息 // 保留原作者写的行内注释
@FeignClient(value = "media-api",configuration = {MultipartSupportConfig.class},fallbackFactory = MediaServiceClientFallbackFactory.class) // 使用注解 @FeignClient(value = "media-api",configuration = {MultipartSupportConfig.class},fallbackFactory = MediaServiceClientFallbackFactory.class)
public interface MediaServiceClient { // 定义 interface MediaServiceClient

    @RequestMapping(value = "/media/upload/coursefile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE) // 使用注解 @RequestMapping(value = "/media/upload/coursefile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String upload(@RequestPart("filedata") MultipartFile filedata, // 代码逻辑
                                      @RequestParam(value= "objectName",required=false) String objectName) throws IOException; // 使用注解 @RequestParam(value= "objectName",required=false) String objectName) throws IOException;

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/feignclient/MediaServiceClientFallback.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 11:06 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public class MediaServiceClientFallback implements MediaServiceClient{ // 定义 class MediaServiceClientFallback
 @Override // 使用注解 @Override
 public String upload(MultipartFile filedata, String objectName) throws IOException { // 定义方法 upload(...)

  return null; // 返回 null
 } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/feignclient/MediaServiceClientFallbackFactory.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import feign.hystrix.FallbackFactory; // 导入 FallbackFactory 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.stereotype.Component; // 导入 Component 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 11:09 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Component // 使用注解 @Component
public class MediaServiceClientFallbackFactory implements FallbackFactory<MediaServiceClient> { // 定义 class MediaServiceClientFallbackFactory
 //拿到了熔断的异常信息throwable // 保留原作者写的行内注释
 @Override // 使用注解 @Override
 public MediaServiceClient create(Throwable throwable) { // 定义方法 create(...)

  return new MediaServiceClient() { // 定义方法 MediaServiceClient(...)
   //发生熔断上传服务调用此方法执行降级逻辑 // 保留原作者写的行内注释
   @Override // 使用注解 @Override
   public String upload(MultipartFile filedata, String objectName) throws IOException { // 定义方法 upload(...)
     log.debug("远程调用上传文件的接口发生熔断:{}",throwable.toString(),throwable); // 执行语句
    return null; // 返回 null
   } // 代码块结束
  }; // 执行语句
 } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/feignclient/SearchServiceClient.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import org.springframework.cloud.openfeign.FeignClient; // 导入 FeignClient 类
import org.springframework.web.bind.annotation.PostMapping; // 导入 PostMapping 类
import org.springframework.web.bind.annotation.RequestBody; // 导入 RequestBody 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 14:47 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@FeignClient(value = "search",fallbackFactory = SearchServiceClientFallbackFactory.class) // 使用注解 @FeignClient(value = "search",fallbackFactory = SearchServiceClientFallbackFactory.class)
public interface SearchServiceClient { // 定义 interface SearchServiceClient

 @PostMapping("/search/index/course") // 使用注解 @PostMapping("/search/index/course")
 public Boolean add(@RequestBody CourseIndex courseIndex); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/feignclient/SearchServiceClientFallbackFactory.java

package com.xuecheng.content.feignclient; // 声明类所在的包路径

import feign.hystrix.FallbackFactory; // 导入 FallbackFactory 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.stereotype.Component; // 导入 Component 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/22 14:50 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Component // 使用注解 @Component
public class SearchServiceClientFallbackFactory implements FallbackFactory<SearchServiceClient> { // 定义 class SearchServiceClientFallbackFactory
    @Override // 使用注解 @Override
    public SearchServiceClient create(Throwable throwable) { // 定义方法 create(...)
        return new SearchServiceClient() { // 定义方法 SearchServiceClient(...)
            @Override // 使用注解 @Override
            public Boolean add(CourseIndex courseIndex) { // 定义方法 add(...)
                log.error("添加课程索引发生熔断,索引信息:{},熔断异常:{}",courseIndex,throwable.toString(),throwable); // 执行语句
                //走降级了返回 false // 保留原作者写的行内注释
                return false; // 返回 false
            } // 代码块结束
        }; // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseBaseMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程基本信息 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
// 课程预览使用的dao层 // 保留原作者写的行内注释
public interface CourseBaseMapper extends BaseMapper<CourseBase> { // 定义 interface CourseBaseMapper

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseBaseMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseBaseMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseBase"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="company_id" property="companyId" /> <!-- 字段与属性的映射规则 -->
        <result column="company_name" property="companyName" /> <!-- 字段与属性的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="users" property="users" /> <!-- 字段与属性的映射规则 -->
        <result column="tags" property="tags" /> <!-- 字段与属性的映射规则 -->
        <result column="mt" property="mt" /> <!-- 字段与属性的映射规则 -->
        <result column="st" property="st" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="teachmode" property="teachmode" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="pic" property="pic" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="change_date" property="changeDate" /> <!-- 字段与属性的映射规则 -->
        <result column="create_people" property="createPeople" /> <!-- 字段与属性的映射规则 -->
        <result column="change_people" property="changePeople" /> <!-- 字段与属性的映射规则 -->
        <result column="audit_status" property="auditStatus" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, company_id, company_name, name, users, tags, mt, st, grade, teachmode, description, pic, create_date, change_date, create_people, change_people, audit_status, status <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseCategoryMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.po.CourseCategory; // 导入 CourseCategory 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程分类 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseCategoryMapper extends BaseMapper<CourseCategory> { // 定义 interface CourseCategoryMapper

    //使用递归查询分类 // 保留原作者写的行内注释
    public List<CourseCategoryTreeDto> selectTreeNodes(String id); // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseCategoryMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseCategoryMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseCategory"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="label" property="label" /> <!-- 字段与属性的映射规则 -->
        <result column="parentid" property="parentid" /> <!-- 字段与属性的映射规则 -->
        <result column="is_show" property="isShow" /> <!-- 字段与属性的映射规则 -->
        <result column="orderby" property="orderby" /> <!-- 字段与属性的映射规则 -->
        <result column="is_leaf" property="isLeaf" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, name, label, parentid, is_show, orderby, is_leaf <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

    <select id="selectTreeNodes"  parameterType="string" resultType="com.xuecheng.content.model.dto.CourseCategoryTreeDto"> <!-- 定义 select 查询语句 -->
        with recursive t1 as ( <!-- SQL 语句片段 -->
            select * from course_category where id=#{id} <!-- 占位符参数 -->
            union all <!-- SQL 语句片段 -->
            select t2.* from course_category t2 inner join t1 on t1.id = t2.parentid <!-- SQL 语句片段 -->

        ) <!-- SQL 语句片段 -->
        select * from t1 <!-- SQL 语句片段 -->
        order by t1.id <!-- SQL 语句片段 -->

    </select> <!-- SQL 语句片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseMarketMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CourseMarket; // 导入 CourseMarket 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程营销信息 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseMarketMapper extends BaseMapper<CourseMarket> { // 定义 interface CourseMarketMapper

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseMarketMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseMarketMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseMarket"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="charge" property="charge" /> <!-- 字段与属性的映射规则 -->
        <result column="price" property="price" /> <!-- 字段与属性的映射规则 -->
        <result column="original_price" property="originalPrice" /> <!-- 字段与属性的映射规则 -->
        <result column="qq" property="qq" /> <!-- 字段与属性的映射规则 -->
        <result column="wechat" property="wechat" /> <!-- 字段与属性的映射规则 -->
        <result column="phone" property="phone" /> <!-- 字段与属性的映射规则 -->
        <result column="valid_days" property="validDays" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, charge, price, original_price, qq, wechat, phone, valid_days <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CoursePublishMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CoursePublishMapper extends BaseMapper<CoursePublish> { // 定义 interface CoursePublishMapper

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CoursePublishMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CoursePublishMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CoursePublish"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="company_id" property="companyId" /> <!-- 字段与属性的映射规则 -->
        <result column="company_name" property="companyName" /> <!-- 字段与属性的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="users" property="users" /> <!-- 字段与属性的映射规则 -->
        <result column="tags" property="tags" /> <!-- 字段与属性的映射规则 -->
        <result column="username" property="username" /> <!-- 字段与属性的映射规则 -->
        <result column="mt" property="mt" /> <!-- 字段与属性的映射规则 -->
        <result column="mt_name" property="mtName" /> <!-- 字段与属性的映射规则 -->
        <result column="st" property="st" /> <!-- 字段与属性的映射规则 -->
        <result column="st_name" property="stName" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="teachmode" property="teachmode" /> <!-- 字段与属性的映射规则 -->
        <result column="pic" property="pic" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="market" property="market" /> <!-- 字段与属性的映射规则 -->
        <result column="teachplan" property="teachplan" /> <!-- 字段与属性的映射规则 -->
        <result column="teachers" property="teachers" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="online_date" property="onlineDate" /> <!-- 字段与属性的映射规则 -->
        <result column="offline_date" property="offlineDate" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
        <result column="remark" property="remark" /> <!-- 字段与属性的映射规则 -->
        <result column="charge" property="charge" /> <!-- 字段与属性的映射规则 -->
        <result column="price" property="price" /> <!-- 字段与属性的映射规则 -->
        <result column="original_price" property="originalPrice" /> <!-- 字段与属性的映射规则 -->
        <result column="valid_days" property="validDays" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, company_id, company_name, name, users, tags, username, mt, mt_name, st, st_name, grade, teachmode, pic, description, market, teachplan, teachers, create_date, online_date, offline_date, status, remark, charge, price, original_price, valid_days <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CoursePublishPreMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CoursePublishPre; // 导入 CoursePublishPre 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程发布 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CoursePublishPreMapper extends BaseMapper<CoursePublishPre> { // 定义 interface CoursePublishPreMapper

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CoursePublishPreMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CoursePublishPreMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CoursePublishPre"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="company_id" property="companyId" /> <!-- 字段与属性的映射规则 -->
        <result column="company_name" property="companyName" /> <!-- 字段与属性的映射规则 -->
        <result column="name" property="name" /> <!-- 字段与属性的映射规则 -->
        <result column="users" property="users" /> <!-- 字段与属性的映射规则 -->
        <result column="tags" property="tags" /> <!-- 字段与属性的映射规则 -->
        <result column="username" property="username" /> <!-- 字段与属性的映射规则 -->
        <result column="mt" property="mt" /> <!-- 字段与属性的映射规则 -->
        <result column="mt_name" property="mtName" /> <!-- 字段与属性的映射规则 -->
        <result column="st" property="st" /> <!-- 字段与属性的映射规则 -->
        <result column="st_name" property="stName" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="teachmode" property="teachmode" /> <!-- 字段与属性的映射规则 -->
        <result column="pic" property="pic" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="market" property="market" /> <!-- 字段与属性的映射规则 -->
        <result column="teachplan" property="teachplan" /> <!-- 字段与属性的映射规则 -->
        <result column="teachers" property="teachers" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="audit_date" property="auditDate" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
        <result column="remark" property="remark" /> <!-- 字段与属性的映射规则 -->
        <result column="charge" property="charge" /> <!-- 字段与属性的映射规则 -->
        <result column="price" property="price" /> <!-- 字段与属性的映射规则 -->
        <result column="original_price" property="originalPrice" /> <!-- 字段与属性的映射规则 -->
        <result column="valid_days" property="validDays" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, company_id, company_name, name, users, tags, username, mt, mt_name, st, st_name, grade, teachmode, pic, description, market, teachplan, teachers, create_date, audit_date, status, remark, charge, price, original_price, valid_days <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseTeacherMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.CourseTeacher; // 导入 CourseTeacher 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程-教师关系表 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseTeacherMapper extends BaseMapper<CourseTeacher> { // 定义 interface CourseTeacherMapper

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/CourseTeacherMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.CourseTeacherMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseTeacher"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="course_id" property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="teacher_name" property="teacherName" /> <!-- 字段与属性的映射规则 -->
        <result column="position" property="position" /> <!-- 字段与属性的映射规则 -->
        <result column="introduction" property="introduction" /> <!-- 字段与属性的映射规则 -->
        <result column="photograph" property="photograph" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, course_id, teacher_name, position, introduction, photograph, create_date <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/TeachplanMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.Teachplan; // 导入 Teachplan 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 * 课程计划 Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface TeachplanMapper extends BaseMapper<Teachplan> { // 定义 interface TeachplanMapper

    //课程计划查询 // 保留原作者写的行内注释
    public List<TeachplanDto> selectTreeNodes(Long courseId); // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/TeachplanMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.TeachplanMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.Teachplan"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="pname" property="pname" /> <!-- 字段与属性的映射规则 -->
        <result column="parentid" property="parentid" /> <!-- 字段与属性的映射规则 -->
        <result column="grade" property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="media_type" property="mediaType" /> <!-- 字段与属性的映射规则 -->
        <result column="start_time" property="startTime" /> <!-- 字段与属性的映射规则 -->
        <result column="end_time" property="endTime" /> <!-- 字段与属性的映射规则 -->
        <result column="description" property="description" /> <!-- 字段与属性的映射规则 -->
        <result column="timelength" property="timelength" /> <!-- 字段与属性的映射规则 -->
        <result column="orderby" property="orderby" /> <!-- 字段与属性的映射规则 -->
        <result column="course_id" property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="course_pub_id" property="coursePubId" /> <!-- 字段与属性的映射规则 -->
        <result column="status" property="status" /> <!-- 字段与属性的映射规则 -->
        <result column="is_preview" property="isPreview" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="change_date" property="changeDate" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, pname, parentid, grade, media_type, start_time, end_time, description, timelength, orderby, course_id, course_pub_id, status, is_preview, create_date, change_date <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

    <resultMap id="treeNodeResultMap" type="com.xuecheng.content.model.dto.TeachplanDto"> <!-- 定义结果映射 -->

        <id     column="one_id"        property="id" /> <!-- 主键列的映射规则 -->
        <result column="one_pname"      property="pname" /> <!-- 字段与属性的映射规则 -->
        <result column="one_parentid"     property="parentid" /> <!-- 字段与属性的映射规则 -->
        <result column="one_grade"  property="grade" /> <!-- 字段与属性的映射规则 -->
        <result column="one_mediaType"   property="mediaType" /> <!-- 字段与属性的映射规则 -->
        <result column="one_stratTime"   property="stratTime" /> <!-- 字段与属性的映射规则 -->
        <result column="one_endTime"   property="endTime" /> <!-- 字段与属性的映射规则 -->
        <result column="one_orderby"   property="orderby" /> <!-- 字段与属性的映射规则 -->
        <result column="one_courseId"   property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="one_coursePubId"   property="coursePubId" /> <!-- 字段与属性的映射规则 -->
        <!--映射子节点,一对多映射,ofType list中的对象类型-->
        <collection property="teachPlanTreeNodes" ofType="com.xuecheng.content.model.dto.TeachplanDto"> <!-- SQL 语句片段 -->
            <id     column="two_id"        property="id" /> <!-- 主键列的映射规则 -->
            <result column="two_pname"      property="pname" /> <!-- 字段与属性的映射规则 -->
            <result column="two_parentid"     property="parentid" /> <!-- 字段与属性的映射规则 -->
            <result column="two_grade"  property="grade" /> <!-- 字段与属性的映射规则 -->
            <result column="two_mediaType"   property="mediaType" /> <!-- 字段与属性的映射规则 -->
            <result column="two_stratTime"   property="stratTime" /> <!-- 字段与属性的映射规则 -->
            <result column="two_endTime"   property="endTime" /> <!-- 字段与属性的映射规则 -->
            <result column="two_orderby"   property="orderby" /> <!-- 字段与属性的映射规则 -->
            <result column="two_courseId"   property="courseId" /> <!-- 字段与属性的映射规则 -->
            <result column="two_coursePubId"   property="coursePubId" /> <!-- 字段与属性的映射规则 -->
            <!--一对一映射-->
            <association property="teachplanMedia" javaType="com.xuecheng.content.model.po.TeachplanMedia"> <!-- SQL 语句片段 -->
                <id column="teachplanMeidaId" property="id"/> <!-- 主键列的映射规则 -->
                <result column="mediaFilename" property="mediaFilename"/> <!-- 字段与属性的映射规则 -->
                <result column="mediaId" property="mediaId"/> <!-- 字段与属性的映射规则 -->

            </association> <!-- SQL 语句片段 -->
        </collection> <!-- SQL 语句片段 -->

    </resultMap> <!-- 结束 resultMap 区块 -->


    <select id="selectTreeNodes" parameterType="long" resultMap="treeNodeResultMap"> <!-- 定义 select 查询语句 -->

        select <!-- SQL 语句片段 -->
            one.id            one_id, <!-- SQL 语句片段 -->
            one.pname         one_pname, <!-- SQL 语句片段 -->
            one.parentid      one_parentid, <!-- SQL 语句片段 -->
            one.grade         one_grade, <!-- SQL 语句片段 -->
            one.media_type    one_mediaType, <!-- SQL 语句片段 -->
            one.start_time    one_stratTime, <!-- SQL 语句片段 -->
            one.end_time      one_endTime, <!-- SQL 语句片段 -->
            one.orderby       one_orderby, <!-- SQL 语句片段 -->
            one.course_id     one_courseId, <!-- SQL 语句片段 -->
            one.course_pub_id one_coursePubId, <!-- SQL 语句片段 -->
            two.id            two_id, <!-- SQL 语句片段 -->
            two.pname         two_pname, <!-- SQL 语句片段 -->
            two.parentid      two_parentid, <!-- SQL 语句片段 -->
            two.grade         two_grade, <!-- SQL 语句片段 -->
            two.media_type    two_mediaType, <!-- SQL 语句片段 -->
            two.start_time    two_stratTime, <!-- SQL 语句片段 -->
            two.end_time      two_endTime, <!-- SQL 语句片段 -->
            two.orderby       two_orderby, <!-- SQL 语句片段 -->
            two.course_id     two_courseId, <!-- SQL 语句片段 -->
            two.course_pub_id two_coursePubId, <!-- SQL 语句片段 -->
            m1.media_fileName mediaFilename, <!-- SQL 语句片段 -->
            m1.id             teachplanMeidaId, <!-- SQL 语句片段 -->
            m1.media_id       mediaId <!-- SQL 语句片段 -->
        from teachplan one <!-- SQL 语句片段 -->
                 left join teachplan two on two.parentid = one.id <!-- SQL 语句片段 -->
                 left join teachplan_media m1 on two.id = m1.teachplan_id <!-- SQL 语句片段 -->
        where one.parentid = 0 <!-- SQL 语句片段 -->
          and one.course_id = #{id} <!-- 占位符参数 -->
        order by one.orderby,two.orderby <!-- SQL 语句片段 -->

    </select> <!-- SQL 语句片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/TeachplanMediaMapper.java

package com.xuecheng.content.mapper; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.mapper.BaseMapper; // 导入 BaseMapper 类
import com.xuecheng.content.model.po.TeachplanMedia; // 导入 TeachplanMedia 类

/** // 保留原作者写的块注释
 * <p> // 保留原作者写的块注释
 *  Mapper 接口 // 保留原作者写的块注释
 * </p> // 保留原作者写的块注释
 * // 保留原作者写的块注释
 * @author itcast // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface TeachplanMediaMapper extends BaseMapper<TeachplanMedia> { // 定义 interface TeachplanMediaMapper

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/mapper/TeachplanMediaMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- SQL 语句片段 -->
<mapper namespace="com.xuecheng.content.mapper.TeachplanMediaMapper"> <!-- MyBatis 映射文件的根标签 -->

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.TeachplanMedia"> <!-- 定义结果映射 -->
        <id column="id" property="id" /> <!-- 主键列的映射规则 -->
        <result column="media_id" property="mediaId" /> <!-- 字段与属性的映射规则 -->
        <result column="teachplan_id" property="teachplanId" /> <!-- 字段与属性的映射规则 -->
        <result column="course_id" property="courseId" /> <!-- 字段与属性的映射规则 -->
        <result column="media_fileName" property="mediaFilename" /> <!-- 字段与属性的映射规则 -->
        <result column="create_date" property="createDate" /> <!-- 字段与属性的映射规则 -->
        <result column="create_people" property="createPeople" /> <!-- 字段与属性的映射规则 -->
        <result column="change_people" property="changePeople" /> <!-- 字段与属性的映射规则 -->
    </resultMap> <!-- 结束 resultMap 区块 -->

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List"> <!-- 声明可复用的 SQL 片段 -->
        id, media_id, teachplan_id, course_id, media_fileName, create_date, create_people, change_people <!-- SQL 语句片段 -->
    </sql> <!-- 结束 SQL 片段 -->

</mapper> <!-- 结束 mapper 标签 -->

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/CourseBaseInfoService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.model.dto.AddCourseDto; // 导入 AddCourseDto 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.EditCourseDto; // 导入 EditCourseDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程信息管理接口 // 保留原作者写的块注释
 * @date 2023/2/12 10:14 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseBaseInfoService { // 定义 interface CourseBaseInfoService

    /** // 保留原作者写的块注释
     * 课程分页查询 // 保留原作者写的块注释
     * @param pageParams 分页查询参数 // 保留原作者写的块注释
     * @param courseParamsDto 查询条件 // 保留原作者写的块注释
     * @param companyId 培训机构id // 保留原作者写的块注释
     * @return 查询结果 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public PageResult<CourseBase> queryCourseBaseList(Long companyId,PageParams pageParams, QueryCourseParamsDto courseParamsDto); // 执行语句

    /** // 保留原作者写的块注释
     * 新增课程 // 保留原作者写的块注释
     * @param companyId 机构id // 保留原作者写的块注释
     * @param addCourseDto 课程信息 // 保留原作者写的块注释
     * @return 课程详细信息 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CourseBaseInfoDto createCourseBase(Long companyId,AddCourseDto addCourseDto); // 执行语句

    /** // 保留原作者写的块注释
     * 根据课程id查询课程信息 // 保留原作者写的块注释
     * @param courseId 课程id // 保留原作者写的块注释
     * @return 课程详细信息 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CourseBaseInfoDto getCourseBaseInfo(Long courseId); // 执行语句

    /** // 保留原作者写的块注释
     * 修改课程 // 保留原作者写的块注释
     * @param companyId 机构id // 保留原作者写的块注释
     * @param editCourseDto 修改课程信息 // 保留原作者写的块注释
     * @return 课程详细信息 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CourseBaseInfoDto updateCourseBase(Long companyId,EditCourseDto editCourseDto); // 执行语句

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/CourseCategoryService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 14:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CourseCategoryService { // 定义 interface CourseCategoryService
 /** // 保留原作者写的块注释
  * 课程分类树形结构查询 // 保留原作者写的块注释
  * // 保留原作者写的块注释
  * @return // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public List<CourseCategoryTreeDto> queryTreeNodes(String id); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/CoursePublishService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类

import java.io.File; // 导入 File 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布相关的接口 // 保留原作者写的块注释
 * @date 2023/2/21 10:03 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface CoursePublishService { // 定义 interface CoursePublishService


 /** // 保留原作者写的块注释
  * @description 获取课程预览信息 // 保留原作者写的块注释
  * @param courseId 课程id // 保留原作者写的块注释
  * @return com.xuecheng.content.model.dto.CoursePreviewDto // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/16 15:36 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 // service接口层只关注业务 // 保留原作者写的行内注释
 // controller层和service层的联系是controller层调用service实现类 // 保留原作者写的行内注释
 public CoursePreviewDto getCoursePreviewInfo(Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * @description 提交审核 // 保留原作者写的块注释
  * @param courseId  课程id // 保留原作者写的块注释
  * @return void // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/18 10:31 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void commitAudit(Long companyId,Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * @description 课程发布接口 // 保留原作者写的块注释
  * @param companyId 机构id // 保留原作者写的块注释
  * @param courseId 课程id // 保留原作者写的块注释
  * @return void // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/20 16:23 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void publish(Long companyId,Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * @description 课程静态化 // 保留原作者写的块注释
  * @param courseId  课程id // 保留原作者写的块注释
  * @return File 静态化文件 // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/23 16:59 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public File generateCourseHtml(Long courseId); // 执行语句
 /** // 保留原作者写的块注释
  * @description 上传课程静态化页面 // 保留原作者写的块注释
  * @param file  静态化文件 // 保留原作者写的块注释
  * @return void // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/23 16:59 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void  uploadCourseHtml(Long courseId, File file); // 执行语句

 /** // 保留原作者写的块注释
  * 根据课程 id查询课程发布信息 // 保留原作者写的块注释
  * @param courseId // 保留原作者写的块注释
  * @return // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public CoursePublish getCoursePublish(Long courseId); // 执行语句
 public CoursePublish getCoursePublishCache(Long courseId); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/impl/CourseBaseInfoServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.mapper.CourseMarketMapper; // 导入 CourseMarketMapper 类
import com.xuecheng.content.model.dto.AddCourseDto; // 导入 AddCourseDto 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.EditCourseDto; // 导入 EditCourseDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.model.po.CourseCategory; // 导入 CourseCategory 类
import com.xuecheng.content.model.po.CourseMarket; // 导入 CourseMarket 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Service; // 导入 Service 类
import org.springframework.transaction.annotation.Transactional; // 导入 Transactional 类

import javax.annotation.Resources; // 导入 Resources 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类
import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 10:16 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Service // 使用注解 @Service
public class CourseBaseInfoServiceImpl implements CourseBaseInfoService { // 定义 class CourseBaseInfoServiceImpl
    @Autowired // 使用注解 @Autowired
    CourseBaseMapper courseBaseMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseMarketMapper courseMarketMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseCategoryMapper courseCategoryMapper; // 执行语句


    @Override // 使用注解 @Override
    public PageResult<CourseBase> queryCourseBaseList(Long companyId,PageParams pageParams, QueryCourseParamsDto courseParamsDto) { // 定义方法 queryCourseBaseList(...)

        //拼装查询条件 // 保留原作者写的行内注释
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>(); // 创建 LambdaQueryWrapper<> 对象并赋值给 queryWrapper
        //根据名称模糊查询,在sql中拼接 course_base.name like '%值%' // 保留原作者写的行内注释
        queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName()),CourseBase::getName,courseParamsDto.getCourseName()); // 执行语句
        //根据课程审核状态查询 course_base.audit_status = ? // 保留原作者写的行内注释
        queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus()), CourseBase::getAuditStatus,courseParamsDto.getAuditStatus()); // 执行语句
        //todo:按课程发布状态查询 // 保留原作者写的行内注释
        //根据培训机构id拼装查询条件 // 保留原作者写的行内注释
        queryWrapper.eq(CourseBase::getCompanyId,companyId); // 执行语句

        //创建page分页参数对象,参数:当前页码,每页记录数 // 保留原作者写的行内注释
        Page<CourseBase> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize()); // 创建 Page<> 对象并赋值给 page
        //开始进行分页查询 // 保留原作者写的行内注释
        Page<CourseBase> pageResult = courseBaseMapper.selectPage(page, queryWrapper); // 把 courseBaseMapper.selectPage(page, queryWrapper) 赋值给 pageResult
        //数据列表 // 保留原作者写的行内注释
        List<CourseBase> items = pageResult.getRecords(); // 把 pageResult.getRecords() 的返回值赋给 items
        //总记录数 // 保留原作者写的行内注释
        long total = pageResult.getTotal(); // 把 pageResult.getTotal() 的返回值赋给 total

        //List<T> items, long counts, long page, long pageSize // 保留原作者写的行内注释
        PageResult<CourseBase> courseBasePageResult = new PageResult<CourseBase>(items,total,pageParams.getPageNo(), pageParams.getPageSize()); // 创建 PageResult<CourseBase> 对象并赋值给 courseBasePageResult
        return  courseBasePageResult; // 返回  courseBasePageResult
    } // 代码块结束

    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto dto){ // 定义方法 createCourseBase(...)

        //参数的合法性校验 // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getName())) { // 保留原作者写的行内注释
////            throw new RuntimeException("课程名称为空"); // 保留原作者写的行内注释
//            XueChengPlusException.cast("课程名称为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getMt())) { // 保留原作者写的行内注释
//            throw new RuntimeException("课程分类为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getSt())) { // 保留原作者写的行内注释
//            throw new RuntimeException("课程分类为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getGrade())) { // 保留原作者写的行内注释
//            throw new RuntimeException("课程等级为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getTeachmode())) { // 保留原作者写的行内注释
//            throw new RuntimeException("教育模式为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getUsers())) { // 保留原作者写的行内注释
//            throw new RuntimeException("适应人群为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        if (StringUtils.isBlank(dto.getCharge())) { // 保留原作者写的行内注释
//            throw new RuntimeException("收费规则为空"); // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释

        //向课程基本信息表course_base写入数据 // 保留原作者写的行内注释
        CourseBase courseBaseNew = new CourseBase(); // 创建 CourseBase 对象并赋值给 courseBaseNew
        //将传入的页面的参数放到courseBaseNew对象 // 保留原作者写的行内注释
//        courseBaseNew.setName(dto.getName()); // 保留原作者写的行内注释
//        courseBaseNew.setDescription(dto.getDescription()); // 保留原作者写的行内注释
        //上边的从原始对象中get拿数据向新对象set,比较复杂 // 保留原作者写的行内注释
        BeanUtils.copyProperties(dto,courseBaseNew);//只要属性名称一致就可以拷贝 // 执行语句
        courseBaseNew.setCompanyId(companyId); // 执行语句
        courseBaseNew.setCreateDate(LocalDateTime.now()); // 执行语句
        //审核状态默认为未提交 // 保留原作者写的行内注释
        courseBaseNew.setAuditStatus("202002"); // 执行语句
        //发布状态为未发布 // 保留原作者写的行内注释
        courseBaseNew.setStatus("203001"); // 执行语句
        //插入数据库 // 保留原作者写的行内注释
        int insert = courseBaseMapper.insert(courseBaseNew); // 把 courseBaseMapper.insert(courseBaseNew) 赋值给 insert
        if(insert<=0){ // 定义方法 方法(...)
            throw new RuntimeException("添加课程失败"); // 主动抛出异常
        } // 代码块结束

        //向课程营销系courese_market写入数据 // 保留原作者写的行内注释
        CourseMarket courseMarketNew = new CourseMarket(); // 创建 CourseMarket 对象并赋值给 courseMarketNew
        //将页面输入的数据拷贝到courseMarketNew // 保留原作者写的行内注释
        BeanUtils.copyProperties(dto,courseMarketNew); // 执行语句
        //课程的id // 保留原作者写的行内注释
        Long courseId = courseBaseNew.getId(); // 把 courseBaseNew.getId() 的返回值赋给 courseId
        courseMarketNew.setId(courseId); // 执行语句
        //保存营销信息 // 保留原作者写的行内注释
        saveCourseMarket(courseMarketNew); // 执行语句
        //从数据库查询课程的详细信息,包括两部分 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfo = getCourseBaseInfo(courseId); // 把 getCourseBaseInfo(courseId) 赋值给 courseBaseInfo


        return courseBaseInfo; // 返回 courseBaseInfo
    } // 代码块结束

    //查询课程信息 // 保留原作者写的行内注释
    public CourseBaseInfoDto getCourseBaseInfo(Long courseId){ // 定义方法 getCourseBaseInfo(...)

        //从课程基本信息表查询 // 保留原作者写的行内注释
        CourseBase courseBase = courseBaseMapper.selectById(courseId); // 把 courseBaseMapper.selectById(courseId) 赋值给 courseBase
        if(courseBase==null){ // 定义方法 方法(...)
            return null; // 返回 null
        } // 代码块结束
        //从课程营销表查询 // 保留原作者写的行内注释
        CourseMarket courseMarket = courseMarketMapper.selectById(courseId); // 把 courseMarketMapper.selectById(courseId) 赋值给 courseMarket

        //组装在一起 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto(); // 创建 CourseBaseInfoDto 对象并赋值给 courseBaseInfoDto
        BeanUtils.copyProperties(courseBase,courseBaseInfoDto); // 执行语句
        if(courseMarket!=null){ // 定义方法 方法(...)
            BeanUtils.copyProperties(courseMarket,courseBaseInfoDto); // 执行语句
        } // 代码块结束

        //通过courseCategoryMapper查询分类信息,将分类名称放在courseBaseInfoDto对象 // 保留原作者写的行内注释
        CourseCategory mtObj = courseCategoryMapper.selectById(courseBase.getMt()); // 把 courseCategoryMapper.selectById(courseBase.getMt()) 赋值给 mtObj
        String mtName = mtObj.getName();//大分类名称 // 把 mtObj.getName() 的返回值赋给 mtName
        courseBaseInfoDto.setMtName(mtName); // 执行语句
        CourseCategory stObj = courseCategoryMapper.selectById(courseBase.getSt()); // 把 courseCategoryMapper.selectById(courseBase.getSt()) 赋值给 stObj
        String stName = stObj.getName();//小分类名称 // 把 stObj.getName() 的返回值赋给 stName
        courseBaseInfoDto.setStName(stName); // 执行语句

        return courseBaseInfoDto; // 返回 courseBaseInfoDto

    } // 代码块结束

    @Override // 使用注解 @Override
    public CourseBaseInfoDto updateCourseBase(Long companyId, EditCourseDto editCourseDto) { // 定义方法 updateCourseBase(...)

        //拿到课程id // 保留原作者写的行内注释
        Long courseId = editCourseDto.getId(); // 把 editCourseDto.getId() 的返回值赋给 courseId
        //查询课程信息 // 保留原作者写的行内注释
        CourseBase courseBase = courseBaseMapper.selectById(courseId); // 把 courseBaseMapper.selectById(courseId) 赋值给 courseBase
        if(courseBase == null){ // 定义方法 方法(...)
            XueChengPlusException.cast("课程不存在"); // 执行语句
        } // 代码块结束

        //数据合法性校验 // 保留原作者写的行内注释
        //根据具体的业务逻辑去校验 // 保留原作者写的行内注释
        //本机构只能修改本机构的课程 // 保留原作者写的行内注释
        if(!companyId.equals(courseBase.getCompanyId())){ // 定义方法 方法(...)
            XueChengPlusException.cast("本机构只能修改本机构的课程"); // 执行语句
        } // 代码块结束

        //封装数据 // 保留原作者写的行内注释
        BeanUtils.copyProperties(editCourseDto,courseBase); // 执行语句
        //修改时间 // 保留原作者写的行内注释
        courseBase.setChangeDate(LocalDateTime.now()); // 执行语句

        //更新数据库 // 保留原作者写的行内注释
        int i = courseBaseMapper.updateById(courseBase); // 把 courseBaseMapper.updateById(courseBase) 赋值给 i
        if(i<=0){ // 定义方法 方法(...)
            XueChengPlusException.cast("修改课程失败"); // 执行语句
        } // 代码块结束
        //更新营销信息 // 保留原作者写的行内注释
        CourseMarket courseMarket = new CourseMarket(); // 创建 CourseMarket 对象并赋值给 courseMarket
        BeanUtils.copyProperties(editCourseDto,courseMarket); // 执行语句

        saveCourseMarket(courseMarket); // 执行语句
        //查询课程信息 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfo = getCourseBaseInfo(courseId); // 把 getCourseBaseInfo(courseId) 赋值给 courseBaseInfo

        return courseBaseInfo; // 返回 courseBaseInfo
    } // 代码块结束

    //单独写一个方法保存营销信息,逻辑:存在则更新,不存在则添加 // 保留原作者写的行内注释
    private int saveCourseMarket(CourseMarket courseMarketNew){ // 定义方法 saveCourseMarket(...)

        //参数的合法性校验 // 保留原作者写的行内注释
        String charge = courseMarketNew.getCharge(); // 把 courseMarketNew.getCharge() 的返回值赋给 charge
        if(StringUtils.isEmpty(charge)){ // 定义方法 方法(...)
            throw new RuntimeException("收费规则为空"); // 主动抛出异常
        } // 代码块结束
        //如果课程收费,价格没有填写也需要抛出异常 // 保留原作者写的行内注释
        if(charge.equals("201001")){ // 定义方法 方法(...)
           if(courseMarketNew.getPrice() ==null || courseMarketNew.getPrice().floatValue()<=0){ // 定义方法 方法(...)
//               throw new RuntimeException("课程的价格不能为空并且必须大于0"); // 保留原作者写的行内注释
               XueChengPlusException.cast("课程的价格不能为空并且必须大于0"); // 执行语句
           } // 代码块结束
        } // 代码块结束

        //从数据库查询营销信息,存在则更新,不存在则添加 // 保留原作者写的行内注释
        Long id = courseMarketNew.getId();//主键 // 把 courseMarketNew.getId() 的返回值赋给 id
        CourseMarket courseMarket = courseMarketMapper.selectById(id); // 把 courseMarketMapper.selectById(id) 赋值给 courseMarket
        if(courseMarket == null){ // 定义方法 方法(...)
            //插入数据库 // 保留原作者写的行内注释
            int insert = courseMarketMapper.insert(courseMarketNew); // 把 courseMarketMapper.insert(courseMarketNew) 赋值给 insert
            return insert; // 返回 insert
        }else{ // 代码逻辑
            //将courseMarketNew拷贝到courseMarket // 保留原作者写的行内注释
            BeanUtils.copyProperties(courseMarketNew,courseMarket); // 执行语句
            courseMarket.setId(courseMarketNew.getId()); // 执行语句
            //更新 // 保留原作者写的行内注释
            int i = courseMarketMapper.updateById(courseMarket); // 把 courseMarketMapper.updateById(courseMarket) 赋值给 i
            return i; // 返回 i
        } // 代码块结束


    } // 代码块结束



} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/impl/CourseCategoryServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.service.CourseCategoryService; // 导入 CourseCategoryService 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Service; // 导入 Service 类

import java.util.ArrayList; // 导入 ArrayList 类
import java.util.List; // 导入 List 类
import java.util.Map; // 导入 Map 类
import java.util.stream.Collectors; // 导入 Collectors 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 14:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Service // 使用注解 @Service
public class CourseCategoryServiceImpl implements CourseCategoryService { // 定义 class CourseCategoryServiceImpl

    @Autowired // 使用注解 @Autowired
    CourseCategoryMapper courseCategoryMapper; // 执行语句

    @Override // 使用注解 @Override
    public List<CourseCategoryTreeDto> queryTreeNodes(String id) { // 定义方法 queryTreeNodes(...)
        //调用mapper递归查询出分类信息 // 保留原作者写的行内注释
        List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes(id); // 把 courseCategoryMapper.selectTreeNodes(id) 赋值给 courseCategoryTreeDtos

        //找到每个节点的子节点,最终封装成List<CourseCategoryTreeDto> // 保留原作者写的行内注释
        //先将list转成map,key就是结点的id,value就是CourseCategoryTreeDto对象,目的就是为了方便从map获取结点,filter(item->!id.equals(item.getId()))把根结点排除 // 保留原作者写的行内注释
        Map<String, CourseCategoryTreeDto> mapTemp = courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2)); // 把 courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2)) 赋值给 mapTemp
        //定义一个list作为最终返回的list // 保留原作者写的行内注释
        List<CourseCategoryTreeDto> courseCategoryList = new ArrayList<>(); // 创建 ArrayList<> 对象并赋值给 courseCategoryList
        //从头遍历 List<CourseCategoryTreeDto> ,一边遍历一边找子节点放在父节点的childrenTreeNodes // 保留原作者写的行内注释
        courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).forEach(item -> { // 定义方法 方法(...)
            if (item.getParentid().equals(id)) { // 定义方法 方法(...)
                courseCategoryList.add(item); // 执行语句
            } // 代码块结束
            //找到节点的父节点 // 保留原作者写的行内注释
            CourseCategoryTreeDto courseCategoryParent = mapTemp.get(item.getParentid()); // 把 mapTemp.get(item.getParentid()) 赋值给 courseCategoryParent
            if(courseCategoryParent!=null){ // 定义方法 方法(...)
                if(courseCategoryParent.getChildrenTreeNodes()==null){ // 定义方法 方法(...)
                    //如果该父节点的ChildrenTreeNodes属性为空要new一个集合,因为要向该集合中放它的子节点 // 保留原作者写的行内注释
                    courseCategoryParent.setChildrenTreeNodes(new ArrayList<CourseCategoryTreeDto>()); // 执行语句
                } // 代码块结束
                //到每个节点的子节点放在父节点的childrenTreeNodes属性中 // 保留原作者写的行内注释
                courseCategoryParent.getChildrenTreeNodes().add(item); // 执行语句
            } // 代码块结束



        }); // 执行语句

        return courseCategoryList; // 返回 courseCategoryList
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/impl/CoursePublishServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.alibaba.fastjson.JSON; // 导入 JSON 类
import com.xuecheng.base.exception.CommonError; // 导入 CommonError 类
import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.content.config.MultipartSupportConfig; // 导入 MultipartSupportConfig 类
import com.xuecheng.content.feignclient.MediaServiceClient; // 导入 MediaServiceClient 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.mapper.CourseMarketMapper; // 导入 CourseMarketMapper 类
import com.xuecheng.content.mapper.CoursePublishMapper; // 导入 CoursePublishMapper 类
import com.xuecheng.content.mapper.CoursePublishPreMapper; // 导入 CoursePublishPreMapper 类
import com.xuecheng.content.model.dto.CourseBaseInfoDto; // 导入 CourseBaseInfoDto 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.model.po.CourseMarket; // 导入 CourseMarket 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类
import com.xuecheng.content.model.po.CoursePublishPre; // 导入 CoursePublishPre 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import com.xuecheng.content.service.TeachplanService; // 导入 TeachplanService 类
import com.xuecheng.messagesdk.model.po.MqMessage; // 导入 MqMessage 类
import com.xuecheng.messagesdk.service.MqMessageService; // 导入 MqMessageService 类
import freemarker.cache.ClassTemplateLoader; // 导入 ClassTemplateLoader 类
import freemarker.template.Configuration; // 导入 Configuration 类
import freemarker.template.Template; // 导入 Template 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.apache.commons.io.IOUtils; // 导入 IOUtils 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.redisson.api.RLock; // 导入 RLock 类
import org.redisson.api.RedissonClient; // 导入 RedissonClient 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.data.redis.core.RedisTemplate; // 导入 RedisTemplate 类
import org.springframework.stereotype.Service; // 导入 Service 类
import org.springframework.transaction.annotation.Transactional; // 导入 Transactional 类
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; // 导入 FreeMarkerTemplateUtils 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.File; // 导入 File 类
import java.io.FileOutputStream; // 导入 FileOutputStream 类
import java.io.InputStream; // 导入 InputStream 类
import java.time.LocalDateTime; // 导入 LocalDateTime 类
import java.util.HashMap; // 导入 HashMap 类
import java.util.List; // 导入 List 类
import java.util.Random; // 导入 Random 类
import java.util.concurrent.TimeUnit; // 导入 TimeUnit 类
import java.util.concurrent.locks.ReentrantLock; // 导入 ReentrantLock 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布相关接口实现 // 保留原作者写的块注释
 * @date 2023/2/21 10:04 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Service // 使用注解 @Service
public class CoursePublishServiceImpl implements CoursePublishService { // 定义 class CoursePublishServiceImpl

    @Autowired // 使用注解 @Autowired
    CourseBaseInfoService courseBaseInfoService; // 执行语句

    @Autowired // 使用注解 @Autowired
    TeachplanService teachplanService; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseBaseMapper courseBaseMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CourseMarketMapper courseMarketMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    CoursePublishPreMapper coursePublishPreMapper; // 执行语句
    @Autowired // 使用注解 @Autowired
    CoursePublishMapper coursePublishMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    MqMessageService mqMessageService; // 执行语句
    @Autowired // 使用注解 @Autowired
    MediaServiceClient mediaServiceClient; // 执行语句

    @Autowired // 使用注解 @Autowired
    RedisTemplate redisTemplate; // 执行语句

    @Autowired // 使用注解 @Autowired
    RedissonClient redissonClient; // 执行语句

    // 课程预览 // 保留原作者写的行内注释
    @Override // 使用注解 @Override
    public CoursePreviewDto getCoursePreviewInfo(Long courseId) { // 定义方法 getCoursePreviewInfo(...)
        CoursePreviewDto coursePreviewDto = new CoursePreviewDto(); // 创建 CoursePreviewDto 对象并赋值给 coursePreviewDto
        //课程基本信息,营销信息 // 保留原作者写的行内注释
        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId); // 把 courseBaseInfoService.getCourseBaseInfo(courseId) 赋值给 courseBaseInfo
        coursePreviewDto.setCourseBase(courseBaseInfo); // 执行语句
        //课程计划信息 // 保留原作者写的行内注释
        List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId); // 把 teachplanService.findTeachplanTree(courseId) 赋值给 teachplanTree
        coursePreviewDto.setTeachplans(teachplanTree); // 执行语句

        return coursePreviewDto; // 返回 coursePreviewDto
    } // 代码块结束

    // 提交审核 // 保留原作者写的行内注释
    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public void commitAudit(Long companyId, Long courseId) { // 定义方法 commitAudit(...)

        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId); // 把 courseBaseInfoService.getCourseBaseInfo(courseId) 赋值给 courseBaseInfo
        if (courseBaseInfo == null) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程找不到"); // 执行语句
        } // 代码块结束
        //审核状态 // 保留原作者写的行内注释
        String auditStatus = courseBaseInfo.getAuditStatus(); // 把 courseBaseInfo.getAuditStatus() 的返回值赋给 auditStatus

        //如果课程的审核状态为已提交则不允许提交 // 保留原作者写的行内注释
        if (auditStatus.equals("202003")) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程已提交请等待审核"); // 执行语句
        } // 代码块结束
        //本机构只能提交本机构的课程 // 保留原作者写的行内注释
        //todo:本机构只能提交本机构的课程 // 保留原作者写的行内注释

        //课程的图片、计划信息没有填写也不允许提交 // 保留原作者写的行内注释
        String pic = courseBaseInfo.getPic(); // 把 courseBaseInfo.getPic() 的返回值赋给 pic
        if (StringUtils.isEmpty(pic)) { // 定义方法 方法(...)
            XueChengPlusException.cast("请求上传课程图片"); // 执行语句
        } // 代码块结束
        //查询课程计划 // 保留原作者写的行内注释
        //课程计划信息 // 保留原作者写的行内注释
        List<TeachplanDto> teachplanTree = teachplanService.findTeachplanTree(courseId); // 把 teachplanService.findTeachplanTree(courseId) 赋值给 teachplanTree
        if (teachplanTree == null || teachplanTree.size() == 0) { // 定义方法 方法(...)
            XueChengPlusException.cast("请编写课程计划"); // 执行语句
        } // 代码块结束

        //查询到课程基本信息、营销信息、计划等信息插入到课程预发布表 // 保留原作者写的行内注释
        CoursePublishPre coursePublishPre = new CoursePublishPre(); // 创建 CoursePublishPre 对象并赋值给 coursePublishPre
        BeanUtils.copyProperties(courseBaseInfo, coursePublishPre); // 执行语句
        //设置机构id // 保留原作者写的行内注释
        coursePublishPre.setCompanyId(companyId); // 执行语句
        //营销信息 // 保留原作者写的行内注释
        CourseMarket courseMarket = courseMarketMapper.selectById(courseId); // 把 courseMarketMapper.selectById(courseId) 赋值给 courseMarket
        //转json // 保留原作者写的行内注释
        String courseMarketJson = JSON.toJSONString(courseMarket); // 把 JSON.toJSONString(courseMarket) 赋值给 courseMarketJson
        coursePublishPre.setMarket(courseMarketJson); // 执行语句
        //计划信息 // 保留原作者写的行内注释
        //转json // 保留原作者写的行内注释
        String teachplanTreeJson = JSON.toJSONString(teachplanTree); // 把 JSON.toJSONString(teachplanTree) 赋值给 teachplanTreeJson
        coursePublishPre.setTeachplan(teachplanTreeJson); // 执行语句
        //状态为已提交 // 保留原作者写的行内注释
        coursePublishPre.setStatus("202003"); // 执行语句
        //提交时间 // 保留原作者写的行内注释
        coursePublishPre.setCreateDate(LocalDateTime.now()); // 执行语句
        //查询预发布表,如果有记录则更新,没有则插入 // 保留原作者写的行内注释
        CoursePublishPre coursePublishPreObj = coursePublishPreMapper.selectById(courseId); // 把 coursePublishPreMapper.selectById(courseId) 赋值给 coursePublishPreObj
        if (coursePublishPreObj == null) { // 定义方法 方法(...)
            //插入 // 保留原作者写的行内注释
            coursePublishPreMapper.insert(coursePublishPre); // 执行语句
        } else { // 代码逻辑
            //更新 // 保留原作者写的行内注释
            coursePublishPreMapper.updateById(coursePublishPre); // 执行语句
        } // 代码块结束

        //更新课程基本信息表的审核状态为已提交 // 保留原作者写的行内注释
        CourseBase courseBase = courseBaseMapper.selectById(courseId); // 把 courseBaseMapper.selectById(courseId) 赋值给 courseBase
        courseBase.setAuditStatus("202003");//审核状态为已提交 // 执行语句

        courseBaseMapper.updateById(courseBase); // 执行语句
    } // 代码块结束

    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public void publish(Long companyId, Long courseId) { // 定义方法 publish(...)

        //查询预发布表 // 保留原作者写的行内注释
        CoursePublishPre coursePublishPre = coursePublishPreMapper.selectById(courseId); // 把 coursePublishPreMapper.selectById(courseId) 赋值给 coursePublishPre
        if (coursePublishPre == null) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程没有审核记录,无法发布"); // 执行语句
        } // 代码块结束
        //状态 // 保留原作者写的行内注释
        String status = coursePublishPre.getStatus(); // 把 coursePublishPre.getStatus() 的返回值赋给 status
        //课程如果没有审核通过不允许发布 // 保留原作者写的行内注释
        if (!status.equals("202004")) { // 定义方法 方法(...)
            XueChengPlusException.cast("课程没有审核通过不允许发布"); // 执行语句
        } // 代码块结束

        //向课程发布表写入数据 // 保留原作者写的行内注释
        CoursePublish coursePublish = new CoursePublish(); // 创建 CoursePublish 对象并赋值给 coursePublish
        BeanUtils.copyProperties(coursePublishPre, coursePublish); // 执行语句
        //先查询课程发布,如果有则更新,没有再添加 // 保留原作者写的行内注释
        CoursePublish coursePublishObj = coursePublishMapper.selectById(courseId); // 把 coursePublishMapper.selectById(courseId) 赋值给 coursePublishObj
        if (coursePublishObj == null) { // 定义方法 方法(...)
            coursePublishMapper.insert(coursePublish); // 执行语句
        } else { // 代码逻辑
            coursePublishMapper.updateById(coursePublish); // 执行语句
        } // 代码块结束

        //向消息表写入数据 // 保留原作者写的行内注释
//        mqMessageService.addMessage("course_publish",String.valueOf(courseId),null,null); // 保留原作者写的行内注释
        saveCoursePublishMessage(courseId); // 执行语句

        //将预发布表数据删除 // 保留原作者写的行内注释
        coursePublishPreMapper.deleteById(courseId); // 执行语句

    } // 代码块结束

    @Override // 使用注解 @Override
    public File generateCourseHtml(Long courseId) { // 定义方法 generateCourseHtml(...)

        Configuration configuration = new Configuration(Configuration.getVersion()); // 创建 Configuration 对象并赋值给 configuration
        //最终的静态文件 // 保留原作者写的行内注释
        File htmlFile = null; // 把变量 htmlFile 初始化为 null
        try { // 尝试执行可能抛出异常的代码
            //拿到classpath路径 // 保留原作者写的行内注释
//            String classpath = this.getClass().getResource("/").getPath(); // 保留原作者写的行内注释
//            //指定模板的目录 // 保留原作者写的行内注释
//            configuration.setDirectoryForTemplateLoading(new File(classpath+"/templates/")); // 保留原作者写的行内注释
            //更改为如下方式 // 保留原作者写的行内注释
            configuration.setTemplateLoader(new ClassTemplateLoader(this.getClass().getClassLoader(), "/templates")); // 执行语句
            //指定编码 // 保留原作者写的行内注释
            configuration.setDefaultEncoding("utf-8"); // 执行语句

            //得到模板 // 保留原作者写的行内注释
            Template template = configuration.getTemplate("course_template.ftl"); // 把 configuration.getTemplate("course_template.ftl") 赋值给 template
            //准备数据 // 保留原作者写的行内注释
            CoursePreviewDto coursePreviewInfo = this.getCoursePreviewInfo(courseId); // 把 this.getCoursePreviewInfo(courseId) 赋值给 coursePreviewInfo
            HashMap<String, Object> map = new HashMap<>(); // 创建 HashMap<> 对象并赋值给 map
            map.put("model", coursePreviewInfo); // 执行语句

            //Template template 模板, Object model 数据 // 保留原作者写的行内注释
            String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); // 把 FreeMarkerTemplateUtils.processTemplateIntoString(template, map) 赋值给 html
            //输入流 // 保留原作者写的行内注释
            InputStream inputStream = IOUtils.toInputStream(html, "utf-8"); // 把 IOUtils.toInputStream(html, "utf-8") 赋值给 inputStream
            htmlFile = File.createTempFile("coursepublish", ".html"); // 把 File.createTempFile("coursepublish", ".html") 赋值给 htmlFile
            //输出文件 // 保留原作者写的行内注释
            FileOutputStream outputStream = new FileOutputStream(htmlFile); // 创建 FileOutputStream 对象并赋值给 outputStream
            //使用流将html写入文件 // 保留原作者写的行内注释
            IOUtils.copy(inputStream, outputStream); // 执行语句
        } catch (Exception ex) { // 定义方法 catch(...)
            log.error("页面静态化出现问题,课程id:{}", courseId, ex); // 执行语句
            ex.printStackTrace(); // 执行语句
        } // 代码块结束

        return htmlFile; // 返回 htmlFile
    } // 代码块结束

    @Override // 使用注解 @Override
    public void uploadCourseHtml(Long courseId, File file) { // 定义方法 uploadCourseHtml(...)
        try { // 尝试执行可能抛出异常的代码
            //将file转成MultipartFile // 保留原作者写的行内注释
            MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(file); // 把 MultipartSupportConfig.getMultipartFile(file) 赋值给 multipartFile
            //远程调用得到返回值 // 保留原作者写的行内注释
            String upload = mediaServiceClient.upload(multipartFile, "course/" + courseId + ".html"); // 把 mediaServiceClient.upload(multipartFile, "course/" + courseId + ".html") 赋值给 upload
            if (upload == null) { // 定义方法 方法(...)
                log.debug("远程调用走降级逻辑得到上传的结果为null,课程id:{}", courseId); // 执行语句
                XueChengPlusException.cast("上传静态文件过程中存在异常"); // 执行语句
            } // 代码块结束
        } catch (Exception ex) { // 定义方法 catch(...)
            ex.printStackTrace(); // 执行语句
            XueChengPlusException.cast("上传静态文件过程中存在异常"); // 执行语句
        } // 代码块结束

    } // 代码块结束

    /** // 保留原作者写的块注释
     * @param courseId 课程id // 保留原作者写的块注释
     * @return void // 保留原作者写的块注释
     * @description 保存消息表记录 // 保留原作者写的块注释
     * @author Mr.M // 保留原作者写的块注释
     * @date 2022/9/20 16:32 // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    private void saveCoursePublishMessage(Long courseId) { // 定义方法 saveCoursePublishMessage(...)

        MqMessage mqMessage = mqMessageService.addMessage("course_publish", String.valueOf(courseId), null, null); // 把 mqMessageService.addMessage("course_publish", String.valueOf(courseId), null, null) 赋值给 mqMessage
        if (mqMessage == null) { // 定义方法 方法(...)
            XueChengPlusException.cast(CommonError.UNKOWN_ERROR); // 执行语句
        } // 代码块结束

    } // 代码块结束

    /** // 保留原作者写的块注释
     * 根据课程 id查询课程发布信息 // 保留原作者写的块注释
     * // 保留原作者写的块注释
     * @param courseId // 保留原作者写的块注释
     * @return // 保留原作者写的块注释
     */ // 保留原作者写的块注释
    public CoursePublish getCoursePublish(Long courseId) { // 定义方法 getCoursePublish(...)
        CoursePublish coursePublish = coursePublishMapper.selectById(courseId); // 把 coursePublishMapper.selectById(courseId) 赋值给 coursePublish
        return coursePublish; // 返回 coursePublish
    } // 代码块结束

    //解决缓存穿透 // 保留原作者写的行内注释
//    @Override // 保留原作者写的行内注释
//    public CoursePublish getCoursePublishCache(Long courseId) { // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//        //从缓存中查询 // 保留原作者写的行内注释
//        Object jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 保留原作者写的行内注释
//        //缓存中有 // 保留原作者写的行内注释
//        if(jsonObj!=null){ // 保留原作者写的行内注释
////            System.out.println("=============从缓存中查询============="); // 保留原作者写的行内注释
//            //缓存中有直接返回数据 // 保留原作者写的行内注释
//            String jsonString = jsonObj.toString(); // 保留原作者写的行内注释
//            if("null".equals(jsonString)){ // 保留原作者写的行内注释
//                return null; // 保留原作者写的行内注释
//            } // 保留原作者写的行内注释
//            CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 保留原作者写的行内注释
//            return coursePublish; // 保留原作者写的行内注释
//        }else{ // 保留原作者写的行内注释
//            System.out.println("==查询数据库=="); // 保留原作者写的行内注释
//            //从数据库查询 // 保留原作者写的行内注释
//            CoursePublish coursePublish = getCoursePublish(courseId); // 保留原作者写的行内注释
////            if(coursePublish!=null){ // 保留原作者写的行内注释
//                //查询完成再存储到redis // 保留原作者写的行内注释
//                redisTemplate.opsForValue().set("course:"+courseId,JSON.toJSONString(coursePublish),30, TimeUnit.SECONDS); // 保留原作者写的行内注释
////            } // 保留原作者写的行内注释
//            return coursePublish; // 保留原作者写的行内注释
//        } // 保留原作者写的行内注释
//    } // 保留原作者写的行内注释

    //使用同步锁解决缓存击穿 // 保留原作者写的行内注释
//    @Override // 保留原作者写的行内注释
//    public CoursePublish getCoursePublishCache(Long courseId) { // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//            //从缓存中查询 // 保留原作者写的行内注释
//            Object jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 保留原作者写的行内注释
//            //缓存中有 // 保留原作者写的行内注释
//            if(jsonObj!=null){ // 保留原作者写的行内注释
////            System.out.println("=============从缓存中查询============="); // 保留原作者写的行内注释
//                //缓存中有直接返回数据 // 保留原作者写的行内注释
//                String jsonString = jsonObj.toString(); // 保留原作者写的行内注释
//                if("null".equals(jsonString)){ // 保留原作者写的行内注释
//                    return null; // 保留原作者写的行内注释
//                } // 保留原作者写的行内注释
//                CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 保留原作者写的行内注释
//                return coursePublish; // 保留原作者写的行内注释
//            }else{ // 保留原作者写的行内注释
//                synchronized (this){ // 保留原作者写的行内注释
//                    //再次查询一下缓存 // 保留原作者写的行内注释
//                    //从缓存中查询 // 保留原作者写的行内注释
//                    jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 保留原作者写的行内注释
//                    //缓存中有 // 保留原作者写的行内注释
//                    if(jsonObj!=null) { // 保留原作者写的行内注释
//                        //缓存中有直接返回数据 // 保留原作者写的行内注释
//                        String jsonString = jsonObj.toString(); // 保留原作者写的行内注释
//                        if("null".equals(jsonString)){ // 保留原作者写的行内注释
//                            return null; // 保留原作者写的行内注释
//                        } // 保留原作者写的行内注释
//                        CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 保留原作者写的行内注释
//                        return coursePublish; // 保留原作者写的行内注释
//                    } // 保留原作者写的行内注释
//                    System.out.println("==查询数据库=="); // 保留原作者写的行内注释
//                    //从数据库查询 // 保留原作者写的行内注释
//                    CoursePublish coursePublish = getCoursePublish(courseId); // 保留原作者写的行内注释
//                    //查询完成再存储到redis // 保留原作者写的行内注释
//                    redisTemplate.opsForValue().set("course:"+courseId,JSON.toJSONString(coursePublish),300, TimeUnit.SECONDS); // 保留原作者写的行内注释
//                    return coursePublish; // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//                } // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
// // 保留原作者写的行内注释
//            } // 保留原作者写的行内注释

    //使用redisson实现分布式锁 // 保留原作者写的行内注释
    @Override // 使用注解 @Override
    public CoursePublish getCoursePublishCache(Long courseId) { // 定义方法 getCoursePublishCache(...)


        //从缓存中查询 // 保留原作者写的行内注释
        Object jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 把 redisTemplate.opsForValue().get("course:" + courseId) 赋值给 jsonObj
        //缓存中有 // 保留原作者写的行内注释
        if (jsonObj != null) { // 定义方法 方法(...)
//            System.out.println("=============从缓存中查询============="); // 保留原作者写的行内注释
            //缓存中有直接返回数据 // 保留原作者写的行内注释
            String jsonString = jsonObj.toString(); // 把 jsonObj.toString() 的返回值赋给 jsonString
            if ("null".equals(jsonString)) { // 定义方法 方法(...)
                return null; // 返回 null
            } // 代码块结束
            CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 把 JSON.parseObject(jsonString, CoursePublish.class) 赋值给 coursePublish
            return coursePublish; // 返回 coursePublish
        } else { // 代码逻辑
            RLock lock = redissonClient.getLock("coursequerylock:" + courseId); // 把 redissonClient.getLock("coursequerylock:" + courseId) 赋值给 lock
            //获取分布式锁 // 保留原作者写的行内注释
            lock.lock(); // 执行语句
            try { // 尝试执行可能抛出异常的代码
                //再次查询一下缓存 // 保留原作者写的行内注释
                //从缓存中查询 // 保留原作者写的行内注释
                jsonObj = redisTemplate.opsForValue().get("course:" + courseId); // 把 redisTemplate.opsForValue().get("course:" + courseId) 赋值给 jsonObj
                //缓存中有 // 保留原作者写的行内注释
                if (jsonObj != null) { // 定义方法 方法(...)
                    //缓存中有直接返回数据 // 保留原作者写的行内注释
                    String jsonString = jsonObj.toString(); // 把 jsonObj.toString() 的返回值赋给 jsonString
                    if ("null".equals(jsonString)) { // 定义方法 方法(...)
                        return null; // 返回 null
                    } // 代码块结束
                    CoursePublish coursePublish = JSON.parseObject(jsonString, CoursePublish.class); // 把 JSON.parseObject(jsonString, CoursePublish.class) 赋值给 coursePublish
                    return coursePublish; // 返回 coursePublish
                } // 代码块结束
                System.out.println("==查询数据库=="); // 执行语句
//                try { // 保留原作者写的行内注释
//                    //手动延迟,测试锁的续期功能 // 保留原作者写的行内注释
//                    Thread.sleep(60000); // 保留原作者写的行内注释
//                } catch (InterruptedException e) { // 保留原作者写的行内注释
//                    throw new RuntimeException(e); // 保留原作者写的行内注释
//                } // 保留原作者写的行内注释
                //从数据库查询 // 保留原作者写的行内注释
                CoursePublish coursePublish = getCoursePublish(courseId); // 把 getCoursePublish(courseId) 赋值给 coursePublish
                //查询完成再存储到redis // 保留原作者写的行内注释
                redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish), 300, TimeUnit.SECONDS); // 执行语句
                return coursePublish; // 返回 coursePublish

            } finally { // 代码逻辑
                //手动释放锁 // 保留原作者写的行内注释
                lock.unlock(); // 执行语句
            } // 代码块结束
        } // 代码块结束


    } // 代码块结束

} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/impl/TeachplanServiceImpl.java

package com.xuecheng.content.service.impl; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.content.mapper.TeachplanMapper; // 导入 TeachplanMapper 类
import com.xuecheng.content.mapper.TeachplanMediaMapper; // 导入 TeachplanMediaMapper 类
import com.xuecheng.content.model.dto.BindTeachplanMediaDto; // 导入 BindTeachplanMediaDto 类
import com.xuecheng.content.model.dto.SaveTeachplanDto; // 导入 SaveTeachplanDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.model.po.Teachplan; // 导入 Teachplan 类
import com.xuecheng.content.model.po.TeachplanMedia; // 导入 TeachplanMedia 类
import com.xuecheng.content.service.TeachplanService; // 导入 TeachplanService 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Service; // 导入 Service 类
import org.springframework.transaction.annotation.Transactional; // 导入 Transactional 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/14 12:11 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Service // 使用注解 @Service
public class TeachplanServiceImpl implements TeachplanService { // 定义 class TeachplanServiceImpl

    @Autowired // 使用注解 @Autowired
    TeachplanMapper teachplanMapper; // 执行语句

    @Autowired // 使用注解 @Autowired
    TeachplanMediaMapper teachplanMediaMapper; // 执行语句

    @Override // 使用注解 @Override
    public List<TeachplanDto> findTeachplanTree(Long courseId) { // 定义方法 findTeachplanTree(...)
        List<TeachplanDto> teachplanDtos = teachplanMapper.selectTreeNodes(courseId); // 把 teachplanMapper.selectTreeNodes(courseId) 赋值给 teachplanDtos
        return teachplanDtos; // 返回 teachplanDtos
    } // 代码块结束

    private int getTeachplanCount(Long courseId,Long parentId){ // 定义方法 getTeachplanCount(...)
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>(); // 创建 LambdaQueryWrapper<> 对象并赋值给 queryWrapper
        queryWrapper = queryWrapper.eq(Teachplan::getCourseId, courseId).eq(Teachplan::getParentid, parentId); // 把 queryWrapper.eq(Teachplan::getCourseId, courseId).eq(Teachplan::getParentid, parentId) 赋值给 queryWrapper
        Integer count = teachplanMapper.selectCount(queryWrapper); // 把 teachplanMapper.selectCount(queryWrapper) 赋值给 count
        return  count+1; // 返回  count+1
    } // 代码块结束
    @Override // 使用注解 @Override
    public void saveTeachplan(SaveTeachplanDto saveTeachplanDto) { // 定义方法 saveTeachplan(...)
        //通过课程计划id判断是新增和修改 // 保留原作者写的行内注释
        Long teachplanId = saveTeachplanDto.getId(); // 把 saveTeachplanDto.getId() 的返回值赋给 teachplanId
        if(teachplanId ==null){ // 定义方法 方法(...)
            //新增 // 保留原作者写的行内注释
            Teachplan teachplan = new Teachplan(); // 创建 Teachplan 对象并赋值给 teachplan
            BeanUtils.copyProperties(saveTeachplanDto,teachplan); // 执行语句
            //确定排序字段,找到它的同级节点个数,排序字段就是个数加1  select count(1) from teachplan where course_id=117 and parentid=268 // 保留原作者写的行内注释
            Long parentid = saveTeachplanDto.getParentid(); // 把 saveTeachplanDto.getParentid() 的返回值赋给 parentid
            Long courseId = saveTeachplanDto.getCourseId(); // 把 saveTeachplanDto.getCourseId() 的返回值赋给 courseId
            int teachplanCount = getTeachplanCount(courseId, parentid); // 把 getTeachplanCount(courseId, parentid) 赋值给 teachplanCount
            teachplan.setOrderby(teachplanCount); // 执行语句
            teachplanMapper.insert(teachplan); // 执行语句

        }else{ // 代码逻辑
            //修改 // 保留原作者写的行内注释
            Teachplan teachplan = teachplanMapper.selectById(teachplanId); // 把 teachplanMapper.selectById(teachplanId) 赋值给 teachplan
            //将参数复制到teachplan // 保留原作者写的行内注释
            BeanUtils.copyProperties(saveTeachplanDto,teachplan); // 执行语句
            teachplanMapper.updateById(teachplan); // 执行语句
        } // 代码块结束

    } // 代码块结束

    @Transactional // 使用注解 @Transactional
    @Override // 使用注解 @Override
    public void associationMedia(BindTeachplanMediaDto bindTeachplanMediaDto) { // 定义方法 associationMedia(...)
        //课程计划id // 保留原作者写的行内注释
        Long teachplanId = bindTeachplanMediaDto.getTeachplanId(); // 把 bindTeachplanMediaDto.getTeachplanId() 的返回值赋给 teachplanId
        Teachplan teachplan = teachplanMapper.selectById(teachplanId); // 把 teachplanMapper.selectById(teachplanId) 赋值给 teachplan
        if(teachplan == null){ // 定义方法 方法(...)
            XueChengPlusException.cast("课程计划不存在"); // 执行语句
        } // 代码块结束

        //先删除原有记录,根据课程计划id删除它所绑定的媒资 // 保留原作者写的行内注释
        int delete = teachplanMediaMapper.delete(new LambdaQueryWrapper<TeachplanMedia>().eq(TeachplanMedia::getTeachplanId, bindTeachplanMediaDto.getTeachplanId())); // 把 teachplanMediaMapper.delete(new LambdaQueryWrapper<TeachplanMedia>().eq(TeachplanMedia::getTeachplanId, bindTeachplanMediaDto.getTeachplanId())) 赋值给 delete

        //再添加新记录 // 保留原作者写的行内注释
        TeachplanMedia teachplanMedia = new TeachplanMedia(); // 创建 TeachplanMedia 对象并赋值给 teachplanMedia
        BeanUtils.copyProperties(bindTeachplanMediaDto,teachplanMedia); // 执行语句
        teachplanMedia.setCourseId(teachplan.getCourseId()); // 执行语句
        teachplanMedia.setMediaFilename(bindTeachplanMediaDto.getFileName()); // 执行语句
        teachplanMediaMapper.insert(teachplanMedia); // 执行语句

    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/jobhandler/CoursePublishTask.java

package com.xuecheng.content.service.jobhandler; // 声明类所在的包路径

import com.xuecheng.base.exception.XueChengPlusException; // 导入 XueChengPlusException 类
import com.xuecheng.content.feignclient.CourseIndex; // 导入 CourseIndex 类
import com.xuecheng.content.feignclient.SearchServiceClient; // 导入 SearchServiceClient 类
import com.xuecheng.content.mapper.CoursePublishMapper; // 导入 CoursePublishMapper 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.po.CoursePublish; // 导入 CoursePublish 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import com.xuecheng.messagesdk.model.po.MqMessage; // 导入 MqMessage 类
import com.xuecheng.messagesdk.service.MessageProcessAbstract; // 导入 MessageProcessAbstract 类
import com.xuecheng.messagesdk.service.MqMessageService; // 导入 MqMessageService 类
import com.xxl.job.core.context.XxlJobHelper; // 导入 XxlJobHelper 类
import com.xxl.job.core.handler.annotation.XxlJob; // 导入 XxlJob 类
import lombok.extern.slf4j.Slf4j; // 导入 Slf4j 类
import org.springframework.beans.BeanUtils; // 导入 BeanUtils 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.stereotype.Component; // 导入 Component 类

import java.io.File; // 导入 File 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程发布的任务类 // 保留原作者写的块注释
 * @date 2023/2/21 16:15 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@Slf4j // 使用注解 @Slf4j
@Component // 使用注解 @Component
public class CoursePublishTask extends MessageProcessAbstract { // 定义 class CoursePublishTask

    @Autowired // 使用注解 @Autowired
    CoursePublishService coursePublishService; // 执行语句

    @Autowired // 使用注解 @Autowired
    SearchServiceClient searchServiceClient; // 执行语句

    @Autowired // 使用注解 @Autowired
    CoursePublishMapper coursePublishMapper; // 执行语句

    //任务调度入口 // 保留原作者写的行内注释
    @XxlJob("CoursePublishJobHandler") // 使用注解 @XxlJob("CoursePublishJobHandler")
    public void coursePublishJobHandler() throws Exception { // 定义方法 coursePublishJobHandler(...)

        // 分片参数 // 保留原作者写的行内注释
        int shardIndex = XxlJobHelper.getShardIndex();//执行器的序号,从0开始 // 把 XxlJobHelper.getShardIndex() 的返回值赋给 shardIndex
        int shardTotal = XxlJobHelper.getShardTotal();//执行器总数 // 把 XxlJobHelper.getShardTotal() 的返回值赋给 shardTotal
        //调用抽象类的方法执行任务 // 保留原作者写的行内注释
        process(shardIndex,shardTotal, "course_publish",30,60); // 执行语句


    } // 代码块结束

    //课程发布任务处理 // 保留原作者写的行内注释
    //执行课程发布任务的逻辑,如果此方法抛出异常说明任务执行失败 // 保留原作者写的行内注释
    @Override // 使用注解 @Override
    public boolean execute(MqMessage mqMessage) { // 定义方法 execute(...)
        //从mqMessage拿到课程id // 保留原作者写的行内注释
        Long courseId = Long.parseLong(mqMessage.getBusinessKey1()); // 把 Long.parseLong(mqMessage.getBusinessKey1()) 赋值给 courseId

        //课程静态化上传到minio // 保留原作者写的行内注释
        generateCourseHtml(mqMessage,courseId); // 执行语句


        //向elasticsearch写索引数据 // 保留原作者写的行内注释
        saveCourseIndex(mqMessage,courseId); // 执行语句

        //向redis写缓存 // 保留原作者写的行内注释

        //返回true表示任务完成 // 保留原作者写的行内注释
        return true; // 返回 true
    } // 代码块结束
    //生成课程静态化页面并上传至文件系统 // 保留原作者写的行内注释
    private void generateCourseHtml(MqMessage mqMessage,long courseId){ // 定义方法 generateCourseHtml(...)
        //消息id // 保留原作者写的行内注释
        Long taskId = mqMessage.getId(); // 把 mqMessage.getId() 的返回值赋给 taskId
        MqMessageService mqMessageService = this.getMqMessageService(); // 把 this.getMqMessageService() 的返回值赋给 mqMessageService

        //做任务幂等性处理 // 保留原作者写的行内注释
        //查询数据库取出该阶段执行状态 // 保留原作者写的行内注释
        int stageOne = mqMessageService.getStageOne(taskId); // 把 mqMessageService.getStageOne(taskId) 赋值给 stageOne
        if(stageOne>0){ // 定义方法 方法(...)
           log.debug("课程静态化任务完成,无需处理..."); // 执行语句
           return ; // 返回 
        } // 代码块结束


        //开始进行课程静态化 生成html页面 // 保留原作者写的行内注释
        File file = coursePublishService.generateCourseHtml(courseId); // 把 coursePublishService.generateCourseHtml(courseId) 赋值给 file
        if(file == null){ // 定义方法 方法(...)
            XueChengPlusException.cast("生成的静态页面为空"); // 执行语句
        } // 代码块结束
        // 将html上传到minio // 保留原作者写的行内注释
        coursePublishService.uploadCourseHtml(courseId,file); // 执行语句

        //..任务处理完成写任务状态为完成 // 保留原作者写的行内注释
        mqMessageService.completedStageOne(taskId); // 执行语句


    } // 代码块结束

    //保存课程索引信息 第二个阶段任务 // 保留原作者写的行内注释
    private void saveCourseIndex(MqMessage mqMessage,long courseId){ // 定义方法 saveCourseIndex(...)
        //任务id // 保留原作者写的行内注释
        Long taskId = mqMessage.getId(); // 把 mqMessage.getId() 的返回值赋给 taskId
        MqMessageService mqMessageService = this.getMqMessageService(); // 把 this.getMqMessageService() 的返回值赋给 mqMessageService
        //取出第二个阶段状态 // 保留原作者写的行内注释
        int stageTwo = mqMessageService.getStageTwo(taskId); // 把 mqMessageService.getStageTwo(taskId) 赋值给 stageTwo

        //任务幂等性处理 // 保留原作者写的行内注释
        if(stageTwo>0){ // 定义方法 方法(...)
            log.debug("课程索引信息已写入,无需执行..."); // 执行语句
            return; // 直接返回不带结果
        } // 代码块结束
        //查询课程信息,调用搜索服务添加索引接口 // 保留原作者写的行内注释
        //从课程发布表查询课程信息 // 保留原作者写的行内注释
        CoursePublish coursePublish = coursePublishMapper.selectById(courseId); // 把 coursePublishMapper.selectById(courseId) 赋值给 coursePublish

        CourseIndex courseIndex = new CourseIndex(); // 创建 CourseIndex 对象并赋值给 courseIndex
        BeanUtils.copyProperties(coursePublish,courseIndex); // 执行语句
        //远程调用 // 保留原作者写的行内注释
        Boolean add = searchServiceClient.add(courseIndex); // 把 searchServiceClient.add(courseIndex) 赋值给 add
        if(!add){ // 定义方法 方法(...)
            XueChengPlusException.cast("远程调用搜索服务添加课程索引失败"); // 执行语句
        } // 代码块结束

        //完成本阶段的任务 // 保留原作者写的行内注释
        mqMessageService.completedStageTwo(taskId); // 执行语句


    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/main/java/com/xuecheng/content/service/TeachplanService.java

package com.xuecheng.content.service; // 声明类所在的包路径

import com.xuecheng.content.model.dto.BindTeachplanMediaDto; // 导入 BindTeachplanMediaDto 类
import com.xuecheng.content.model.dto.SaveTeachplanDto; // 导入 SaveTeachplanDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划管理相关接口 // 保留原作者写的块注释
 * @date 2023/2/14 12:10 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
public interface TeachplanService { // 定义 interface TeachplanService
 /** // 保留原作者写的块注释
  * 根据课程id查询课程计划 // 保留原作者写的块注释
  * @param courseId 课程计划 // 保留原作者写的块注释
  * @return // 保留原作者写的块注释
  */ // 保留原作者写的块注释
  public List<TeachplanDto> findTeachplanTree(Long courseId); // 执行语句

 /** // 保留原作者写的块注释
  * 新增/修改/保存课程计划 // 保留原作者写的块注释
  * @param saveTeachplanDto // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void saveTeachplan(SaveTeachplanDto saveTeachplanDto); // 执行语句


 /** // 保留原作者写的块注释
  * @description 教学计划绑定媒资 // 保留原作者写的块注释
  * @param bindTeachplanMediaDto // 保留原作者写的块注释
  * @return com.xuecheng.content.model.po.TeachplanMedia // 保留原作者写的块注释
  * @author Mr.M // 保留原作者写的块注释
  * @date 2022/9/14 22:20 // 保留原作者写的块注释
  */ // 保留原作者写的块注释
 public void associationMedia(BindTeachplanMediaDto bindTeachplanMediaDto); // 执行语句
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/content/CourseBaseInfoServiceTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.junit.jupiter.api.Assertions; // 导入 Assertions 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseBaseInfoServiceTests { // 定义 class CourseBaseInfoServiceTests

    @Autowired // 使用注解 @Autowired
    CourseBaseInfoService courseBaseInfoService; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseBaseInfoService() { // 定义方法 testCourseBaseInfoService(...)

        //查询条件 // 保留原作者写的行内注释
        QueryCourseParamsDto courseParamsDto = new QueryCourseParamsDto(); // 创建 QueryCourseParamsDto 对象并赋值给 courseParamsDto
        courseParamsDto.setCourseName("java");//课程名称查询条件 // 执行语句
        courseParamsDto.setAuditStatus("202004");//202004表示课程审核通过 // 执行语句
        //分页参数对象 // 保留原作者写的行内注释
        PageParams pageParams = new PageParams(); // 创建 PageParams 对象并赋值给 pageParams
        pageParams.setPageNo(2L); // 执行语句
        pageParams.setPageSize(2L); // 执行语句

        PageResult<CourseBase> courseBasePageResult = courseBaseInfoService.queryCourseBaseList(null,pageParams, courseParamsDto); // 把 courseBaseInfoService.queryCourseBaseList(null,pageParams, courseParamsDto) 赋值给 courseBasePageResult
        System.out.println(courseBasePageResult); // 执行语句

    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/content/CourseBaseMapperTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.junit.jupiter.api.Assertions; // 导入 Assertions 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseBaseMapperTests { // 定义 class CourseBaseMapperTests

    @Autowired // 使用注解 @Autowired
    CourseBaseMapper courseBaseMapper; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseBaseMapper() { // 定义方法 testCourseBaseMapper(...)
        CourseBase courseBase = courseBaseMapper.selectById(18); // 把 courseBaseMapper.selectById(18) 赋值给 courseBase
        Assertions.assertNotNull(courseBase); // 执行语句

        //详细进行分页查询的单元测试 // 保留原作者写的行内注释
        //查询条件 // 保留原作者写的行内注释
        QueryCourseParamsDto courseParamsDto = new QueryCourseParamsDto(); // 创建 QueryCourseParamsDto 对象并赋值给 courseParamsDto
        courseParamsDto.setCourseName("java");//课程名称查询条件 // 执行语句

        //拼装查询条件 // 保留原作者写的行内注释
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>(); // 创建 LambdaQueryWrapper<> 对象并赋值给 queryWrapper
        //根据名称模糊查询,在sql中拼接 course_base.name like '%值%' // 保留原作者写的行内注释
        queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName()),CourseBase::getName,courseParamsDto.getCourseName()); // 执行语句
        //根据课程审核状态查询 course_base.audit_status = ? // 保留原作者写的行内注释
        queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus()), CourseBase::getAuditStatus,courseParamsDto.getAuditStatus()); // 执行语句
        //todo:按课程发布状态查询 // 保留原作者写的行内注释
        //分页参数对象 // 保留原作者写的行内注释
        PageParams pageParams = new PageParams(); // 创建 PageParams 对象并赋值给 pageParams
        pageParams.setPageNo(1L); // 执行语句
        pageParams.setPageSize(2L); // 执行语句

        //创建page分页参数对象,参数:当前页码,每页记录数 // 保留原作者写的行内注释
        Page<CourseBase> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize()); // 创建 Page<> 对象并赋值给 page
        //开始进行分页查询 // 保留原作者写的行内注释
        Page<CourseBase> pageResult = courseBaseMapper.selectPage(page, queryWrapper); // 把 courseBaseMapper.selectPage(page, queryWrapper) 赋值给 pageResult
        //数据列表 // 保留原作者写的行内注释
        List<CourseBase> items = pageResult.getRecords(); // 把 pageResult.getRecords() 的返回值赋给 items
        //总记录数 // 保留原作者写的行内注释
        long total = pageResult.getTotal(); // 把 pageResult.getTotal() 的返回值赋给 total

        //List<T> items, long counts, long page, long pageSize // 保留原作者写的行内注释
        PageResult<CourseBase> courseBasePageResult = new PageResult<CourseBase>(items,total,pageParams.getPageNo(), pageParams.getPageSize()); // 创建 PageResult<CourseBase> 对象并赋值给 courseBasePageResult
        System.out.println(courseBasePageResult); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/content/CourseCategoryMapperTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 导入 LambdaQueryWrapper 类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; // 导入 Page 类
import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.mapper.CourseBaseMapper; // 导入 CourseBaseMapper 类
import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import org.apache.commons.lang3.StringUtils; // 导入 StringUtils 类
import org.junit.jupiter.api.Assertions; // 导入 Assertions 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseCategoryMapperTests { // 定义 class CourseCategoryMapperTests

    @Autowired // 使用注解 @Autowired
    CourseCategoryMapper courseCategoryMapper; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseCategoryMapper() { // 定义方法 testCourseCategoryMapper(...)
        List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes("1"); // 把 courseCategoryMapper.selectTreeNodes("1") 赋值给 courseCategoryTreeDtos
        System.out.println(courseCategoryTreeDtos); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/content/CourseCategoryServiceTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.base.model.PageParams; // 导入 PageParams 类
import com.xuecheng.base.model.PageResult; // 导入 PageResult 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.dto.QueryCourseParamsDto; // 导入 QueryCourseParamsDto 类
import com.xuecheng.content.model.po.CourseBase; // 导入 CourseBase 类
import com.xuecheng.content.service.CourseBaseInfoService; // 导入 CourseBaseInfoService 类
import com.xuecheng.content.service.CourseCategoryService; // 导入 CourseCategoryService 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description TODO // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class CourseCategoryServiceTests { // 定义 class CourseCategoryServiceTests

    @Autowired // 使用注解 @Autowired
    CourseCategoryService courseCategoryService; // 执行语句

    @Test // 使用注解 @Test
    public void testCourseCategoryService() { // 定义方法 testCourseCategoryService(...)

        List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryService.queryTreeNodes("1"); // 把 courseCategoryService.queryTreeNodes("1") 赋值给 courseCategoryTreeDtos
        System.out.println(courseCategoryTreeDtos); // 执行语句

    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/content/FeignUploadTest.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.content.config.MultipartSupportConfig; // 导入 MultipartSupportConfig 类
import com.xuecheng.content.feignclient.MediaServiceClient; // 导入 MediaServiceClient 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 类

import java.io.File; // 导入 File 类
import java.io.IOException; // 导入 IOException 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 测试远程调用媒资服务 // 保留原作者写的块注释
 * @date 2023/2/22 10:30 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class FeignUploadTest { // 定义 class FeignUploadTest

    @Autowired // 使用注解 @Autowired
    MediaServiceClient mediaServiceClient; // 执行语句

    @Test // 使用注解 @Test
    public void test() throws IOException { // 定义方法 test(...)

        //将file转成MultipartFile // 保留原作者写的行内注释
        File file = new File("D:\\develop\\upload\\120.html"); // 创建 File 对象并赋值给 file
        MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(file); // 把 MultipartSupportConfig.getMultipartFile(file) 赋值给 multipartFile
        //远程调用得到返回值 // 保留原作者写的行内注释
        String upload = mediaServiceClient.upload(multipartFile, "course/120.html"); // 把 mediaServiceClient.upload(multipartFile, "course/120.html") 赋值给 upload
        if(upload==null){ // 定义方法 方法(...)
            System.out.println("走了降级逻辑"); // 执行语句
        } // 代码块结束
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/content/FreemarkerTest.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.content.mapper.TeachplanMapper; // 导入 TeachplanMapper 类
import com.xuecheng.content.model.dto.CoursePreviewDto; // 导入 CoursePreviewDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import com.xuecheng.content.service.CoursePublishService; // 导入 CoursePublishService 类
import freemarker.template.Configuration; // 导入 Configuration 类
import freemarker.template.Template; // 导入 Template 类
import freemarker.template.TemplateException; // 导入 TemplateException 类
import org.apache.commons.io.IOUtils; // 导入 IOUtils 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; // 导入 FreeMarkerTemplateUtils 类

import java.io.File; // 导入 File 类
import java.io.FileOutputStream; // 导入 FileOutputStream 类
import java.io.IOException; // 导入 IOException 类
import java.io.InputStream; // 导入 InputStream 类
import java.util.HashMap; // 导入 HashMap 类
import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 测试freemarker页面静态化方法 // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class FreemarkerTest { // 定义 class FreemarkerTest

    @Autowired // 使用注解 @Autowired
    CoursePublishService coursePublishService; // 执行语句

    @Test // 使用注解 @Test
    public void testGenerateHtmlByTemplate() throws IOException, TemplateException { // 定义方法 testGenerateHtmlByTemplate(...)

        Configuration configuration = new Configuration(Configuration.getVersion()); // 创建 Configuration 对象并赋值给 configuration

        //拿到classpath路径 // 保留原作者写的行内注释
        String classpath = this.getClass().getResource("/").getPath(); // 把 this.getClass().getResource("/").getPath() 的返回值赋给 classpath
        //指定模板的目录 // 保留原作者写的行内注释
        configuration.setDirectoryForTemplateLoading(new File(classpath+"/templates/")); // 执行语句
        //指定编码 // 保留原作者写的行内注释
        configuration.setDefaultEncoding("utf-8"); // 执行语句

        //得到模板 // 保留原作者写的行内注释
        Template template = configuration.getTemplate("course_template.ftl"); // 把 configuration.getTemplate("course_template.ftl") 赋值给 template
        //准备数据 // 保留原作者写的行内注释
        CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(120L); // 把 coursePublishService.getCoursePreviewInfo(120L) 赋值给 coursePreviewInfo
        HashMap<String, Object> map = new HashMap<>(); // 创建 HashMap<> 对象并赋值给 map
        map.put("model",coursePreviewInfo); // 执行语句

        //Template template 模板, Object model 数据 // 保留原作者写的行内注释
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); // 把 FreeMarkerTemplateUtils.processTemplateIntoString(template, map) 赋值给 html
        //输入流 // 保留原作者写的行内注释
        InputStream inputStream = IOUtils.toInputStream(html, "utf-8"); // 把 IOUtils.toInputStream(html, "utf-8") 赋值给 inputStream
        //输出文件 // 保留原作者写的行内注释
        FileOutputStream outputStream = new FileOutputStream(new File("D:\\develop\\upload\\120.html")); // 创建 FileOutputStream 对象并赋值给 outputStream
        //使用流将html写入文件 // 保留原作者写的行内注释
        IOUtils.copy(inputStream,outputStream); // 执行语句


    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/content/TeachplanMapperTests.java

package com.xuecheng.content; // 声明类所在的包路径

import com.xuecheng.content.mapper.CourseCategoryMapper; // 导入 CourseCategoryMapper 类
import com.xuecheng.content.mapper.TeachplanMapper; // 导入 TeachplanMapper 类
import com.xuecheng.content.model.dto.CourseCategoryTreeDto; // 导入 CourseCategoryTreeDto 类
import com.xuecheng.content.model.dto.TeachplanDto; // 导入 TeachplanDto 类
import org.junit.jupiter.api.Test; // 导入 Test 类
import org.springframework.beans.factory.annotation.Autowired; // 导入 Autowired 类
import org.springframework.boot.test.context.SpringBootTest; // 导入 SpringBootTest 类

import java.util.List; // 导入 List 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 课程计划mapper测试 // 保留原作者写的块注释
 * @date 2023/2/12 9:24 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@SpringBootTest // 使用注解 @SpringBootTest
public class TeachplanMapperTests { // 定义 class TeachplanMapperTests

    @Autowired // 使用注解 @Autowired
    TeachplanMapper teachplanMapper; // 执行语句

    @Test // 使用注解 @Test
    public void testSelectTreeNodes() { // 定义方法 testSelectTreeNodes(...)
        List<TeachplanDto> teachplanDtos = teachplanMapper.selectTreeNodes(117L); // 把 teachplanMapper.selectTreeNodes(117L) 赋值给 teachplanDtos
        System.out.println(teachplanDtos); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/java/com/xuecheng/ContentApplication.java

package com.xuecheng; // 声明类所在的包路径

import org.springframework.boot.SpringApplication; // 导入 SpringApplication 类
import org.springframework.boot.autoconfigure.SpringBootApplication; // 导入 SpringBootApplication 类
import org.springframework.cloud.openfeign.EnableFeignClients; // 导入 EnableFeignClients 类

/** // 保留原作者写的块注释
 * @author Mr.M // 保留原作者写的块注释
 * @version 1.0 // 保留原作者写的块注释
 * @description 内容管理服务启动类 // 保留原作者写的块注释
 * @date 2023/2/11 15:49 // 保留原作者写的块注释
 */ // 保留原作者写的块注释
@EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"}) // 使用注解 @EnableFeignClients(basePackages={"com.xuecheng.content.feignclient"})
@SpringBootApplication // 使用注解 @SpringBootApplication
public class ContentApplication { // 定义 class ContentApplication
    public static void main(String[] args) { // 定义方法 main(...)
        SpringApplication.run(ContentApplication.class, args); // 执行语句
    } // 代码块结束
} // 代码块结束

文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/resources/bootstrap.yml

spring:
  application:
    name: content-service
  cloud:
    nacos:
      server-addr: 192.168.101.65:8848
      discovery: #服务注册相关配置
        namespace: dev402
        group: xuecheng-plus-project
      config: #配置文件相关配置
        namespace: dev402
        group: xuecheng-plus-project
        file-extension: yaml
        refresh-enabled: true
        shared-configs:
          - data-id: feign-${spring.profiles.active}.yaml
            group: xuecheng-plus-common
            refresh: true
  profiles:
    active: dev   #环境名
#  datasource:
#    driver-class-name: com.mysql.cj.jdbc.Driver
#    url: jdbc:mysql://192.168.101.65:3306/xc402_content?serverTimezone=UTC&userUnicode=true&useSSL=false&
#    username: root
#    password: mysql
# 日志文件配置路径
logging:
  config: classpath:log4j2-dev.xml


文件:xuecheng-plus-content/xuecheng-plus-content-service/src/test/resources/log4j2-dev.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- XML 声明 -->
<Configuration monitorInterval="180" packages=""> <!-- SQL 语句片段 -->
    <properties> <!-- SQL 语句片段 -->
        <property name="logdir">logs</property> <!-- SQL 语句片段 -->
        <property name="PATTERN">%date{YYYY-MM-dd HH:mm:ss,SSS} %level [%thread][%file:%line] - %msg%n%throwable</property> <!-- SQL 语句片段 -->
    </properties> <!-- SQL 语句片段 -->
    <Appenders> <!-- SQL 语句片段 -->
        <Console name="Console" target="SYSTEM_OUT"> <!-- SQL 语句片段 -->
            <PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
        </Console> <!-- SQL 语句片段 -->

        <RollingFile name="ErrorAppender" fileName="${logdir}/error.log" <!-- SQL 语句片段 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/error.%d{yyyy-MM-dd-HH}.log" append="true"> <!-- SQL 语句片段 -->
            <PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <!-- SQL 语句片段 -->
            <Policies> <!-- SQL 语句片段 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- SQL 语句片段 -->
            </Policies> <!-- SQL 语句片段 -->
        </RollingFile> <!-- SQL 语句片段 -->

        <RollingFile name="DebugAppender" fileName="${logdir}/info.log" <!-- SQL 语句片段 -->
            filePattern="${logdir}/$${date:yyyy-MM-dd}/info.%d{yyyy-MM-dd-HH}.log" append="true"> <!-- SQL 语句片段 -->
            <PatternLayout pattern="${PATTERN}"/> <!-- SQL 语句片段 -->
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/> <!-- SQL 语句片段 -->
            <Policies> <!-- SQL 语句片段 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" /> <!-- SQL 语句片段 -->
            </Policies> <!-- SQL 语句片段 -->
        </RollingFile> <!-- SQL 语句片段 -->
        
        <!--异步appender-->
         <Async name="AsyncAppender" includeLocation="true"> <!-- SQL 语句片段 -->
            <AppenderRef ref="ErrorAppender"/> <!-- SQL 语句片段 -->
            <AppenderRef ref="DebugAppender"/> <!-- SQL 语句片段 -->
        </Async> <!-- SQL 语句片段 -->
    </Appenders> <!-- SQL 语句片段 -->
    
    <Loggers> <!-- SQL 语句片段 -->
         <!--过滤掉spring和mybatis的一些无用的debug信息
        <logger name="org.springframework" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
        <logger name="org.mybatis" level="INFO"> <!-- SQL 语句片段 -->
        </logger>--> <!-- SQL 语句片段 -->
        <logger name="cn.itcast.wanxinp2p.consumer.mapper" level="DEBUG"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->

        <logger name="springfox" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
		<logger name="org.apache.http" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
        <logger name="com.netflix.discovery" level="INFO"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
        
        <logger name="RocketmqCommon"  level="INFO" > <!-- SQL 语句片段 -->
		</logger> <!-- SQL 语句片段 -->
		
		<logger name="RocketmqRemoting" level="INFO"  > <!-- SQL 语句片段 -->
		</logger> <!-- SQL 语句片段 -->
		
		<logger name="RocketmqClient" level="WARN"> <!-- SQL 语句片段 -->
		</logger> <!-- SQL 语句片段 -->

        <logger name="org.dromara.hmily" level="WARN"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->

        <logger name="org.dromara.hmily.lottery" level="WARN"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->

        <logger name="org.dromara.hmily.bonuspoint" level="WARN"> <!-- SQL 语句片段 -->
        </logger> <!-- SQL 语句片段 -->
		
        <!--OFF   0-->
        <!--FATAL   100-->
        <!--ERROR   200-->
        <!--WARN   300-->
        <!--INFO   400-->
        <!--DEBUG   500-->
        <!--TRACE   600-->
        <!--ALL   Integer.MAX_VALUE-->
        <Root level="DEBUG" includeLocation="true"> <!-- SQL 语句片段 -->
            <AppenderRef ref="AsyncAppender"/> <!-- SQL 语句片段 -->
            <AppenderRef ref="Console"/> <!-- SQL 语句片段 -->
            <AppenderRef ref="DebugAppender"/> <!-- SQL 语句片段 -->
        </Root> <!-- SQL 语句片段 -->
    </Loggers> <!-- SQL 语句片段 -->
</Configuration> <!-- SQL 语句片段 -->
servlet:
https://clxhxhhr.top/posts/4371/
作者
clxstart
发布于
2026-09-26
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。