day08【课程发布-课程大纲和课程发布】

1 课程大纲列表和章节管理

01-课程大纲列表显示

一、后端实现

1、定义vo

章节:ChapterVo

@Data

@ApiModel(value = "章节信息")

public class ChapterVo {

    private String id;

    private String title;

    //表示小节

    private List<VideoVo> children = new ArrayList<>();

}

小节:VideoVo

@Data

@ApiModel(value = "课时信息")

public class VideoVo {

    private String id;

    private String title;

}

2、服务层

接口

public interface EduChapterService extends IService<EduChapter> {

    //根据课程id进行查询章节小节

    List<ChapterVo> getChapterVideoByCourseId(String courseId);

}

实现

@Service

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

    //在Chapter里面不能查询Video,因此需要将Video的接口注入进来

    @Autowired

    private EduVideoService videoService;


    //根据课程id进行查询章节小节

    @Override

    public List<ChapterVo> getChapterVideoByCourseId(String courseId) {

        //1 根据课程id查询课程里面的所有章节EduChapter

        QueryWrapper<EduChapter> wrapperChapter = new QueryWrapper<>();

        wrapperChapter.eq("course_id",courseId);

        List<EduChapter> eduChapterList = baseMapper.selectList(wrapperChapter);


        //2 根据课程id查询课程里面的所有小节EduVideo

        QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>();

        wrapperVideo.eq("course_id",courseId);

        List<EduVideo> eduVideoList = videoService.list(wrapperVideo);


        //创建list集合,用于最终封装数据

        List<ChapterVo> finalList = new ArrayList<>();


        //3 遍历查询章节list集合进行封装

        //遍历查询章节list集合

        for (int i = 0; i < eduChapterList.size(); i++) {

            //每个章节

            EduChapter eduChapter = eduChapterList.get(i);

            //把eduChapter对象值复制到chapterVo里面

            ChapterVo chapterVo = new ChapterVo();

            BeanUtils.copyProperties(eduChapter,chapterVo);

            //把chapterVo放到最终list集合

            finalList.add(chapterVo);

            //创建集合,用于封装小节

            List<VideoVo> videoVoList = new ArrayList<>();


            //4 遍历查询小节list集合进行封装

            for (int m = 0; m < eduVideoList.size(); m++) {

                //得到每个小节

                EduVideo eduVideo = eduVideoList.get(m);

                //判断小节的chapter_id和章节里面的id是否一样

                if (eduVideo.getChapterId().equals(eduChapter.getId())){

                    VideoVo videoVo = new VideoVo();

                    BeanUtils.copyProperties(eduVideo,videoVo);

                    videoVoList.add(videoVo);

                }

            }

            //把封装的小节list集合放到章节对象chapterVo里面

            chapterVo.setChildren(videoVoList);

        }

        return finalList;

    }

}

3、web层

@Api(description="课程章节管理")

@RestController

@RequestMapping("/eduservice/chapter")

@CrossOrigin

public class EduChapterController {

    @Autowired

    EduChapterService chapterService;

    //课程大纲列表,查询课程下的章节和小节,根据课程id进行查询章节小节

    @ApiOperation(value = "嵌套章节数据列表")

    @GetMapping("getChapterVideo/{courseId}")

    public R getChapterVideo(@ApiParam(name = "courseId", value = "课程ID", required = true)

                              @PathVariable String courseId){

        List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId);

        return R.ok().data("allChapterVideo",list);

    }

}

4、Swagger测试

二、前端实现

1、定义api

chapter.js

import request from '@/utils/request'

export default{

    //1 根据课程id获取章节和小节数据列表

    getAllChapterVideo(courseId){

        return request({

            url:`/eduservice/chapter/getChapterVideo/${courseId}`,

            method:'get'

        })

    }

}

2、定义组件脚本

定义data

            courseId:'', // 所属课程

            chapterVideoList:[], // 章节嵌套课时列表

定义相关methods获取章节和课时列表

        //根据课程id查询章节和小节

        getChapterVideo(){

            chapter.getAllChapterVideo(this.courseId)

                .then(response=>{

                    this.chapterVideoList = response.data.allChapterVideo

                })

        },

获取路由的id

