代码编织梦想

1 内容介绍

  • 课程大纲管理
    • 课程大纲列表显示
    • 章节、小节的添加、修改、删除
  • 课程信息确认
    • 编写sql语句实现
    • 课程最终发布

2 课程大纲列表后端实现

参考课程分类列表

1 创建实体类

创建章节、小节两个实体类,在章节实体类中使用list表示小节

@Data
public class VideoVo {
    private String id;
    private String title;
}
@Data
public class ChapterVo {
    private String id;
    private String title;
    // 章节包含很多小节
    private List<VideoVo> children = new ArrayList<>();
}

2 controller和service

controller

@RestController
@RequestMapping("/eduservice/chapter")
@CrossOrigin
public class EduChapterController {
    @Autowired
    private EduChapterService chapterService;
    // 课程大纲列表,根据课程id查章节、小节
    @GetMapping("getChapterVideo/{courseId}")
    public R getChapterVideo(@PathVariable String courseId) {
        List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId);
        return R.ok().data("allChapterVideo", list);
    }
}

service

@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {

    @Autowired
    private EduVideoService videoService; // 注入小节的service
    // 课程大纲列表,根据课程id查章节、小节
    @Override
    public List<ChapterVo> getChapterVideoByCourseId(String courseId) {
        // 1.根据课程id查询课程里面所有的章节
        QueryWrapper<EduChapter> wrapperChapter = new QueryWrapper<>();
        wrapperChapter.eq("course_id", courseId);
        List<EduChapter> eduChapterList = baseMapper.selectList(wrapperChapter);
        // 2.根据课程id查询课程里面所有的小节
        QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>();
        wrapperVideo.eq("course_id", courseId);
        List<EduVideo> eduVideoList = videoService.list(wrapperVideo);
        // 创建list集合,用于最终的数据封装 ChapterVo
        List<ChapterVo> finalList = new ArrayList<>();
        // 3.遍历查询章节list集合,进行封装
        for (EduChapter eduChapter: eduChapterList) {
            ChapterVo chapterVo = new ChapterVo();
            BeanUtils.copyProperties(eduChapter, chapterVo);
            finalList.add(chapterVo);
            // 创建list集合,用于封装章节中的小节VideoVo
            List<VideoVo> videoList = new ArrayList<>();
            // 4.遍历查询小节list集合,进行封装
            for (EduVideo eduVideo : eduVideoList) {
                // 判断小节的chapter_id与章节的id是否一样
                if (eduVideo.getChapterId().equals(eduChapter.getId())) {
                    VideoVo videoVo = new VideoVo();
                    BeanUtils.copyProperties(eduVideo, videoVo);
                    videoList.add(videoVo);
                }
            }
            // 小节list放到章节中
            chapterVo.setChildren(videoList);
        }
        return finalList;
    }
}

3 课程大纲列表前端实现

1 api/edu/chapter.js

import request from '@/utils/request'

export default {
  // 根据课程id获取章节、小节列表
  getAllChapterVideo(courseId) {
    return request({
      url: `/eduservice/chapter/getChapterVideo/${courseId}`,
      method: 'get'
    })
  }
}

2 chapter.vue

<!-- 章节 -->
<ul>
  <li v-for="chapter in chapterVideoList" :key="chapter.id">
    {{ chapter.title }}
    <ul>
      <li v-for="video in chapter.children" :key="video.id">
        {{ video.title }}
      </li>
    </ul>
  </li>
