直播课堂系统09--腾讯云点播管理模块(一)


说是点播模块,其实就是继续写后端的curd…

点播管理模块

需求

添加点播课程,包含课程基本信息,课程章节,课程小结和最终发布

在这里插入图片描述

数据库相关表

在这里插入图片描述

环境搭建

以下全都是代码生成器生成,这些我自己写,截图记录一下格式。请添加图片描述

功能实现-课程列表

首先实现分页条件查询点播课程的功能,大体如下
在这里插入图片描述

创建mapper

继续创建mapper和一个空的xml

@Mapper
public interface CourseMapper extends BaseMapper<Course> {
}

编写service

首先创建一个CourseService接口,在service接口里面声明分页查询方法

public interface TeacherService extends IService<Teacher> {
    //分页获取课程列表 CourseQueryVo是因为只取需要的
    Map<String,Object> findPage(Page<Course> pageParam, CourseQueryVo courseQueryVo)
}

然后编写impl,记住impl首先extends在implements

@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService  {

    @Autowired
    private CourseService courseService;
    @Autowired
    private TeacherService teacherService;
    @Autowired
    private SubjectService subjectService;
    @Override
    public Map<String, Object> findPage(Page<Course> pageParam, CourseQueryVo courseQueryVo) {
        //获取Vo里面的各种属性
        String title = courseQueryVo.getTitle();//名称
        Long subjectId = courseQueryVo.getSubjectId();//二级分类
        Long subjectParentId = courseQueryVo.getSubjectParentId();//一级分类
        Long teacherId = courseQueryVo.getTeacherId();//讲师

        //封装条件
        QueryWrapper<Course> wrapper = new QueryWrapper<>();
        //判断字符串是否为空
        if(!StringUtils.isEmpty(title)) {
            wrapper.like("title",title);
        }
        if(!StringUtils.isEmpty(subjectId)) {
            wrapper.eq("subject_id",subjectId);
        }
        if(!StringUtils.isEmpty(subjectParentId)) {
            wrapper.eq("subject_parent_id",subjectParentId);
        }
        if(!StringUtils.isEmpty(teacherId)) {
            wrapper.eq("teacher_id",teacherId);
        }

        //调用方法查询
        Page<Course> pages = baseMapper.selectPage(pageParam, wrapper);
        long totalCount = pages.getTotal();//总记录数
        long totalPage = pages.getPages();//总页数
        long currentPage = pages.getCurrent();//当前页
        long size = pages.getSize();//每页记录数
        //每页数据集合
        List<Course> records = pages.getRecords();
        //将集合进行封装
        records.stream().forEach(item ->{
            //根据集合里的存储获取其讲师和分类的名称
            this.getTeacherOrSubjectName(item);
        });
        //封装返回数据
        HashMap<String, Object> map = new HashMap<>();
        map.put("totalCount",totalCount);
        map.put("totalPage",totalPage);
        map.put("records",records);
        return map;
    }
    //获取讲师和分类名称
    private Course getTeacherOrSubjectName(Course course){
        //因为查询出来的是course 所以肯定也要传入course
        //从其他的数据库里查询讲师和分类的具体名称
        Teacher teacher = teacherService.getById(course.getTeacherId());
        if(teacher!=null){
            //course继承的是BaseEntity 有一个其他参数为Map<String,Object> param
            //将其加入其中,需要多表连接查询时便这样使用
            course.getParam().put("teacherName",teacher.getName());
        }
        //查询分类名称
        Subject subjectOne = subjectService.getById(course.getSubjectParentId());
        if(subjectOne != null) {
            course.getParam().put("subjectParentTitle",subjectOne.getTitle());
        }
        Subject subjectTwo = subjectService.getById(course.getSubjectId());
        if(subjectTwo != null) {
            course.getParam().put("subjectTitle",subjectTwo.getTitle());
        }
        return course;
    }
}

编写controller

创建CourseController.java,注意刚加载时这里传入的Vo里面全都是null,所以传到service里面后,会变成查询所有。
Course本身已经与对应的数据库表绑定,所以由它构建QueryWrapper可以直接在对应表中进行查询。无论数据库表里有多少列,都会按定义的model的列返回,其余的列不会被返回。