created(){

        //获取路由的id

        //判断里面如果有参数,并且是id参数,则获取

        if(this.$route.params && this.$route.params.id){

            this.courseId = this.$route.params.id

            //根据课程id查询章节和小节

            this.getChapterVideo()

        }

    },

3、定义组件模板

简单的无样式:

    <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>

含有样式:

<!-- 章节 -->

    <ul class="chanpterList">

        <li

            v-for="chapter in chapterVideoList"

            :key="chapter.id">

            <p>

                {{ chapter.title }}


                <span class="acts">

                    <el-button type="text">添加课时</el-button>

                    <el-button style="" type="text">编辑</el-button>

                    <el-button type="text">删除</el-button>

                </span>

            </p>


            <!-- 视频 -->

            <ul class="chanpterList videoList">

                <li

                    v-for="video in chapter.children"

                    :key="video.id">

                    <p>

                        {{ video.title }}

                        <span class="acts">

                            <el-button type="text">编辑</el-button>

                            <el-button type="text">删除</el-button>

                        </span>

                    </p>

                </li>

            </ul>

        </li>

    </ul>

    <div>

        <el-button @click="previous">上一步</el-button>

        <el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步</el-button>

    </div>

http://localhost:9528/#/course/chapter/14

4、定义样式

将样式的定义放在页面的最后

scope表示这里定义的样式只在当前页面范围内生效,不会污染到其他的页面

<style scoped>

    .chanpterList{

        position: relative;

        list-style: none;

        margin: 0;

        padding: 0;

    }

    .chanpterList li{

    position: relative;

    }

    .chanpterList p{

    float: left;

    font-size: 20px;

    margin: 10px 0;

    padding: 10px;

    height: 70px;

    line-height: 50px;

    width: 100%;

    border: 1px solid #DDD;

    }

    .chanpterList .acts {

        float: right;

        font-size: 14px;

    }

    .videoList{

    padding-left: 50px;

    }

    .videoList p{

    float: left;

    font-size: 14px;

    margin: 10px 0;

    padding: 10px;

    height: 50px;

    line-height: 30px;

    width: 100%;

    border: 1px dotted #DDD;

    }

</style>


02-章节管理后端接口开发

一、新增章节

web层

//新增章节

    @ApiOperation(value = "新增章节")

    @PostMapping("addChapter")

    public R addChapter(@RequestBody EduChapter eduChapter){

        chapterService.save(eduChapter);

        return R.ok();

    }

二、根据id查询

web层

//根据id查询

    @ApiOperation(value = "根据ID查询章节")

    @GetMapping("getChapterById/{id}")

    public R getChapterById(@ApiParam(name = "id", value = "章节ID", required = true)

                            @PathVariable String id){

        EduChapter eduChapter = chapterService.getById(id);

        return R.ok().data("chapter",eduChapter);

    }

三、更新

web层

    //根据ID修改章节

    @ApiOperation(value = "根据ID修改章节")

    @PostMapping("updateChapterById")

    public R updateChapterById( @ApiParam(name = "chapter", value = "章节对象", required = true)

                                @RequestBody EduChapter eduChapter){

        chapterService.updateById(eduChapter);

        return R.ok();

    }

四、删除

1、web层

    //根据ID删除章节

    //章节里面有小节需要先将小节删除

    @ApiOperation(value = "根据ID删除章节")

    @DeleteMapping("{chapterId}")

    public R deleteChapterById(@PathVariable String chapterId){

        boolean flag = chapterService.deleteChapter(chapterId);

        if (flag){

            return R.ok();

        }else {

            return R.error();

        }

    }

2、Service

EduChapterService层:接口

    //根据ID删除章节

    //章节里面有小节需要先将小节删除

    boolean deleteChapter(String chapterId);

ChapterService层:实现

    //删除章节的方法

    @Override

    public boolean deleteChapter(String chapterId) {

        //根据chapterId章节id 查询小节表,如果查询数据,不进行删除

        QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();

        wrapper.eq("chapter_id",chapterId);

        //返回的是集合,但是我们只需要判断章节里有没有小节

//        List<EduVideo> list = videoService.list(wrapper);

        int count = videoService.count(wrapper);

        //判断

        if (count > 0 ){//查询出小节,不进行删除

            throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");

        }else {

            //删除章节

            int result = baseMapper.deleteById(chapterId);

            //成功 1>0 0>0

            return result>0;

        }

    }