</ul>
created() {
  // 获取路由的id值
  if (this.$route.params && this.$route.params.id) {
    this.courseId = this.$route.params.id
  }
  // 根据课程id查询章节和小节
  this.getChapterVideo()
},
methods: {
  // 根据课程id查询章节、小节列表
  getChapterVideo() {
    chapter.getAllChapterVideo(this.courseId)
      .then(response => {
        this.chapterVideoList = response.data.allChapterVideo
      })
  },

4 修改课程基本信息

  • 点击上一步,回到上一步,并做课程基本信息数据回显

  • 数据回显页面,修改数据库内容

1 后端接口

  • 根据课程id,查询课程基本信息接口
  • 修改课程信息接口

controller

// 根据课程id查询课程基本信息
@GetMapping("getCourseInfo/{courseId}")
public R getCourseInfo(@PathVariable String courseId) {
    CourseInfoVo courseInfoVo = courseService.getCourseInfo(courseId);
    return R.ok().data("courseInfoVo", courseInfoVo);
}

// 修改课程信息
@PostMapping("updateCourseInfo")
public R updateCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {
    courseService.updateCourseInfo(courseInfoVo);
    return R.ok();
}

service

// 根据课程id查询课程基本信息
@Override
public CourseInfoVo getCourseInfo(String courseId) {
    // 1 查询课程表
    EduCourse eduCourse = baseMapper.selectById(courseId);
    CourseInfoVo courseInfoVo = new CourseInfoVo();
    BeanUtils.copyProperties(eduCourse, courseInfoVo);
    // 2 查询描述表
    EduCourseDescription courseDescription = courseDescriptionService.getById(courseId);
    courseInfoVo.setDescription(courseDescription.getDescription());
    return courseInfoVo;
}

// 修改课程信息
@Override
public void updateCourseInfo(CourseInfoVo courseInfoVo) {
    // 1 修改课程表
    EduCourse eduCourse = new EduCourse();
    BeanUtils.copyProperties(courseInfoVo, eduCourse);
    int update = baseMapper.updateById(eduCourse);
    if (update == 0) {
        throw new GuliException(20001, "修改课程信息失败");
    }
    // 2 修改描述表
    EduCourseDescription description = new EduCourseDescription();
    BeanUtils.copyProperties(courseInfoVo, description);
    courseDescriptionService.updateById(description);
}

2 前端实现

  • 定义接口的两个方法:api/edu/course.js

    // 3 根据课程id查询课程基本信息
    getCourseInfo(id) {
      return request({
        url: `/eduservice/course/getCourseInfo/${id}`,
        method: 'get'
      })
    },
    // 4 修改课程信息
    updateCourseInfo(courseInfoVo) {
      return request({
        url: '/eduservice/course/updateCourseInfo',
        method: 'post',
        data: courseInfoVo
      })
    }
    
  • 修改chapter.vue,跳转页面

    // 上一步
    previous() {
      this.$router.push({ path: '/course/info/' + this.courseId })
    },
    // 下一步
    next() {
      this.$router.push({ path: '/course/publish/' + this.courseId })
    }
    
  • 在info页面实现数据回显

    • 获取路由的课程id,调用根据id查询接口,数据显示
    // 获取路由id值
    if (this.$route.params && this.$route.params.id) {
      this.courseId = this.$route.params.id
      // 调用根据课程id查询
      this.getInfo()
    }
    
    // 7 根据课程id查询
    getInfo() {
      course.getCourseInfo(this.courseId)
        .then(response => {
          this.courseInfo = response.data.courseInfoVo
        })
    }
    
  • 下拉列表数据回显

    • 根据存储id和分类所有id进行比较,如果比较相同,让相同值进行数据回显

      <!--selected选中-->
      <select>
      	<option selected>前端开发</option>
      <select>
      
      created() {
        // 修改
        if (this.$route.params && this.$route.params.id) { // 获取路由id值
          this.courseId = this.$route.params.id
          // 调用根据课程id查询
          this.getInfo()
        } else { // 添加
          // 初始化所有讲师
          this.getListTeacher()
          // 初始化一级分类
          this.getOneSubject()
        }
      },
      
      // 7 根据课程id查询
      getInfo() {
        course.getCourseInfo(this.courseId)
          .then(response => {
            // courseInfo包含一级分类id和二级分类id
            this.courseInfo = response.data.courseInfoVo
            // 1)查询所有的分类,包含一级和二级
            subject.getSubjectList()
              .then(response => {
                // 2)获取所有一级分类
                this.subjectOneList = response.data.list
                // 3)遍历一级分类,获取二级分类
                for (const oneSubject of this.subjectOneList) {
                  if (this.courseInfo.subjectParentId === oneSubject.id) { // 当前一级分类id和所有一级分类id比较
                    this.subjectTwoList = oneSubject.children
                  }
                }
              })
            // 初始化所有讲师
            this.getListTeacher()
          })
      }
      
  • 添加、修改并保存

    // 添加课程
    addCourse() {
      course.addCourseInfo(this.courseInfo)
        .then(response => {
          // 提示信息
          this.$message({
            type: 'success',
            message: '添加课程信息成功'
          })
          // 跳转到下一步
          this.$router.push({ path: '/course/chapter/' + response.data.courseId })
        })
    },
    // 修改课程
    updateCourse() {
      course.updateCourseInfo(this.courseInfo)
        .then(response => {
          // 提示信息
          this.$message({
            type: 'success',
            message: '修改课程信息成功'
          })
          // 跳转到下一步
          this.$router.push({ path: '/course/chapter/' + this.courseId })
        })
    },
    // 6 保存
    saveOrUpdate() {
      // 判断添加还是修改
      // 没有id值,添加
      if (!this.courseInfo.id) {
        this.addCourse()
      } else {
        // 有id值,修改
        this.updateCourse()
      }
    },
    

5 课程章节操作

添加、修改、删除

1 添加按钮:添加章节

<el-button type="text" @click="dialogChapterFormVisible=true">添加章节</el-button>

2 点击 添加章节 按钮,弹出添加框,输入章节信息,点击保存添加

<!-- 添加和修改章节表单 -->
<el-dialog :visible.sync="dialogChapterFormVisible" title="添加章节">
  <el-form :model="chapter" label-width="120px">
    <el-form-item label="章节标题">
      <el-input v-model="chapter.title"/>
    </el-form-item>
    <el-form-item label="章节排序">
      <el-input-number v-model="chapter.sort" :min="0" controls-position="right"/>
    </el-form-item>
  </el-form>
  <div slot="footer" class="dialog-footer">
    <el-button @click="dialogChapterFormVisible = false">取 消</el-button>
    <el-button type="primary" @click="saveOrUpdate">确 定</el-button>
  </div>
</el-dialog>

3 开发章节后端接口

  • 修改章节

  • 添加章节

  • 删除章节

    • 如果章节里面没有小节,直接删除
    • 如果章节里面有小节
      • 第一种:删除章节的时候,把里面的所有小节都删除
      • 第二种:不让其删除(√)

controller

// 添加章节
@PostMapping("addChapter")
public R addChapter(@RequestBody EduChapter eduChapter) {
    chapterService.save(eduChapter);
    return R.ok();
}

// 根据章节id查询
@GetMapping("getChapterInfo/{chapterId}")
public R getChapterInfo(@PathVariable String chapterId) {
    EduChapter eduChapter = chapterService.getById(chapterId);
    return R.ok().data("chapter", eduChapter);
}

// 修改章节
@PostMapping("updateChapter")
public R updateChapter(@RequestBody EduChapter eduChapter) {
    chapterService.updateById(eduChapter);
    return R.ok();
}

// 删除章节
@DeleteMapping("{chapterId}")
public R deleteChapter(@PathVariable String chapterId) {
    boolean flag = chapterService.deleteChapter(chapterId);
    return flag ? R.ok() : R.error();
}

service

// 删除章节
@Override
public boolean deleteChapter(String chapterId) {
    // 根据章节id查询小节表,如果能查到数据,不删除
    QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();
    wrapper.eq("chapter_id", chapterId);
    int count = videoService.count(wrapper);
    if (count > 0) { // 有小节,不删除
        throw new GuliException(20001, "不能删除");
    } else { // 没有小节,删除
        int result = baseMapper.deleteById(chapterId);
        return result > 0;
    }
}

4 前端调用接口

chapter.js

// 添加章节
addChapter(chapter) {
  return request({
    url: `/eduservice/chapter/addChapter`,
    method: 'post',
    data: chapter
  })
},
// 根据id查询章节
getChapter(chapterId) {
  return request({
    url: `/eduservice/chapter/getChapterInfo/${chapterId}`,
    method: 'get'
  })
},
// 修改章节
updateChapter(chapter) {
  return request({
    url: `/eduservice/chapter/updateChapter`,
    method: 'post',
    data: chapter
  })
},
// 删除章节
deleteChapter(chapterId) {
  return request({
    url: `/eduservice/chapter/${chapterId}`,
    method: 'delete'
  })
}

chapter.vue

<ul>
  <li v-for="chapter in chapterVideoList" :key="chapter.id">
    <p>
      {{ chapter.title }}
      <span class="acts">
        <el-button type="text" @click="openEditChapter(chapter.id)">编辑</el-button>
        <el-button type="text" @click="removeChapter(chapter.id)">删除</el-button>
      </span>
    </p>
    <ul>
// 添加章节
addChapter() {
  // 设置课程id到chapter对象中
  this.chapter.courseId = this.courseId
  chapter.addChapter(this.chapter)
    .then(response => {
      // 1) 关闭弹窗
      this.dialogChapterFormVisible = false
      // 2) 提示信息
      this.$message({
        type: 'success',
        message: '添加章节成功'
      })
      // 3) 刷新页面
      this.getChapterVideo()
    })
},
// 修改章节
updateChapter() {
  chapter.updateChapter(this.chapter)
    .then(response => {
      // 1) 关闭弹窗
      this.dialogChapterFormVisible = false
      // 2) 提示信息
      this.$message({
        type: 'success',
        message: '修改章节成功'
      })
      // 3) 刷新页面
      this.getChapterVideo()
    })
},
// 保存
saveOrUpdate() {
  if (!this.chapter.id) {
    this.addChapter()
  } else {
    this.updateChapter()
  }
},
// 删除章节
removeChapter(chapterId) {
  // 1)弹出确认框
  this.$confirm('此操作将删除章节, 是否继续?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => { // 点击确认,执行then方法
    chapter.deleteChapter(chapterId)
      .then((response) => {
        // 提示信息
        this.$message({
          type: 'success',
          message: '删除成功!'
        })
        // 2) 刷新页面
        this.getChapterVideo()
      })
  })
},
// 修改章节弹框数据回显
openEditChapter(chapterId) {
  // 弹框
  this.dialogChapterFormVisible = true
  // 调用接口
  chapter.getChapter(chapterId)
    .then(response => {
      this.chapter = response.data.chapter
    })
}