@Api(tags = "课程管理接口")
@RestController
@CrossOrigin
@RequestMapping(value = "/admin/vod/course")
public class CourseController {
    @Autowired
    private CourseService courseService;

    @ApiOperation(value = "获取分页列表")
    @GetMapping("{page}/{limit}")
    public Result index(
            @ApiParam(name = "page", value = "当前页码", required = true)
            @PathVariable Long page,
            @ApiParam(name = "limit", value = "每页记录数", required = true)
            @PathVariable Long limit,
            @ApiParam(name = "courseVo", value = "查询对象", required = false)
                    CourseQueryVo courseQueryVo){
        //根据传入的page和limit构建page对象
        Page<Course> coursePage = new Page<>(page, limit);
        //将其传入方法中进行填充 并返回
        Map<String, Object> map = courseService.findPage(coursePage, courseQueryVo);
        return Result.ok(map);
    }
}

课程列表前端

首先在router下的index.js添加前端路径,再在api里添加调用后端接口的方法,再在views里面写组件且调用api里的方法。

index.js

在router下的index.js下添加路由

  //课程管理
  {
    path: '/vod',
    component: Layout,
    redirect: '/vod/course/list',
    name: 'Vod',
    meta: {
      title: '点播管理',
      icon: 'el-icon-bank-card'
    },
    alwaysShow: true,
    children: [
      {
        path: 'course/list',
        name: 'CourseList',
        component: () => import('@/views/vod/course/list'),
        meta: { title: '课程列表' }
      },
    ]
  },

api

在api目录创建路由文件course.js

import request from '@/utils/request'
const api_name = 'admin/vod/course'

export default{
    //课程列表
    getPageList(page,limit,searchObj){
        return request({
            //写的是后端路径
            url:`${api_name}/${page}/${limit}`,
            method: 'get',
            params: searchObj
        })
    },
}

在teacher.js里面定义一个获取所有讲师,以list形式返回的接口,之前在controller里面写过,不要和分页查询搞混。

    //获取所有讲师 不分页
    list(){
        return request({
            url:`${api_name}/findAll`,
            method:`get`
        })
    },

vue的list编写

再编写index.js里的/views/vod/course/list页面,代码也太多了,整整两百多行,复制粘贴后读一下吧…
实在摆不上来了,就这样吧。

效果

在这里插入图片描述

功能实现-发布课程-填写课程基本信息

发布课程一共有三个流程:填写课程基本信息,创建课程大纲-发布课程,这里实现第一个功能:填写课程基本信息。
每个课程都有自己的描述,并且也会经常更新,故创建一个数据库表course_description在这里插入图片描述

创建mapper

创建mapper并且将其与CourseDescription模型绑定,以后使用这个mapper进行的搜索都会在其model映射的table里。

public interface CourseDescriptionMapper extends BaseMapper<CourseDescription> {
}

不要忘记建对应的xml,虽然是空的。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.ggkt.vod.mapper.CourseDescriptionMapper">

</mapper>

创建service

创建service接口,然后创建其实现impl,记住实例化对应的mapper进行查询操作。
写到这里时悟了,之前的model分为model和vo,其中model对应的是数据库表,vo对应的是前端传来的数据,将其用一个model接收。

public interface CourseDescriptionService extends IService<CourseDescription> {
    //返回新建课程的基本ID
    public Long saveCourseInfo(CourseFormVo vo);
}

实现impl

@Service
public class CourseDescriptionImpl extends ServiceImpl<CourseDescriptionMapper, CourseDescription> implements CourseDescriptionService {
    //声明mapper
    @Autowired
    private CourseDescriptionMapper courseDescriptionMapper;
    //声明别家mapper 因为需要插入它的数据库
    @Autowired
    private CourseMapper courseMapper;
    //将获得的vo信息传入数据库中并存储 需要插入的表用:course表 courseDescription表
    @Override
    public Long saveCourseInfo(CourseFormVo vo) {
        Course course = new Course();
        //把传入的vo复制给course
        BeanUtils.copyProperties(vo,course);
        //插入course数据库
        courseMapper.insert(course);

        //插入coursedescription
        CourseDescription courseDescription = new CourseDescription();
        //研究了一下为什么直接转 然后发现命名不太一样...上面的course是一样的
        courseDescription.setDescription(vo.getDescription());
        courseDescription.setCourseId(course.getId());
        //插入数据库
        //descriptionService.save(courseDescription); 笔记里用的是这句 我感觉用mapper好一点吧。
        courseDescriptionMapper.insert(courseDescription);
        
        //返回课程id
        return course.getId();
    }
}