五、Swagger测试



03-章节管理前端页面实现

一、定义api

    //添加章节 传入章节对象chapter

    addChapter(chapter){

        return request({

            url:`/eduservice/chapter/addChapter`,

            method:'post',

            data:chapter

        })

    },

    //根据ID查询章节

    getChapter(chapterId){

        return request({

            url:`/eduservice/chapter/getChapterById/${chapterId}`,

            method:'get'

        })

    },

    //修改章节

    updateChapter(chapter){

        return request({

            url:`/eduservice/chapter/updateChapterById`,

            method:'post',

            data:chapter

        })

    },

    //删除章节

    deleteChapter(chapterId){

        return request({

            url:`/eduservice/chapter/${chapterId}`,

            method:'delete'

        })

    }

二、新增章节页面功能

1、定义data数据

            dialogChapterFormVisible:false,//设置默认弹框的值false(即不显示)

            chapter:{//章节对象

                title:'',

                sort:0

            },

2、添加章节按钮

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

3、章节表单dialog

<!-- 添加和修改章节表单 -->

    <el-dialog title="添加章节" :visible.sync="dialogChapterFormVisible">

        <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>

4、添加章节methods

        //添加章节

        saveOrUpdate(){

            chapter.addChapter(this.chapter)

                .then(response=>{

                    //关闭弹框

                    this.dialogChapterFormVisible = false

                    //提示信息

                    this.$message({

                        type:'success',

                        message:'添加章节成功'

                    });

                    //刷新页面  即把所有数据再查询一次

                    this.getChapterVideo()

                })

        },

执行了全局异常处理:说明后端代码有问题了

错误原因:chapter对象的实体类中有courseId没有值

        //添加章节

        saveOrUpdate(){

            //设置课程id到chapter对象里面

            //因为chapter对象的实体类中有courseId

            this.chapter.courseId = this.courseId

            chapter.addChapter(this.chapter)

                .then(response=>{

                    //关闭弹框

                    this.dialogChapterFormVisible = false

                    //提示信息

                    this.$message({

                        type:'success',

                        message:'添加章节成功'

                    });

                    //刷新页面 即把所有数据再查询一次

                    this.getChapterVideo()

                })

        },



问题:

解决方案:每次弹框的时候把表单数据清空

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

把添加章节中的值写到一个方法里面:

<el-button type="text"  @click="openChapterDialog()">添加章节</el-button>

通过方法进行弹框操作:

        //弹出添加章节页面

        openChapterDialog(){

            //弹框

            this.dialogChapterFormVisible = true

            //表单数据清空

            this.chapter.title =''

            this.chapter.sort = 0            

        },

三、修改章节信息

1、编辑章节按钮

<li

            v-for="chapter in chapterVideoList"

            :key="chapter.id">

            <p>

                {{ chapter.title }}                

                <span class="acts">

                    <el-button type="text">添加课时</el-button>

                    <!-- 绑定事件 -->

                    <!-- 修改数据的时候首先需要根据id查询进行数据回显 -->

                    <el-button style="" type="text" @click="openEditChapter(chapter.id)">编辑</el-button>

                    <el-button type="text">删除</el-button>

                </span>

            </p>

2、定义编辑方法

        //修改章节弹框、数据回显

        //方法中需要章节id,如chapterId

        openEditChapter(chapterId){

            //弹框

            this.dialogChapterFormVisible = true

            //调用接口

            chapter.getChapter(chapterId)

                .then(response=>{

                    this.chapter = response.data.chapter

                })

        },

3、定义更新方法

         //修改章节 与添加章节差不多

        updateChapter(){

            //设置课程id到chapter对象里面

            //因为chapter对象的实体类中有courseId

            this.chapter.courseId = this.courseId

            chapter.updateChapter(this.chapter)

                .then(response=>{

                    //关闭弹框

                    this.dialogChapterFormVisible = false

                    //提示信息

                    this.$message({

                        type:'success',

                        message:'修改章节成功'

                    });

                    //刷新页面 即把所有数据再查询一次

                    this.getChapterVideo()

                })                

        },

        saveOrUpdate(){

            if(!this.chapter.id){

                this.addChapter()

            }else{

                this.updateChapter()

            }

        },