6 课程小节操作

7 课程信息确认

1 概述

封面、课程名称、课程价格、课程分类、课程简介、课程讲师…

这些数据需要查询多张表,一般通过手写sql实现

多表连接查询:内连接、左外连接、右外连接

select ec.id,ec.title,ec.price,ec.lesson_num,
ecd.description,
et.`name`,
es1.title as oneSubject,
es2.title AS twoSubject
from edu_course ec LEFT OUTER JOIN edu_course_description ecd ON ec.id=ecd.id
LEFT OUTER JOIN edu_teacher et ON ec.teacher_id=et.id
LEFT OUTER JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
LEFT OUTER JOIN edu_subject es2 ON ec.subject_id=es2.id
WHERE ec.id='1555079243711434754'

2 controller、service、mapper

controller

// 根据课程id查询课程确认信息
@GetMapping("getPublishCourseInfo/{id}")
public R getPublishCourseInfo(@PathVariable String id) {
    CoursePublishVo coursePublishVo = courseService.publishCourseInfo(id);
    return R.ok().data("publishCourse", coursePublishVo);
}

service

@Override
public CoursePublishVo publishCourseInfo(String id) {
    CoursePublishVo publishCourseInfo = baseMapper.getPublishCourseInfo(id);
    return publishCourseInfo;
}