controller

这部分代码很坑,它service和mapper都是分开建的,但是课程添加,竟然是写在CourseController里,研究了下是因为在CourseService里面要做到对Description表的修改,按编码规范来说,需要注入其service,所以要将Description的类创建到Service层次,故对前面进行大改。

现在更改CourseController.java

    @ApiOperation(value = "新增")
    @PostMapping("save")
    public Result save(@RequestBody CourseFormVo vo){
        Long id = courseService.saveCourseInfo(vo);
        return Result.ok(id);
    }

前端

在index.js里面修改路由

      {
        path: 'course/info',
        name: 'CourseInfo',
        component: () => import('@/views/vod/course/form'),
        meta: { title: '发布课程' },
        hidden: true
      },

同时,在课程列表的界面也有添加键触发跳转,所以那里也要加上。
在这里插入图片描述

    add() {
      //跳转到的应该是路由 不是界面
      this.$router.push({ path: 'course/info' })
    },

跳转的应该是index.js里面定义的路由
在api的course.js里定义接口,注意这里不能按笔记一样写在params里,这样后端接收不到,因为后端接收的是RequestBody,必须把它放在data里!

    saveCourseInfo(courseInfo){
        return request({
            //写的是后端路径
            url:`${api_name}/save`,
            method: `post`,
            // params: courseInfo
            data: courseInfo
        })
    },

创建index.js里面指向的course/form.vue
在这里插入图片描述
在这里插入图片描述
注意这里要获取路由名字,所以要在前面的index.js里面添加,在created()时能跳转到对应界面。

      {
        path: 'course/info',
        name: 'CourseInfo',
        component: () => import('@/views/vod/course/form'),
        meta: { title: '发布课程' },
        hidden: true
      },
      {
        path: 'course/info/:id',
        name: 'CourseInfoEdit',
        component: () => import('@/views/vod/course/form'),
        meta: { title: '编辑课程' },
        hidden: true
      },
      {
        path: 'course/chapter/:id',
        name: 'CourseChapterEdit',
        component: () => import('@/views/vod/course/form'),
        meta: { title: '编辑大纲' },
        hidden: true
      },
<template>
  <div class="app-container">
    <h2 style="text-align: center;">发布新课程</h2>
    <el-steps :active="active" finish-status="success" simple style="margin-bottom: 40px">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="发布课程" />
    </el-steps>
    <!-- 填写课程基本信息 -->
    <info v-if="active === 0" />
    <!-- 创建课程大纲 -->
    <chapter v-if="active === 1" />
    <!-- 发布课程 -->
    <Publish v-if="active === 2 || active === 3" />
  </div>
</template>
<script>
// 引入子组件
import Info from '@/views/vod/course/components/Info'
import Chapter from '@/views/vod/course/components/Chapter'
import Publish from '@/views/vod/course/components/Publish'
export default {
  components: { Info, Chapter, Publish }, // 注册子组件
  data() {
    return {
      active: 0,
      courseId: null
    }
  },
  created() {
    // 获取路由id
    if (this.$route.params.id) {
      this.courseId = this.$route.params.id
    }
    if (this.$route.name === 'CourseInfoEdit') {
      this.active = 0
    }
    if (this.$route.name === 'CourseChapterEdit') {
      this.active = 1
    }
  }
}
</script>

注意一下上面引入了三个组件Info Chapter Publish
其组件关系如下
在这里插入图片描述

代码太长直接解读一下。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

效果

在这里插入图片描述

功能实现:修改课程基本信息

这个功能实现修改回显,即假如已经填写好基本信息并且点击保存,此时又想回退修改的情景。

修改Service