四、删除章节

1、按钮

<el-button type="text" @click="removeChapter(chapter.id)">删除</el-button>

2、定义删除方法

        //删除章节

        removeChapter(chapterId){

            this.$confirm('此操作将删除章节, 是否继续?', '提示', {

                    confirmButtonText: '确定',

                    cancelButtonText: '取消',

                    type: 'warning'

                }).then(() => {//点击确定,执行then方法

                    //调用删除的方法

                    chapter.deleteChapter(chapterId)

                        .then(response=>{//删除成功            

                        //提示信息

                        this.$message({

                            type: 'success',

                            message: '删除成功!'

                        })

                        //刷新页面 即把所有数据再查询一次

                        this.getChapterVideo()             

                    })

                })//点击取消,执行catch方法

        },



2 课时(小节)管理

01-课时管理后端开发

EduVideoController.java

@RestController

@RequestMapping("/eduservice/video")

@CrossOrigin

public class EduVideoController {

    @Autowired

    private EduVideoService eduVideoService;


    //添加小节

    @ApiOperation(value = "添加小节")

    @PostMapping("addVideo")

    public R addVideo(@ApiParam(name = "videoForm", value = "课时对象", required = true)

                        @RequestBody EduVideo eduVideo){

        eduVideoService.save(eduVideo);

        return R.ok();

    }


    //删除小节

    //TODO 后面这个方法需要完善:删除小节的时候,同时把里面视频删除

    @DeleteMapping("deleteVideo/{id}")

    public R deleteVideo(@PathVariable String id){

        eduVideoService.removeById(id);

        return R.ok();

    }


    //根据id查询

    @ApiOperation(value = "根据ID查询小节")

    @GetMapping("getVideoById/{id}")

    public R getVideoById(String id){

        EduVideo eduVideo = eduVideoService.getById(id);

        return R.ok().data("video",eduVideo);

    }


    //修改小节

    @PostMapping("updateVideo")

    public R updateVideo(@RequestBody EduVideo eduVideo){

        eduVideoService.updateById(eduVideo);

        return R.ok();

    }

}

<el-button type="text" @click="openVideo(chapter.id)">添加小节</el-button>

添加和修改课时表单:课时表单dialog

    <!-- 添加和修改课时表单 -->

    <el-dialog :visible.sync="dialogVideoFormVisible" title="添加课时">

        <el-form :model="video" label-width="120px">

            <el-form-item label="课时标题">

                <el-input v-model="video.title"/>

            </el-form-item>

            <el-form-item label="课时排序">

                <el-input-number v-model="video.sort" :min="0" controls-position="right"/>

            </el-form-item>

            <el-form-item label="是否免费">

                <el-radio-group v-model="video.free">

                    <el-radio :label="true">免费</el-radio>

                    <el-radio :label="false">默认</el-radio>

                </el-radio-group>

            </el-form-item>

            <el-form-item label="上传视频">

                <!-- TODO -->

            </el-form-item>

        </el-form>

        <div slot="footer" class="dialog-footer">

            <el-button @click="dialogVideoFormVisible = false">取 消</el-button>

            <el-button :disabled="saveVideoBtnDisabled" type="primary" @click="saveOrUpdateVideo">确 定</el-button>

        </div>

    </el-dialog>

data

            // video对象的定义

            video:{

                title:'',

                sort:0,

                free:0,

                videoSourceId:''

            },

dialogVideoFormVisible:false

methods

         //添加小节弹框方法

        openVideo(chapterId){

            this.dialogVideoFormVisible = true

        },

测试:

http://localhost:9528/#/course/chapter/1281130194916139010

定义api

创建video.js

参考course.js 

import request from '@/utils/request'

export default{

    //添加小节

    addVideo(video){

        return request({

            url:`/eduservice/video/addVideo`,

            method:'post',

            data:video

        })

    },

    //根据ID查询小节

    getVideo(id){

        return request({

            url:`/eduservice/video/deleteVideo/${id}`,

            method:'get'

        })

    },

    //修改小节

    updateVideo(video){

        return request({

            url:`/eduservice/video/updateVideo`,

            method:'post',

            data:video

        })

    },

    //删除小节

    deleteVideo(id){

        return request({

            url:`/eduservice/chapter/deleteVideo/${id}`,

            method:'delete'

        })

    }

}

