谷粒学院——Day07【课程发布-添加课程信息】

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 谷粒学院——Day07【课程发布-添加课程信息】
❤ 作者主页: 欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于 Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得 关注点赞收藏评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉

课程分类管理【添加分类前端】

一、配置路由

1. 添加路由

src/router/index.js 中添加路由:

  {
    // 地址输入
    path: '/subject',
    component: Layout,
    // redirect: 重定向地址
    redirect: '/subject/list',
    name: '课程分类管理',
    // title:显示标签  icon:显示图标
    meta: { title: '课程分类管理', icon: 'example' },
    children: [
      {
        path: 'list',
        name: '课程分类列表',
        component: () => import('@/views/edu/subject/list'),
        meta: { title: '课程分类列表', icon: 'table' }
      },
      {
        path: 'save',
        name: '添加课程分类',
        component: () => import('@/views/edu/subject/save'),
        meta: { title: '添加课程分类', icon: 'tree' }
      }  
    ]
  }

2. 添加vue组件

在这里插入图片描述
 


二、表单组件save.vue

1. js定义数据

<script>
export default {
    data() {
      return {
            BASE_API: process.env.BASE_API, // 接口API地址
            importBtnDisabled: false,  // 按钮是否禁用
            loading: false
      }
    },

    created() {

    },

    methods: {
    
    }
}

</script>

2. template

<template>
      <div class="app-container">
    <el-form label-width="120px">
      <el-form-item label="信息描述">
        <el-tag type="info">excel模版说明</el-tag>
        <el-tag>
          <i class="el-icon-download" />
          <a :href="'/static/01.xlsx'">点击下载模版</a>
        </el-tag>
      </el-form-item>
      <el-form-item label="选择Excel">
        <el-upload
          ref="upload"
          :auto-upload="false"
          :on-success="fileUploadSuccess"
          :on-error="fileUploadError"
          :disabled="importBtnDisabled"
          :limit="1"
          :action="BASE_API + '/eduservice/subject/addSubject'"
          name="file"
          accept="application/vnd.ms-excel"
        >
          <el-button slot="trigger" size="small" type="primary"
            >选取文件</el-button
          >
          <el-button
            :loading="loading"
            style="margin-left: 10px"
            size="small"
            type="success"
            @click="submitUpload"
            >上传到服务器</el-button
          >
        </el-upload>
      </el-form-item>
    </el-form>
  </div>

</template>

3. js上传方法

methods: {

        // 点击按钮上传文件到接口里面
        submitUpload() {
            this.importBtnDisabled = true
            this.loading = true
            this.$refs.upload.submit()
        },

        // 上传成功
        fileUploadSuccess(response) {
           
        },

        // 上传失败
        fileUploadError() { 
       
        }
    }

4. 回调函数

 // 上传成功
        fileUploadSuccess(response) {
            // 提示信息
            this.loading = false
            this.$message({
                type: 'success',
                message: '添加课程分类成功'
            })
            // 跳转到课程分类列表


        },

        // 上传失败
        fileUploadError() { 
            this.loading = false
            this.$message({
                type: 'error',
                message: '添加课程 分类失败'
            })
        }

5. 测试

在这里插入图片描述
 
在这里插入图片描述
 
在这里插入图片描述


课程分类管理【课程分类列表】

一、后端实现

1. 创建实体类

entity 包下创建 Subject 包用于封装树形数据结构。

  • 一级分类
@Data
public class OneSubject {

    private String id;

    private String title;

    //一个一级分类有多个二级分类
    private List<TwoSubject> children = new ArrayList<>();

}
  • 二级分类
//二级分类
@Data
public class TwoSubject {

    private String id;

    private String title;
}

2. controller层

// 课程分类列表(树形)
    @ApiOperation(value = "嵌套数据列表")
    @GetMapping("getAllSubject")
    public R getAllSubject() {

        // list集合泛型是一级分类,一级分类中本身含有二级分类
        List<OneSubject> list = eduSubjectService.getAllOneTwoSubject();
        return R.ok().data("list", list);
    }

3. service层

EduSubjectService 接口中添加方法:

public interface EduSubjectService extends IService<EduSubject> {

    // 添加课程分类
    void addSubject(MultipartFile file, EduSubjectService eduSubjectService);

    // 课程分类列表(树形)
    List<OneSubject> getAllOneTwoSubject();
}

EduSubjectServiceImpl实现类中添加方法:

// 课程分类列表(树形)
    @Override
    public List<OneSubject> getAllOneTwoSubject() {

        // 1. 查询所有的一级分类 parent_id = 0
        QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();
        wrapperOne.eq("parent_id", 0);
        List<EduSubject> oneSubjectList = baseMapper.selectList(wrapperOne);

        // 2. 查询所有的二级分类 parent_id != 0
        QueryWrapper<EduSubject> wrapperTwo = new QueryWrapper<>();
        wrapperTwo.ne("parent_id", 0);
        List<EduSubject> twoSubjectList = baseMapper.selectList(wrapperTwo);

        // 创建list集合,用于封装最终数据
        List<OneSubject> finalSubjectList = new ArrayList<>();

        // 3. 封装一级分类
        // 查询出来所有的一级分类list集合遍历,得到每一个一级分类对象,获取每个一级分类对象值
        // 封装到要求的最终list集合里面 List<OneSubject> finalSubjectList
        for (int i = 0; i < oneSubjectList.size(); i ++ ) { // 遍历oneSubjectList集合
            // 得到oneSubjectList每个eduSubject对象
            EduSubject eduSubject = oneSubjectList.get(i);

            // 把eduSubject里面的值获取出来,放到oneSubject对象中去
            OneSubject oneSubject = new OneSubject();

//            oneSubject.setId(eduSubject.getId());
//            oneSubject.setTitle(eduSubject.getTitle());

            // 把eduSubject值复制到oneSubject中去【要求两个类的复制注入的属性名一致】
            BeanUtils.copyProperties(eduSubject, oneSubject);

            // 多个oneSubject放到finalSubject里面
            finalSubjectList.add(oneSubject);

            // 4. 封装二级分类
            // 在一级分类循环遍历查询所有的二级分类
            // 创建list集合封装每个一级分类的二级分类
            ArrayList<TwoSubject> twoFinalSubjectList = new ArrayList<>();

            // 遍历二级list集合
            for (int j = 0; j < twoSubjectList.size(); j++) {
                // 获取每个二级分类
                EduSubject tSubject = twoSubjectList.get(j);
                // 判断二级分类parent_id 和一级分类的id是否一样
                if (tSubject.getParentId().equals(oneSubject.getId())) {
                    // 把tSubject的值复制到TwoSubject,最终放到twoSubjectList里面
                    TwoSubject twoSubject = new TwoSubject();
                    BeanUtils.copyProperties(tSubject, twoSubject);
                    twoFinalSubjectList.add(twoSubject);
                }
            }

            // 把一级下面所有二级分类放到oneSubject里面
            oneSubject.setChildren(twoFinalSubjectList);
        }


        return finalSubjectList;
    }

二、前端实现

1. 参考 views/tree/index.vue

2. 创建api

api/edu/subject.js

import request from '@/utils/request' // 引入已经封装好的axios 和 拦截器

export default {

    // 课程分类列表
    getSubjectList() {
        return request({
            url: `/eduservice/subject/getAllSubject`,
            method: 'get'
          })
    }

}

3. list.vue

<template>
  <div class="app-container">
    <el-input v-model="filterText" placeholder="Filter keyword" style="margin-bottom:30px;" />

    <el-tree
      ref="tree2"
      :data="data2"
      :props="defaultProps"
      :filter-node-method="filterNode"
      class="filter-tree"
      default-expand-all
    />

  </div>
</template>

<script>
import subject from '@/api/edu/subject.js'
export default {

  data() {
    return {
      filterText: '',
      data2: [],  // 返回所有分类数据
      defaultProps: {
        children: 'children',
        label: 'title'
      }
    }
  },

  created() {
    this.getAllSubjectList()
  },

  watch: {
    filterText(val) {
      this.$refs.tree2.filter(val)
    }
  },

  methods: {
    getAllSubjectList(){
      subject.getSubjectList()
        .then(resp=>{
            this.data2 = resp.data.list
        })
    },

    filterNode(value, data) {
      if (!value) return true
      return data.title.indexOf(value) !== -1;
    }
  }
}
</script>

4. 优化前端过滤功能

 filterNode(value, data) {
      if (!value) return true
      return data.title.toLowerCase().indexOf(value.toLowerCase()) !== -1
    }

5. 测试

在这里插入图片描述


课程管理【课程发布流程说明】

请添加图片描述
 
请添加图片描述
 
请添加图片描述
 
在这里插入图片描述
 


课程管理【课程相关表关系】

在这里插入图片描述


课程管理【添加课程分析】

一、设计数据库