mapper

public interface EduCourseMapper extends BaseMapper<EduCourse> {

    public CoursePublishVo getPublishCourseInfo(String courseId);

}
<!--sql语句:根据课程id查询课程确认信息-->
<select id="getPublishCourseInfo" resultType="com.mys.eduservice.entity.vo.CoursePublishVo">
    select ec.id,ec.title,ec.price,ec.lesson_num AS lessonNum,ec.cover,
           ecd.description,
           et.`name` as teacherName,
           es1.title as subjectLevelOne,
           es2.title AS subjectLevelTwo
    from edu_course ec LEFT OUTER JOIN edu_course_description ecd ON ec.id=ecd.id
                       LEFT OUTER JOIN edu_teacher et ON ec.teacher_id=et.id
                       LEFT OUTER JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
                       LEFT OUTER JOIN edu_subject es2 ON ec.subject_id=es2.id
    WHERE ec.id=#{courseId}
</select>

3 测试报错与解决

问题:项目中创建mapper接口,编写xml文件写sql语句,执行出错:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.mys.eduservice.mapper.EduCourseMapper.getPublishCourseInfo

错误原因:由maven默认加载机制造成的,maven加载时,把java文件夹里面.java文件进行编译,其他类型文件不会加载

解决办法:

  • 复制xml到target目录

  • 把xml文件放到resources目录

  • 推荐:通过配置实现

    • pom.xml

      <build>
          <resources>
              <resource>
                  <directory>src/main/java</directory>
                  <includes>
                      <include>**/*.xml</include>
                  </includes>
                  <filtering>false</filtering>
              </resource>
          </resources>
      </build>
      
    • application.properties

      # 配置mapper.xml文件的路径
      mybatis-plus.mapper-locations=classpath:com/mys/eduservice/mapper/xml/*.xml
      

4 前端实现:显示查询的课程确认信息

api/edu/course.js

// 5 课程确认信息的显示
getPublishCourseInfo(id) {
  return request({
    url: `/eduservice/course/getPublishCourseInfo/${id}`,
    method: 'get'
  })
}

publish.vue

created() {
  // 1 获取路由课程id
  if (this.$route.params && this.$route.params.id) {
    this.courseId = this.$route.params.id
    // 2 调用接口方法,根据课程id查询
    this.getPublishCourseId()
  }
},
// 根据课程id查询确认发布信息
getPublishCourseId() {
  course.getPublishCourseInfo(this.courseId)
    .then(response => {
      this.publishCourse = response.data.publishCourse
      // 提示信息
      this.$message({
        type: 'success',
        message: '查询课程发布确认信息成功'
      })
    })
}

8 课程最终发布

修改课程的status状态是Normal-已发布

controller

// 课程最终发布:修改课程状态
@PostMapping("publishCourse/{id}")
public R publishCourse(@PathVariable String id) {
    EduCourse eduCourse = new EduCourse();
    eduCourse.setId(id);
    eduCourse.setStatus("Normal");
    courseService.updateById(eduCourse);
    return R.ok();
}

publish.vue

// 发布
publish() {
  // 调用发布方法
  course.publishCourse(this.courseId)
    .then(response => {
      // 提示信息
      this.$message({
        type: 'success',
        message: '发布成功'
      })
      // 跳转到课程列表
      this.$router.push({ path: '/course/list' })
    })
},
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mys_mys/article/details/128728193

谷粒学院(二十五)项目总结-爱代码爱编程

一、项目的启动运行 1、启动nacos D:\develop\nacos-server-1.1.4\nacos\bin下双击 startup.cmd 即可 2、启动Redis D:\develop\redis-2.8.9 下双击服务端redis-server.exe即可 2、启动nginx cmd中:nginx.exe 关闭:需要使用 ngi

JAVA-----学谷粒商城的一些笔记-爱代码爱编程

                        学谷粒商城的一些笔记 docker logs docker logs  elasticsearch docker logs  847容器id docker update 8dd --restart=always   容器id   自启 docker restart elasticsearch 重启

谷粒学院项目_自行整理笔记-爱代码爱编程

技术栈:Redis、Nginx、Springboot、maven、Git 如果本文对你有帮助,欢迎点赞、订阅以及star我的项目。 你的支持是我创作的最大动力! 第一天 项目背景 在线教育:网络教学 商业模式 B2B2C、B2C(管理员和普通用户) 功能模块 系统后台(管理员):讲师管理模块、课程分类管理模块、课程管理模块、统计分析模块、订

尚硅谷谷粒学院学习笔记1--基础环境搭建-爱代码爱编程

基础环境搭建 数据库设计规约模块说明环境搭建创建一个Spring Boot 的父工程,版本使用:2.2.1.RELEASE父工程pom.xml里面添加在pom.xml中添加依赖的版本删除pom.xml中的内容添加 确定依赖的版本配置 锁定依赖的版本删除src目录搭建service模块在父工程guli-parent下面创建模块service添加模块类

尚硅谷谷粒学院学习笔记6--EasyExcel,课程相关模块,章节小节-爱代码爱编程

EasyExcel,课程相关模块 EasyExcelExcel导入导出的应用场景引入依赖创建实体类EasyExcel对Excel写操作EasyExcel对Excel读操作创建读取操作的监听器读取课程分类管理接口service-edu模块配置依赖创建和Excel对应的实体类EduSubjectControllerEduSubjectServiceEd

尚硅谷谷粒学院学习笔记8--整合NUXT前台页面,已经整合redis缓存-爱代码爱编程

整合NUXT前台页面,已经整合redis缓存 服务端渲染技术NUXT什么是服务端渲染什么是NUXT下载压缩包解压修改package.json修改nuxt.config.js在命令提示终端中运行NUXT目录结构安装幻灯片插件配置插件页面布局复制静态资源default.vue首页面index.vue幻灯片插件路由固定路由动态路由首页显示banner数据

谷粒学院自学笔记_昭浅灬的博客-爱代码爱编程

谷粒学院自学笔记 day01上午一.MyBatis-Plus的入门配置 下午 day02 day01 上午 一.MyBatis-Plus的入门配置 1.学习了MyBaits-plus的相

在线教育-谷粒学院学习笔记(一)-爱代码爱编程

文章目录 1 项目介绍2 项目商业模式3 项目功能模块——B2C4 项目技术点——前后端分离5 MybatisPlus6 mp添加操作7 mp修改操作8 mp简单查询9 mp分页查询10 mp删除11 mp性能分析1

在线教育-谷粒学院学习笔记(二)-爱代码爱编程

文章目录 1 内容介绍2 前后端分离开发3 搭建项目环境4 开发讲师模块5 讲师列表6 讲师逻辑删除7 统一结果返回8 分页功能9 多条件组合查询带分页10 添加讲师11 讲师修改功能12 统一异常处理13 同一日志

在线教育-谷粒学院学习笔记(三)-爱代码爱编程

文章目录 1 搭建前端项目环境2 前端页面框架介绍3 讲师管理前端开发4 后台系统登录功能改造到本地5 前端框架开发过程6 讲师列表前端实现7 讲师分页前端实现8 讲师条件查询前端实现9 讲师删除功能前端实现10 讲

在线教育-谷粒学院学习笔记(五)-爱代码爱编程

文章目录 1 内容介绍2 课程分类前端实现3 课程列表功能实现4 课程管理概括5 添加课程信息后端实现6 添加课程信息前端实现7 前端完善 1 内容介绍 添加课程分类前端实现课程分类列表显示功能(树形结构

谷粒学院-爱代码爱编程

谷粒学院— 第一天 项目介绍 基本概述 在线教育顾名思义,是以网络为介质的教学方式,通过网络,学员与教师即使相隔万里也可以开展教学 活动;此外,借助网络课件,学员还可以随时随地进行学习,真正打破了时间和空间的限制,对于