引入到chapter.vue

import video from'@/api/edu/video'

添加小节的时候需要课程id和章节id:

        //添加小节弹框方法

        openVideo(chapterId){

            //弹框

            this.dialogVideoFormVisible = true

            //添加小节的时候需要课程id和章节id

            //设置章节id

            this.video.chapterId = chapterId

            //设置课程id

            this.video.courseId = this.courseId

        },

添加小节方法:

        //添加小节

        addVideo(){

            video.addVideo(this.video)

                .then(response=>{

                    //关闭弹框

                    this.dialogVideoFormVisible = false

                    //提示信息

                    this.$message({

                        type:'success',

                        message:'添加小节成功'

                    });

                    //刷新页面 即把所有数据再查询一次

                    this.getChapterVideo()

                })

        },

       saveOrUpdateVideo(){

            this.addVideo()

        },

测试:

清空表单数据:

      //添加小节弹框方法

        openVideo(chapterId){

            //弹框

            this.dialogVideoFormVisible = true

            //添加小节的时候需要课程id和章节id

            //设置章节id

            this.video.chapterId = chapterId

            //设置课程id

            this.video.courseId = this.courseId

            //表单数据清空

            this.video.title = ''

            this.video.sort = 0

            this.video.free = 0

            this.videoSourceId = ''

        },

编辑:

<el-button type="text" @click="openEditVideo(video.id)">编辑</el-button>


        //小节弹框、数据回显

        //方法中需要小节id

        openEditVideo(videoId){

            //弹框

            this.dialogVideoFormVisible = true

            //调用接口

            video.getVideo(videoId)

                .then(response=>{

                    this.video = response.data.video

                })

        },

解决是否免费不回显问题:

修改与实体类里面对应:将所有free改为isFree

解决添加小节表单的是否免费没有默认值问题:


02-课时管理前端开发

删除课时

1、按钮

  <el-button type="text" @click="removeVideo(video.id)">删除</el-button>

2、定义删除方法

         //删除小节

        removeVideo(videoId){

            this.$confirm('此操作将删除小节, 是否继续?', '提示', {

                    confirmButtonText: '确定',

                    cancelButtonText: '取消',

                    type: 'warning'

                }).then(() => {//点击确定,执行then方法

                    //调用删除的方法

                    video.deleteVideo(videoId)

                        .then(response=>{//删除成功            

                        //提示信息

                        this.$message({

                            type: 'success',

                            message: '删除成功!'

                        })

                        //刷新页面 即把所有数据再查询一次

                        this.getChapterVideo()             

                    })

                })//点击取消,执行catch方法

        },

http://localhost:9528/#/course/chapter/1281130194916139010



3 课程最终发布

http://localhost:9528/#/course/publish/1281130194916139010

    可以根据课程id查询。

    把课程信息最终显示进行确认,但是需要查询多张表

    方法一:可以使用封装的方式,比如章节小节,一级二级分类。

          但是现在不太合适因为表比较多。

        在实际中,当查询的表比较多,或者要写一些复杂的查询操作,一般建议手写sql语句实现。

多表连接查询

    内连接:查询两张表有关联的数据,比如下表只能查询出前两条数据,运维没有关联,因此查询不出来

    左外连接(LEFT OUTER JOIN,OUTER可省略):左边表所有数据都查出来,右边表只查关联

    右外连接:右边表所有数据都查出来,左边表只查关联

         一般用的比较多的是内连接和左外连接。

        本案例中,特点:课程中课程没有简介,即简介为空;可能也没有分类,也没有讲师。所以使用内连接不太合适。因此使用左外连接比较合适。

         左外连接查询课程简介、课程讲师:

        课程分类:特点有两个ID(一个一级分类,一个二级分类),但是一级分类、二级分类都保存在edu_subject表中,如果一张表中有多个字段都同时关联同一张表,可以查询多次,可以先使用一级分类ID查询表关联出来,再使用二级分类ID查询关联出来,即查询两次。

根据课程id进行查询:

测试:

SELECT *

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 = es1.id

WHERE ec.id='1281130194916139010'