首先在CourseService接口里面添加对应的方法。

    //根据ID获取课程信息
    CourseFormVo getCourseFormVoById(long id);
    //根据ID修改课程信息
    void updateCourseById(CourseFormVo courseFormVo);

impl实现

    //根据id获取课程信息
    @Override
    public CourseFormVo getCourseFormVoById(long id) {
        //首先拿着id去数据库查数据 查到对应的课程
        Course course = courseMapper.selectById(id);
        if(course==null) return null;
        //从course_description表取数据
        //需要注意的是在course_description表里,课程id的存储方式是course_id 而非id 所以不能使用getById方法
        //需要建一个wapper 用eq
//        CourseDescription byId = courseDescriptionService.getById(id);
        QueryWrapper<CourseDescription> QueryWrapper = new QueryWrapper<>();
        QueryWrapper.eq("course_id",id);
        CourseDescription description = courseDescriptionService.getOne(QueryWrapper);
        //创建Form对象 因为要返回前端
        CourseFormVo courseFormVo = new CourseFormVo();
        BeanUtils.copyProperties(course,courseFormVo);
        if(description!=null){
            courseFormVo.setDescription(description.getDescription());
        }
        return courseFormVo;
    }

    //根据id修改课程信息
    @Override
    public void updateCourseById(CourseFormVo courseFormVo) {
        //获取课程
        Course course = new Course();
        BeanUtils.copyProperties(courseFormVo,course);
        //需要传入一个实体 根据这个实体的id更新
        courseMapper.updateById(course);

        //修改课程详情信息 同理如上 不能直接getid 修改的有详情和课程id
        QueryWrapper<CourseDescription> QueryWrapper = new QueryWrapper<>();
        QueryWrapper.eq("course_id",courseFormVo.getid());
        CourseDescription description = courseDescriptionService.getOne(QueryWrapper);
        
        description.setDescription(courseFormVo.getDescription());
        description.setId(course.getId());

        //更新上去 同理不能用updateById
        QueryWrapper.eq("course_id",description.getid());
        courseDescriptionService.update(QueryWrapper);
    }

controller实现

    @ApiOperation(value = "获取")
    @GetMapping("get/{id}")
    public Result get(@PathVariable Long id) {
        CourseFormVo course = courseService.getCourseFormVoById(id);
        return Result.ok(course);
    }
    @ApiOperation(value = "修改")
    @PutMapping("update")
    public Result updateById(@RequestBody CourseFormVo courseFormVo) {
        courseService.updateCourseById(courseFormVo);
        return Result.ok(null);
    }

前端实现

在api的course.js里定义连接后端的方法

    //id获取课程信息
    getCourseInfoById(id) {
        return request({
        url: `${api_name}/get/${id}`,
        method: 'get'
        })
    },
    //修改课程信息
    updateCourseInfoById(courseInfo) {
        return request({
        url: `${api_name}/update`,
        method: 'put',
        data: courseInfo
        })
    },