edu_teacher、edu_subject 数据库表在之前的功能中已经添加过了。

  • edu_course_description课程简介表

    CREATE TABLE `edu_course_description` (
      `id` char(19) NOT NULL COMMENT '课程ID',
      `description` text COMMENT '课程简介',
      `gmt_create` datetime NOT NULL COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程简介';
    
    #
    # Data for table "edu_course_description"
    #
    
    INSERT INTO `edu_course_description` VALUES ('1104870479077879809','<p>11</p>','2019-03-11 06:23:44','2019-03-11 06:23:44'),('1192252213659774977','<p>测试</p>','2019-11-07 09:27:33','2019-11-13 16:21:28'),('14','','2019-03-13 06:04:43','2019-03-13 06:05:33'),('15','','2019-03-13 06:03:33','2019-03-13 06:04:22'),('18','<p>本套Java视频完全针对零基础学员,课堂实录,自发布以来,好评如潮!Java视频中注重与学生互动,讲授幽默诙谐、细致入微,覆盖Java基础所有核心知识点,同类Java视频中也是代码量大、案例多、实战性强的。同时,本Java视频教程注重技术原理剖析,深入JDK源码,辅以代码实战贯穿始终,用实践驱动理论,并辅以必要的代码练习。</p>\n<p>------------------------------------</p>\n<p>视频特点:</p>\n<p>通过学习本Java视频教程,大家能够真正将Java基础知识学以致用、活学活用,构架Java编程思想,牢牢掌握\"源码级\"的Javase核心技术,并为后续JavaWeb等技术的学习奠定扎实基础。<br /><br />1.通俗易懂,细致入微:每个知识点高屋建瓴,深入浅出,简洁明了的说明问题<br />2.具实战性:全程真正代码实战,涵盖上百个企业应用案例及练习<br />3.深入:源码分析,更有 Java 反射、动态代理的实际应用等<br />4.登录尚硅谷官网,技术讲师免费在线答疑</p>','2019-03-06 18:06:36','2019-10-30 19:58:36');
    
  • edu_video课程小节表

    CREATE TABLE `edu_video` (
      `id` char(19) NOT NULL COMMENT '视频ID',
      `course_id` char(19) NOT NULL COMMENT '课程ID',
      `chapter_id` char(19) NOT NULL COMMENT '章节ID',
      `title` varchar(50) NOT NULL COMMENT '节点名称',
      `video_source_id` varchar(100) DEFAULT NULL COMMENT '云端视频资源',
      `video_original_name` varchar(100) DEFAULT NULL COMMENT '原始文件名称',
      `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序字段',
      `play_count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '播放次数',
      `is_free` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否可以试听:0收费 1免费',
      `duration` float NOT NULL DEFAULT '0' COMMENT '视频时长(秒)',
      `status` varchar(20) NOT NULL DEFAULT 'Empty' COMMENT 'Empty未上传 Transcoding转码中  Normal正常',
      `size` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '视频源文件大小(字节)',
      `version` bigint(20) unsigned NOT NULL DEFAULT '1' COMMENT '乐观锁',
      `gmt_create` datetime NOT NULL COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`),
      KEY `idx_course_id` (`course_id`),
      KEY `idx_chapter_id` (`chapter_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程视频';
    
    #
    # Data for table "edu_video"
    #
    
    INSERT INTO `edu_video` VALUES ('1182499307429339137','18','32','第一节','','',0,0,0,0,'',0,1,'2019-10-11 11:32:59','2019-10-11 11:57:38'),('1185312444399071234','14','1','12','','',0,0,0,0,'Empty',0,1,'2019-10-19 05:51:23','2019-10-19 05:51:33'),('1189434737808990210','18','44','测试','','',1,0,0,0,'Empty',0,1,'2019-10-30 14:51:55','2019-10-30 14:51:55'),('1189471423678939138','18','1181729226915577857','test','2b887dc9584d4dc68908780ec57cd3b9','视频',1,0,0,0,'Empty',0,1,'2019-10-30 17:17:41','2019-10-30 17:17:41'),('1189476403626409986','18','1181729226915577857','22','5155c73dc112475cbbddccf4723f7cef','视频.mp4',0,0,0,0,'Empty',0,1,'2019-10-30 17:37:29','2019-10-30 17:37:29'),('1192252824606289921','1192252213659774977','1192252428399751169','第一课时','756cf06db9cb4f30be85a9758b19c645','eae2b847ef8503b81f5d5593d769dde2.mp4',0,0,0,0,'Empty',0,1,'2019-11-07 09:29:59','2019-11-07 09:29:59'),('1192628092797730818','1192252213659774977','1192252428399751169','第二课时','2a02d726622f4c7089d44cb993c531e1','eae2b847ef8503b81f5d5593d769dde2.mp4',0,0,1,0,'Empty',0,1,'2019-11-08 10:21:10','2019-11-08 10:21:22'),('1192632495013380097','1192252213659774977','1192252428399751169','第三课时','4e560c892fdf4fa2b42e0671aa42fa9d','eae2b847ef8503b81f5d5593d769dde2.mp4',0,0,1,0,'Empty',0,1,'2019-11-08 10:38:40','2019-11-08 10:38:40'),('1194117638832111617','1192252213659774977','1192252428399751169','第四课时','4e560c892fdf4fa2b42e0671aa42fa9d','eae2b847ef8503b81f5d5593d769dde2.mp4',0,0,0,0,'Empty',0,1,'2019-11-12 13:00:05','2019-11-12 13:00:05'),('1196263770832023554','1192252213659774977','1192252428399751169','第五课时','27d21158b0834cb5a8d50710937de330','eae2b847ef8503b81f5d5593d769dde2.mp4',5,0,0,0,'Empty',0,1,'2019-11-18 11:08:03','2019-11-18 11:08:03'),('17','18','15','第一节:Java简介','196116a6fee742e1ba9f6c18f65bd8c1','1',1,1000,1,100,'Draft',0,1,'2019-01-01 13:08:57','2019-10-11 11:26:39'),('18','18','15','第二节:表达式和赋值语句','2d99b08ca0214909899910c9ba042d47','7 - How Do I Find Time for My ',2,999,1,100,'Draft',0,1,'2019-01-01 13:09:02','2019-03-08 03:30:27'),('19','18','15','第三节:String类','51120d59ddfd424cb5ab08b44fc8b23a','eae2b847ef8503b81f5d5593d769dde2.mp4',3,888,0,100,'Draft',0,1,'2019-01-01 13:09:05','2019-11-12 12:50:45'),('20','18','15','第四节:程序风格','2a38988892d84df598752226c50f3fa3','00-day10总结.avi',4,666,0,100,'Draft',0,1,'2019-01-01 13:09:05','2019-10-11 09:20:09');
    
    
  • edu_course课程基本信息

    CREATE TABLE `edu_course` (
      `id` char(19) NOT NULL COMMENT '课程ID',
      `teacher_id` char(19) NOT NULL COMMENT '课程讲师ID',
      `subject_id` char(19) NOT NULL COMMENT '课程专业ID',
      `subject_parent_id` char(19) NOT NULL COMMENT '课程专业父级ID',
      `title` varchar(50) NOT NULL COMMENT '课程标题',
      `price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '课程销售价格,设置为0则可免费观看',
      `lesson_num` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '总课时',
      `cover` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '课程封面图片路径',
      `buy_count` bigint(10) unsigned NOT NULL DEFAULT '0' COMMENT '销售数量',
      `view_count` bigint(10) unsigned NOT NULL DEFAULT '0' COMMENT '浏览数量',
      `version` bigint(20) unsigned NOT NULL DEFAULT '1' COMMENT '乐观锁',
      `status` varchar(10) NOT NULL DEFAULT 'Draft' COMMENT '课程状态 Draft未发布  Normal已发布',
      `is_deleted` tinyint(3) DEFAULT NULL COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
      `gmt_create` datetime NOT NULL COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`),
      KEY `idx_title` (`title`),
      KEY `idx_subject_id` (`subject_id`),
      KEY `idx_teacher_id` (`teacher_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程';
    
    #
    # Data for table "edu_course"
    #
    
    INSERT INTO `edu_course` VALUES ('1192252213659774977','1189389726308478977','1178214681139539969','1178214681118568449','java基础课程:test',0.01,2,'https://guli-file-190513.oss-cn-beijing.aliyuncs.com/cover/default.gif',4,387,1,'Normal',0,'2019-11-07 09:27:33','2019-11-18 13:35:03'),('14','1189389726308478977','1101348944971091969','1101348944920760321','XHTML CSS2 JS整站制作教程课程学习',0.00,3,'http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/13/d0086eb0-f2dc-45f7-bba1-744d95e5be0f.jpg',3,44,15,'Normal',0,'2018-04-02 18:33:34','2019-11-16 21:21:45'),('15','1189389726308478977','1101348944971091969','1101348944920760321','HTML5入门课程学习',0.00,23,'http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/13/22997b8e-3606-4d2e-9b4f-09f48418b6e4.jpg',0,51,17,'Normal',0,'2018-04-02 18:34:32','2019-11-12 10:19:20'),('18','1189389726308478977','1178214681139539969','1178214681118568449','Java精品课程',0.01,20,'http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg',151,737,6,'Normal',0,'2018-04-02 21:28:46','2019-11-18 11:14:52');
    
    
  • edu_chapter课程章节表

    CREATE TABLE `edu_chapter` (
      `id` char(19) NOT NULL COMMENT '章节ID',
      `course_id` char(19) NOT NULL COMMENT '课程ID',
      `title` varchar(50) NOT NULL COMMENT '章节名称',
      `sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '显示排序',
      `gmt_create` datetime NOT NULL COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL COMMENT '更新时间',
      PRIMARY KEY (`id`),
      KEY `idx_course_id` (`course_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程';
    
    #
    # Data for table "edu_chapter"
    #
    
    INSERT INTO `edu_chapter` VALUES ('1','14','第一章:HTML',0,'2019-01-01 12:27:40','2019-01-01 12:55:30'),('1181729226915577857','18','第七章:I/O流',70,'2019-10-09 08:32:58','2019-10-09 08:33:20'),('1192252428399751169','1192252213659774977','第一章节',0,'2019-11-07 09:28:25','2019-11-07 09:28:25'),('15','18','第一章:Java入门',0,'2019-01-01 12:27:40','2019-10-09 09:13:19'),('3','14','第二章:CSS',0,'2019-01-01 12:55:35','2019-01-01 12:27:40'),('32','18','第二章:控制台输入和输出',0,'2019-01-01 12:27:40','2019-01-01 12:27:40'),('44','18','第三章:控制流',0,'2019-01-01 12:27:40','2019-01-01 12:27:40'),('48','18','第四章:类的定义',0,'2019-01-01 12:27:40','2019-01-01 12:27:40'),('63','18','第五章:数组',0,'2019-01-01 12:27:40','2019-01-01 12:27:40'),('64','18','第六章:继承',61,'2019-01-01 12:27:40','2019-10-09 08:32:47');
    
    

二、使用代码生成器生成课程相关代码

只需要在 CodeGenerator 类中修改数据库表即可,即 strategy.setInclude("edu_course", "edu_course_description", "edu_chapter", "edu_video")

public class CodeGenerator {

    @Test
    public void run() {

        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir("D:\\IDEA\\guli_parent\\service\\service_edu" + "/src/main/java"); //输出目录

        gc.setAuthor("jyu_zwy"); //作者名
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖

        gc.setServiceName("%sService");    //去掉Service接口的首字母I
        gc.setIdType(IdType.ID_WORKER_STR); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式

        mpg.setGlobalConfig(gc);

        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/guli?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("abc123");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();

        //生成包:com.atguigu.eduservice
        pc.setModuleName("eduservice"); //模块名
        pc.setParent("com.atguigu");

        //生成包:com.atguigu.controller
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("edu_course", "edu_course_description", "edu_chapter", "edu_video");//根据数据库哪张表生成,有多张表就加逗号继续填写

        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

        mpg.setStrategy(strategy);


        // 6、执行
        mpg.execute();

    }
}

因为课程简介是依赖于课程的,所以删掉EduCourseDescriptionController 类。


课程管理【添加课程信息】

一、后端实现

1. 实体类

vo 包下创建 CourseInfoVo 类封装表单提交数据。

@ApiModel(value = "课程基本信息", description = "编辑课程基本信息的表单对象")
@Data
public class CourseInfoVo {

    @ApiModelProperty(value = "课程ID")
    private String id;

    @ApiModelProperty(value = "课程讲师ID")
    private String teacherId;

    @ApiModelProperty(value = "课程专业ID")
    private String subjectId;

    @ApiModelProperty(value = "课程标题")
    private String title;

    @ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")
    private BigDecimal price;

    @ApiModelProperty(value = "总课时")
    private Integer lessonNum;

    @ApiModelProperty(value = "课程封面图片路径")
    private String cover;

    @ApiModelProperty(value = "课程简介")
    private String description;
}

2. controller层

@RestController
@CrossOrigin
@RequestMapping("/eduservice/course")
public class EduCourseController {

    @Autowired
    private EduCourseService courseService;

    // 添加课程基本信息方法
    @PostMapping("addCourseInfo")
    public R addCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {
        courseService.saveCourseInfo(courseInfoVo);
        return R.ok();
    }
}

3. service层

EduCourseService:

public interface EduCourseService extends IService<EduCourse> {

    // 添加课程基本信息
    void saveCourseInfo(CourseInfoVo courseInfoVo);
}

EduCourseServiceImpl:

@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {

    // 课程描述的注入
    @Autowired
    private EduCourseDescriptionService courseDescriptionService;


    // 添加课程基本信息
    @Override
    public void saveCourseInfo(CourseInfoVo courseInfoVo) {

        // 1.向课程表中添加课程基本信息
        // CourseInfo 对象 转换成 EduCourse对象
        EduCourse eduCourse = new EduCourse();
        BeanUtils.copyProperties(courseInfoVo, eduCourse);
        int result = baseMapper.insert(eduCourse);

        if (result <= 0) { // 表示添加失败
            throw new GuliException(20001, "添加课程信息失败");
        }

        // 2.向课程简介表添加课程简介
        // edu_course_description
        EduCourseDescription eduCourseDescription = new EduCourseDescription();
        eduCourseDescription.setDescription(courseInfoVo.getDescription());
        courseDescriptionService.save(eduCourseDescription);
    }
}

4. 测试

防止测试报错,需要提前做一下操作:

  1. EntityEduCourseEduCourseDescription 中的创建时间和更新时间中加入对应的注解。

EduCourse:

@ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;
`EduCourseDescription`:
```
@ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;
```
  1. 把数据库表 edu_coursesubject_parent_id 字段设置为可以为 null

在这里插入图片描述
 

  1. 启动项目后,访问http://localhost:8001/swagger-ui.html

在这里插入图片描述
 
在这里插入图片描述
 
edu_course:
在这里插入图片描述
 
edu_course_description:
在这里插入图片描述
 

5. 优化

  1. 问题

edu_course 课程信息表 和 edu_course_description 课程描述表之间没有关联。
添加进去的数据 id 不一样,没有进行一对一的关联。
在这里插入图片描述
 

  1. 修改 EduCourseServiceImpl 代码

    @Service
    public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {
    
        // 课程描述的注入
        @Autowired
        private EduCourseDescriptionService courseDescriptionService;
    
    
        // 添加课程基本信息
        @Override
        public void saveCourseInfo(CourseInfoVo courseInfoVo) {
    
            // 1.向课程表中添加课程基本信息
            // CourseInfo 对象 转换成 EduCourse对象
            EduCourse eduCourse = new EduCourse();
            BeanUtils.copyProperties(courseInfoVo, eduCourse);
            int result = baseMapper.insert(eduCourse);
    
            if (result <= 0) { // 表示添加失败
                throw new GuliException(20001, "添加课程信息失败");
            }
    
            // 获取添加之后课程信息的id
            String cid = eduCourse.getId();
    
            // 2.向课程简介表添加课程简介
            // edu_course_description
            EduCourseDescription eduCourseDescription = new EduCourseDescription();
            eduCourseDescription.setDescription(courseInfoVo.getDescription());
    
            // 手动设置描述课程表的id,与上面的课程信息表id关联
            eduCourseDescription.setId(cid);
    
            courseDescriptionService.save(eduCourseDescription);
    
        }
    }
    

    在这里插入图片描述

  2. 修改 EduCourseDescriptionid 生成策略,设置为 手动输入

    在这里插入图片描述

 

  1. 修改controller,添加返回课程id

    @RestController
    @CrossOrigin
    @RequestMapping("/eduservice/course")
    public class EduCourseController {
    
        @Autowired
        private EduCourseService courseService;
    
        // 添加课程基本信息方法
        @PostMapping("addCourseInfo")
        public R addCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {
            // 返回添加之后课程id,为了后面的添加大纲使用
            String id = courseService.saveCourseInfo(courseInfoVo);
            return R.ok().data("courseId", id);
        }
    }
  2. 修改service,返回String

    public interface EduCourseService extends IService<EduCourse> {
    
        // 添加课程基本信息
        String saveCourseInfo(CourseInfoVo courseInfoVo);
    }
    
  3. 测试

在这里插入图片描述
 
在这里插入图片描述
 
在这里插入图片描述
 


二、前端实现

1. 添加路由

 {
    // 地址输入
    path: '/course',
    component: Layout,
    // redirect: 重定向地址
    redirect: '/course/list',
    name: '课程管理',
    // title:显示标签  icon:显示图标
    meta: { title: '课程管理', icon: 'example' },
    children: [
      {
        path: 'list',
        name: '课程列表',
        component: () => import('@/views/edu/course/list'),
        meta: { title: '课程列表', icon: 'table' }
      },
      {
        path: 'info',
        name: '添加课程',
        component: () => import('@/views/edu/course/info'),
        meta: { title: '添加课程', icon: 'tree' }
      },
      {
        path: 'info/:id',
        name: 'EduCourseInfoEdit',
        component: () => import('@/views/edu/course/info'),
        meta: { title: '编辑课程基本信息', noCache: true },
        hidden: true
        },
        {
        path: 'chapter/:id',
        name: 'EduCourseChapterEdit',
        component: () => import('@/views/edu/course/chapter'),
        meta: { title: '编辑课程大纲', noCache: true },
        hidden: true
        },
        {
        path: 'publish/:id',
        name: 'EduCoursePublishEdit',
        component: () => import('@/views/edu/course/publish'),
        meta: { title: '发布课程', noCache: true },
        hidden: true
        }
    ]
  },

2. 添加vue组件

在这里插入图片描述
 

3. 整合步骤条组件

  1. 课程信息页面 info.vue
<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="1" process-status="wait" align-center style="margin-bottom: 40px;">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>

    <el-form label-width="120px">
      <el-form-item>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="next"
          >保存并下一步</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      saveBtnDisabled:false,
    };
  },

    created(){

    },

  methods: {
    next() {
        //跳转到第二步
      this.$router.push({path:'/course/chapter/1'})
    },
  }
};
</script>

<style></style>
  1. 课程大纲页面 chapter.vue
.<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="2" process-status="wait" align-center style="margin-bottom: 40px;">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>
    <el-form label-width="120px">
      <el-form-item>
        <el-button @click="previous">上一步</el-button>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="next"
          >下一步</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      saveBtnDisabled: false,
    };
  },

    created() {
    
  },

  methods: {
    //跳转到上一步
    previous() {
      this.$router.push({ path: "/course/info/1" });
    },
    next() {
      //跳转到第三步
      this.$router.push({ path: "/course/publish/1" });
    },
  }
  
};
</script>

<style>
</style>
  1. 课程发布页面 publish.vue
.<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="3" process-status="wait" align-center style="margin-bottom: 40px;">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>
    <el-form label-width="120px">
      <el-form-item>
        <el-button @click="previous">返回修改</el-button>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="publish"
          >发布课程</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      saveBtnDisabled: false,
    };
  },

  methods: {
    //跳转到上一步
    previous() {    
      this.$router.push({ path: "/course/chapter/1" });
    },
    publish(){
      this.$router.push({ path: "/course/list" });
    }

  },

};
</script>

<style>
</style>

 
在这里插入图片描述
 
在这里插入图片描述
 
在这里插入图片描述
 


课程管理【编辑课程基本信息】

一、前端实现

1. 定义api

import request from '@/utils/request' // 引入已经封装好的axios 和 拦截器

export default {

    // 添加课程信息
    addCourseInfo(courseInfo) {
        return request({
            url: `/eduservice/course/addCourseInfo`,
            method: 'post', 
            data: courseInfo
          })
    }
}

2. 组件模板

<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="1" process-status="wait" align-center style="margin-bottom: 40px;">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>

    <el-form label-width="120px">
      <el-form-item label="课程标题">
        <el-input
          v-model="courseInfo.title"
          placeholder=" 示例:机器学习项目课:从基础到搭建项目视频课程。专业名称注意大小写"
        />
      </el-form-item>

      <!-- 所属分类 TODO -->

      <!-- 课程讲师 TODO -->

      <el-form-item label="总课时">
        <el-input-number
          :min="0"
          v-model="courseInfo.lessonNum"
          controls-position="right"
          placeholder="请填写课程的总课时数"
        />
      </el-form-item>

      <!-- 课程简介 TODO -->
      <el-form-item label="课程简介">
        <el-input v-model="courseInfo.description" placeholder="" />
      </el-form-item>

      <!-- 课程封面 TODO -->

      <el-form-item label="课程价格">
        <el-input-number
          :min="0"
          v-model="courseInfo.price"
          controls-position="right"
          placeholder="免费课程请设置为0元"
        />
        元
      </el-form-item>

      <el-form-item>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          @click="saveOrUpdate"
          >保存并下一步</el-button
        >
      </el-form-item>
    </el-form>
  </div>
</template>

<script>

import course from "@/api/edu/course"

export default {
  data() {
    return {
      saveBtnDisabled:false,
      courseInfo: {
        title: "",
          subjectId: "",
          teacherId: "",
          lessonNum: 0,
          description: "",
          cover: "",
          price: 0,
      },
    };
  },

    created(){

    },

  methods: {
    saveOrUpdate() {
        course.addCourseInfo(this.courseInfo)
            .then(response => {
                // 提示
                this.$message({
                    message: "添加课程信息成功",
                    type: "success",
                    })
                //跳转到第二步
                this.$router.push({ path: "/course/chapter/"+response.data.courseId})
            })
        
    }
  }
};
</script>

<style></style>

3. 测试

在这里插入图片描述
 
在这里插入图片描述

 
在这里插入图片描述
 


二、讲师下拉列表实现

1. 组件模板

src\views\edu\course\info.vue

      <!--课程讲师-->
    <el-form-item label="课程讲师">
        <el-select v-model="courseInfo.teacherId" placeholder="请选择">
            <el-option
                    v-for="teacher in teacherLists"
                    :key="teacher.id"
                    :label="teacher.name"
                    :value="teacher.id"
                    ></el-option>
        </el-select>
    </el-form-item>

2. 定义api

src\api\edu\course.js

   // 查询所有讲师
    getListTeacher(){
        return request({
            url:"/eduservice/edu-teacher/findAll",
            method: 'get',
        })
    }

3. 组件js

<script>

import course from "@/api/edu/course"

export default {
  data() {
    return {
      saveBtnDisabled:false,
      courseInfo: {
        title: "",
          subjectId: "",
          teacherId: "",
          lessonNum: 0,
          description: "",
          cover: "",
          price: 0,
      },
      teacherList:[] // 封装所有讲师数据
    };
  },

    created(){
        // 初始化所有讲师
        this.getListTeacher()
    },

  methods: {
    // 查询所有的讲师
    getListTeacher() {
        course.getListTeacher()
            .then(response => {
                this.teacherList = response.data.items
            })
    },

    saveOrUpdate() {
        course.addCourseInfo(this.courseInfo)
            .then(response => {
                // 提示
                this.$message({
                    message: "添加课程信息成功",
                    type: "success",
                    })
                //跳转到第二步
                this.$router.push({ path: "/course/chapter/"+response.data.courseId})
            })
        
    }
  }
};
</script>

4. 测试

在这里插入图片描述
 
在这里插入图片描述
 


三、课程分类多级联动的实现

1. 级联显示一级分类

  1. 组件数据定义

定义在 data 中:

subjectOneList: [], //封装所以一级分类数据
subjectTwoList: [], //封装二级分类数据    
  1. 组件模板

    <el-form-item label="课程分类">
        <el-select
           v-model="courseInfo.subjectParentId"
           placeholder="一级分类"
            @change="subjectLevelOneChanged">
                <el-option
                    v-for="subject in subjectOneList"
                    :key="subject.id"
                    :label="subject.title"
                    :value="subject.id"
                    ></el-option>
        </el-select>
    </el-form-item>
  2. 组件js

    <script>
    
    import course from "@/api/edu/course"
    import subject from "@/api/edu/subject"
    
    export default {
      data() {
               ........
          subjectOneList:[], // 一级分类
          subjectTwoList:[], // 二级分类
        };
      },
    
        created(){
            .....
            // 初始化一级分类
            this.getOneSubject()
        },
    
      methods: {
        // 查询所有的一级分类
        getOneSubject() {
            subject.getSubjectList() 
                .then(response => {
                    this.subjectOneList = response.data.list
                })
        },
        .......
            
        }
      }
    };
    </script>
  3. 测试

在这里插入图片描述
 


1. 级联显示二级分类

  1. 组件模板

    <el-select v-model="courseInfo.subjectId" placeholder="二级分类">
            <el-option
                   v-for="subject in subjectTwoList"
                   :key="subject.id"
                   :label="subject.title"
                   :value="subject.id"
                   ></el-option>
            </el-select>
  2. 注册change事件

在一级分类的 < el-select > 组件中注册 change 事件:
在这里插入图片描述

  1. 定义change事件方法

     // 点击某一级分类,触发change,显示对应的二级分类
        subjectLevelOneChanged(value) {
            // value就是一级分类的id值
            // 遍历所有的分类,包含一级和二级
            for  (let i = 0; i < this.subjectOneList.length; i ++ ) {
                // subjectOneList[i]: 每个一级分类
                // 判断:所有一级分类id 和 点击一级分类id是否一样
                if (this.subjectOneList[i].id === value) {
                    // 从一级分类获取里面所有的二级分类
                    this.subjectTwoList = this.subjectOneList[i].children;
                   // 把二级分类id值清空
                    this.courseInfo.subjectId = "";
                }
            }
            
        },
  2. 测试

在这里插入图片描述
 


四、课程封面

1. 定义data数据

BASE_API: process.env.BASE_API, // 接口API地址

2. 组件模板

<!-- 课程封面 -->
        <el-form-item label="课程封面">
            <el-upload
                    :show-file-list="false"
                    :on-success="handleAvatarSuccess"
                    :before-upload="beforeAvatarUpload"
                    :action="BASE_API + '/eduoss/fileoss'"
                    class="avatar-uploader"
                    >
                <img :src="courseInfo.cover" style="width: 200px; height: 150px"/>
            </el-upload>
        </el-form-item>

3. 结果回调

    // 上传封面成功调用的方法
    handleAvatarSuccess(res, file) {
        this.courseInfo.cover = res.data.url
    },

    // 上转之前调用的方法
    beforeAvatarUpload(file) {
        const isJPG = file.type === "image/jpeg";
        const isLt2M = file.size / 1024 / 1024 < 2;
        if (!isJPG) {
            this.$message.error("上传头像图片只能是 JPG 格式!");
        }
        if (!isLt2M) {
            this.$message.error("上传头像图片大小不能超过 2MB!");
        }
        return isJPG && isLt2M;
    },

4. 测试

在这里插入图片描述
 
在这里插入图片描述
 
 
创作不易,如果有帮助到你,请给文章==点个赞和收藏==,让更多的人看到!!!
==关注博主==不迷路,内容持续更新中。

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
8天前
|
SQL 安全 调度
网安预习课程笔记(九到十节)
本文是一篇关于渗透测试案例的分享,案例主要包括弱口令、SQL注入、内网渗透等多个环节,详细描述了渗透测试工作者如何通过一系列步骤获取不同服务器和系统的管理员权限。提到了一些关键的IP地址、数据库和服务器的发现,以及利用这些漏洞如何获取的权限。文章最后讨论了漏洞报告的组成部分,强调了报告的规范性和清晰度对于有效沟通和解决问题的重要性。 请注意,由于原始内容包含多个图片和链接,摘要中并未包含这些元素。
41 11
|
8天前
|
Cloud Native 分布式数据库 数据库
【PolarDB-X 从入门到精通】课程随堂互动获奖公告
【PolarDB-X 从入门到精通】课程随堂互动获奖名单出炉!快看看你是否在榜?
【PolarDB-X 从入门到精通】课程随堂互动获奖公告
|
10月前
|
传感器 监控 数据可视化
SDYY大学普通话考试报名系统说明文档
SDYY大学普通话考试报名系统说明文档
86 1
|
XML 存储 前端开发
谷粒学院——Day08【课程发布-课程大纲和课程发布】
谷粒学院——Day08【课程发布-课程大纲和课程发布】
118 0
谷粒学院——Day08【课程发布-课程大纲和课程发布】
|
前端开发 安全 JavaScript
谷粒学院——Day14【首页课程和名师功能】
谷粒学院——Day14【首页课程和名师功能】
317 0
谷粒学院——Day14【首页课程和名师功能】
|
存储 JSON 前端开发
谷粒学院——Day02【环境搭建和讲师管理接口开发】
谷粒学院——Day02【环境搭建和讲师管理接口开发】
259 0
谷粒学院——Day02【环境搭建和讲师管理接口开发】
|
存储 JavaScript 前端开发
谷粒学院——Day03【项目前端相关基础知识一】
谷粒学院——Day03【项目前端相关基础知识一】
75 0
谷粒学院——Day03【项目前端相关基础知识一】
|
编解码 JavaScript 前端开发
谷粒学院——Day04【项目前端相关基础知识二】
谷粒学院——Day04【项目前端相关基础知识二】
84 0
谷粒学院——Day04【项目前端相关基础知识二】
|
前端开发 API
谷粒学院(七)讲师列表前端实现(上)
谷粒学院(七)讲师列表前端实现
谷粒学院(七)讲师列表前端实现(上)
|
前端开发 JavaScript API
谷粒学院(七)讲师列表前端实现(下)
谷粒学院(七)讲师列表前端实现

热门文章

最新文章