mapper:

        mapper中有接口和接口有对应的配置文件,在接口中可以定义方法,在配置文件中写sql语句。

        在配置文件中编写sql语句:

            在接口中编方法,根据课程id查询课程基本信息方法,这个方法需要编写一个返回对象(之前定义的对象都不太合适),专门进行封装。

02-课程最终发布后端

一、根据id查询课程发布信息

1、定义vo

CoursePublishVo

@Data

public class CoursePublishVo {

    private String id;

    private String title;

    private String cover;

    private Integer lessonNum;

    private String subjectLevelOne;

    private String subjectLevelTwo;

    private String teacherName;

    private String price;//只用于显示

}

2、数据访问层

在接口EduCourseMapper.java中编方法

public interface EduCourseMapper extends BaseMapper<EduCourse> {

    public CoursePublishVo getPublishCourseInfo(String courseId);

}

实现:EduCourseMapper.xml编写sql语句

        可通过select标签进行查询,属性一:id值为mapper接口中的方法名称,属性二:resultType返回值类型需要为包和类型的全路径。

        在select中编写sql语句,进行修改,需要与实体类进行对应:

        课程id是需要传递过来的,法一:where ec.id=#{courseId} 法二:where ec.id=${courseId},但是我们一般不用$,因为$表示字符串拼接,会产生sql注入问题。

<!-- sql语句:根据课程id查询课程确认信息 -->

    <select id="getPublishCourseInfo" resultType="com.atguigu.eduservice.entity.CoursePublishVo">

        SELECT ec.id,ec.price,ec.title,ec.cover,ec.lesson_num AS lessonNum,

                        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、web层

    //根据ID获取课程发布信息

    @ApiOperation(value = "根据ID获取课程发布信息")

    @GetMapping("getPublishCourseInfo/{courseId}")

    public R getPublishCourseInfo(@PathVariable String courseId){

        CoursePublishVo coursePublishVo = courseService.getPublishCourseInfo(courseId);

        return R.ok().data("publish",coursePublishVo);

    }

4、业务层

接口:

    //根据ID获取课程发布信息

    CoursePublishVo getPublishCourseInfo(String courseId);

实现:

    @Override

    public CoursePublishVo getPublishCourseInfo(String courseId) {

        return baseMapper.getPublishCourseInfo(courseId);

    }

        //调用mapper,在impl实现类中调用方法,可以使用baseMapper也可以写this,但是调用我们自己写的mapper,就必须写baseMapper才能调到。

        通过controller调service,然后service用baseMapper调mapper中的方法,方法会最终执行到sql语句,得到数据。

测试:报告异常“执行了全局异常处理”

BinDing : 数据绑定异常。

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

导致这个错误的两个原因:一 看mapper中的方法名......是否写错;二 由于maven默认的加载机制造成的问题,maven加载时候,把java文件夹里面. java类型文件进行编译,如果其他类型文件,不会加载。

问题分析:

        dao层编译后只有class文件,没有mapper.xml,因为maven工程在默认情况下src/main/java目录下的所有资源文件是不发布到target目录下的,

解决方案:

1、在guli_edu的pom中配置如下节点

 <include>**/*.xml</include>

*表示加载的是一级目录

**表示多级目录,在xml前面有很多级目录

<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->

<build>

        <resources>

            <resource>

                <directory>src/main/java</directory>

                <includes>

                    <include>**/*.xml</include>

                </includes>

                <filtering>false</filtering>

            </resource>

        </resources>

    </build>

2、在Spring Boot配置文件中添加配置(service_edu的application.properties)

#配置mapper xml文件的路径