再修改info.vue界面,里面写具体的更新方法

  created() {
    if(this.$parent.courseId){
      // 回显 如果带有courseId 说明是从下一个步骤里回来的 只有在上一个步骤里点击保存 才能带有一个保存的courseId 先加载它的信息并修改就完事
      this.fetchCourseInfoById(this.$parent.courseId)
    }else{ //新增
      // 初始化分类列表
      this.initSubjectList()
    }
    // 获取讲师列表
    this.initTeacherList()
  },
    // 获取课程信息
    fetchCourseInfoById(id) {
      courseApi.getCourseInfoById(id).then(response => {
        this.courseInfo = response.data
        // 初始化分类列表
        subjectApi.getChildList(0).then(response => {
          this.subjectList = response.data
          // 填充二级菜单:遍历一级菜单列表,
          this.subjectList.forEach(subject => {
            // 找到和courseInfo.subjectParentId一致的父类别记录
            if (subject.id === this.courseInfo.subjectParentId) {
              // 拿到当前类别下的子类别列表,将子类别列表填入二级下拉菜单列表
              subjectApi.getChildList(subject.id).then(response => {
                this.subjectLevelTwoList = response.data
              })
            }
          })
        })
      })
    },
    // 保存并下一步
    saveAndNext() {
      this.saveBtnDisabled = true
      if (!this.$parent.courseId) {
        this.saveData()
      } else {
        this.updateData()
      }
    },
    // 修改
    updateData() {
      courseApi.updateCourseInfoById(this.courseInfo).then(response => {
        this.$message.success(response.message)
        this.$parent.courseId = response.data // 获取courseId
        this.$parent.active = 1 // 下一步
      })
    },
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程内容主要讲解如下几点:1:如何使用navaicat导入sql2:如何设置网站的的数据库账号等,如何使用iis发布网站3:演示后台管理功能、前台用户功能 该系统主要分餐厅网站管理员、游客、注册用户这几个角色网站管理系统设置网站设置:网站名称、关键字、描述、网站介绍关于我们设置:设置关于我们、联系我们、加入我们、法律声明广告和留言       首页轮播图设置:支持上传轮播图;       留言列表:用户的所有留言信息、支持删除机构图片       分类设置:支持录入、查看列表、修改、删除功能;比如外围、内部、教室等图片       录入图片:录入分类、标题、图片、介绍等       管理图片:查看所有图片列表;支持修改功能;支持删除功能会员管理查看会员信息列表、支持删除功能资讯中心       添加资讯:类型、标题、资讯内容等       管理资讯:查看所有资讯列表;支持修改功能;支持删除功能       资讯浏览列表:所有用户的信息浏览记录;支持删除功能。       信息收藏数据:所有注册用户的收藏资讯列表;支持删除功能。       信息评论列表:所有注册用户的用户评论列表;支持审核和删除功能。视频管理       视频分类设置:支持录入、查看列表、修改、删除功能;设置好后请不要修改;       视频课程录入:录入分类、名称、价格、介绍、课程封面等       视频课程管理:支持查看所有视频课程;支持修改和删除        课程视频录入,主要分下面几个步骤:       第1步:点击课程列表里面的【目录和内容】,可以查看现有课程章节课程       第2步:点击“录入章节”,添加章节名称。       第3步:点击章节后面的【录入具体视频课程】,录入课程名称、上传视频、勾选是否免费。       这样对应视频课程章节课程录入完毕;支持点击对应的“修改”操作。 订单管理       订单列表:查看所有用户的课程购买信息,购买课程、封面、价格、用户信息;评论管理       评论列表管理:显示所有用户对视频课程的标题、评论内容、评论审核的状态、评论用户。       对评论支持审核和删除;只有审核通过的评论才在前台显示。游客机构介绍关于我们、联系我们、加入我们、法律声明结构环境查看机构所有相关的环境图片和介绍资讯中心查看餐厅网站的所有资讯:通告、帮助中心、养生食谱、行业资讯留言反馈给网站管理员留言:主题、联系人、电话、邮箱、内容等视频中心可以查看站点的所有视频分类和对应的视频课程的封面和价格信息;如果要进行操作必须要注册为会员。注册用户注册用户除了享有游客的特别功能外,还有一些功能。注册和登录注册功能:填写用户名和密码注册登录:登录后可以享有会员功能。视频收藏、评论登录的会员可以在视频详情页面,收藏,评论(评论信息需要后台审核后显示)资讯收藏、评论登录的会员可以在资讯闲情页面,收藏,评论(评论信息需要后台审核后显示)视频购买座位预约步骤如下:第一步:选择分类;查看视频信息第二步:在视频情,点击“我要购买该课程”进行购买这里是模拟支付,假定点击去付款就真实支付 我的视频课程订单可以查看我已经购买的视频课程列表,可以直接点击课程名称回到课程学习页面视频课程浏览、收藏、评论信息管理当前会员资讯的视频课程浏览信息列表;收藏列表,支持删除;评论列表资讯浏览、收藏、评论信息管理当前会员资讯的浏览信息列表;资讯收藏列表,支持删除;评论列表用户信息维护自己的会员信息,包括:姓名、联系方式、邮箱、头像、简介、详细介绍等;支持修改功能密码修改和退出登录密码修改:修改自己的密码退出登录:清除登录的cookie、跳转到首页

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值