mybatis-plus.mapper-locations=classpath:com/atguigu/eduservice/mapper/xml/*.xml

classpath表示类路径:src里面的内容

重新启动,在swagger中测试:

courseId:1281130194916139010


01-课程最终发布前端

一、前端代码

发布页面课程信息显示:

1、定义api

分析这个页面一共有两个远程方法:一个是根据课程id获取课程基本预览信息,第二个是发布课程

/api/edu/course.js

//发布课程

    getPublicCourseInfo(courseId){

        return request({

            url:`/eduservice/course/getPublishCourseInfo/${courseId}`,

            method:'get'

        })

    }

2、定义数据模型

/edu/course/publish.vue

 data(){

        return {

            saveBtnDisabled: false, // 保存按钮是否禁用

            courseId:'',// 所属课程

            coursePublish:{}

        }

    },

3、组件方法定义

在publish.vue中引入js文件

import course from '@/api/edu/course'

得到路由中的id值:

created(){

      if(this.$route.params && this.$route.params.id){

        this.courseId = this.$route.params.id

      }

    },

调用接口中的方法:

created(){

      if(this.$route.params && this.$route.params.id){

        this.courseId = this.$route.params.id

        // 根据id获取课程基本信息

        this.getCoursePublishId()

      }

    },

编写获取数据的方法:

 //根据课程id查询

        getCoursePublishId(){

          course.getPublicCourseInfo(this.courseId)

            .then(response=>{

              this.coursePublish = response.data.publish

            })

        }

4、组件模板

    把内容在页面中进行显示,获取publishCourse对象里面的值,通过{{}}。

    <div class="ccInfo">

      <img :src="coursePublish.cover">

      <div class="main">

        <h2>{{ coursePublish.title }}</h2>

        <p class="gray"><span>共{{ coursePublish.lessonNum }}课时</span></p>

        <p><span>所属分类:{{ coursePublish.subjectLevelOne }} — {{ coursePublish.subjectLevelTwo }}</span></p>

        <p>课程讲师:{{ coursePublish.teacherName }}</p>

        <h3 class="red">¥{{ coursePublish.price }}</h3>

      </div>

    </div>

    <div>

              <el-button @click="previous">返回修改</el-button>

              <el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button>

    </div>

5、css样式

<style scoped>

.ccInfo {

    background: #f5f5f5;

    padding: 20px;

    overflow: hidden;

    border: 1px dashed #DDD;

    margin-bottom: 40px;

    position: relative;

}

.ccInfo img {

    background: #d6d6d6;

    width: 500px;

    height: 278px;

    display: block;

    float: left;

    border: none;

}

.ccInfo .main {

    margin-left: 520px;

}

.ccInfo .main h2 {

    font-size: 28px;

    margin-bottom: 30px;

    line-height: 1;

    font-weight: normal;

}

.ccInfo .main p {

    margin-bottom: 10px;

    word-wrap: break-word;

    line-height: 24px;

    max-height: 48px;

    overflow: hidden;

}

.ccInfo .main p {

    margin-bottom: 10px;

    word-wrap: break-word;

    line-height: 24px;

    max-height: 48px;

    overflow: hidden;

}

.ccInfo .main h3 {

    left: 540px;

    bottom: 20px;

    line-height: 1;

    font-size: 28px;

    color: #d32f24;

    font-weight: normal;

    position: absolute;

}

</style>

http://localhost:9528/#/course/publish/1281130194916139010


发布课程(修改课程状态):

一、根据id发布课程

1、web层

//根据id发布课程

    //课程最终发布

    //修改课程状态

    @ApiOperation(value = "根据id发布课程")

    @PostMapping("publishCourse/{courseId}")

    public R publishCourse(@PathVariable String courseId){

        EduCourse eduCourse = new EduCourse();

        eduCourse.setId(courseId);

        eduCourse.setStatus("Normal");//设置课程发布状态

        courseService.updateById(eduCourse);

        return R.ok();

    }

二、前端

1、定义api

分析这个页面一共有两个远程方法:一个是根基课程id获取课程基本预览信息,第二个是发布课程

/api/edu/course.js

 //课程最终发布

    publicCourseInfo(courseId){

        return request({

            url:`/eduservice/course/publishCourse/${courseId}`,

            method:'post'

        })

    }

2、组件方法定义

发布课程的点击事件

publish(){

              this.$confirm('发布课程, 是否继续?', '提示', {

                    confirmButtonText: '继续',

                    cancelButtonText: '取消',

                    type: 'warning'

                }).then(() => {//点击继续,执行then方法

                    course.publicCourseInfo(this.courseId)

                      .then(response=>{

                        //提示信息

                        this.$message({

                            type: 'success',

                            message: '发布成功!'

                        })

                        //跳转到第二步

                        this.$router.push({path:'/course/list'})

                      })                  

                })//点击取消,执行catch方法

        },

测试:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。