SpringBoot在线教育项目(八)

一、课程大纲列表显示

一、后端实现

1、定义vo
ChapterVo

package com.guli.edu.vo;
@ApiModel(value = "章节信息")
@Data
public class ChapterVo implements Serializable {
​
    private static final long serialVersionUID = 1L;
​
    private String id;
    private String title;
    private List<VideoVo> children = new ArrayList<>();
}

VideoVo

package com.guli.edu.vo;
@ApiModel(value = "课时信息")
@Data
public class VideoVo implements Serializable {
​
    private static final long serialVersionUID = 1L;
​
    private String id;
    private String title;
    private Boolean free;
}

2、服务层
接口

package com.guli.edu.service;
public interface ChapterService extends IService<Chapter> {
    List<ChapterVo> nestedList(String courseId);
}

实现

package com.guli.edu.service.impl;
​
@Service
public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter> implements ChapterService {
​
    @Autowired
    private VideoService videoService;
​
    @Override
    public List<ChapterVo> nestedList(String courseId) {
​
        //最终要的到的数据列表
        ArrayList<ChapterVo> chapterVoArrayList = new ArrayList<>();
​
        //获取章节信息
        QueryWrapper<Chapter> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("course_id", courseId);
        queryWrapper1.orderByAsc("sort", "id");
        List<Chapter> chapters = baseMapper.selectList(queryWrapper1);
​
        //获取课时信息
        QueryWrapper<Video> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("course_id", courseId);
        queryWrapper2.orderByAsc("sort", "id");
        List<Video> videos = videoService.list(queryWrapper2);
​
        //填充章节vo数据
        int count1 = chapters.size();
        for (int i = 0; i < count1; i++) {
            Chapter chapter = chapters.get(i);
​
            //创建章节vo对象
            ChapterVo chapterVo = new ChapterVo();
            BeanUtils.copyProperties(chapter, chapterVo);
            chapterVoArrayList.add(chapterVo);
​
            //填充课时vo数据
            ArrayList<VideoVo> videoVoArrayList = new ArrayList<>();
            int count2 = videos.size();
            for (int j = 0; j < count2; j++) {
​
                Video video = videos.get(j);
                if(chapter.getId().equals(video.getChapterId())){
​
                    //创建课时vo对象
                    VideoVo videoVo = new VideoVo();
                    BeanUtils.copyProperties(video, videoVo);
                    videoVoArrayList.add(videoVo);
                }
            }
            chapterVo.setChildren(videoVoArrayList);
        }
​
        return chapterVoArrayList;
    }
}

3、web层

package com.guli.edu.controller.admin;
​
@Api(description="课程章节管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/chapter")
public class ChapterAdminController {
​
    @Autowired
    private ChapterService chapterService;
    
    @ApiOperation(value = "嵌套章节数据列表")
    @GetMapping("nested-list/{courseId}")
    public R nestedListByCourseId(
            @ApiParam(name = "courseId", value = "课程ID", required = true)
            @PathVariable String courseId){
​
        List<ChapterVo> chapterVoList = chapterService.nestedList(courseId);
        return R.ok().data("items", chapterVoList);
    }
}

4、Swagger测试

二、前端实现

1、定义api
chapter.js

import request from '@/utils/request'
​
const api_name = '/admin/edu/chapter'
​
export default {
​
  getNestedTreeList(courseId) {
    return request({
      url: `${api_name}/nested-list/${courseId}`,
      method: 'get'
    })
  }
}

2、定义组件脚本
定义data

courseId: '', // 所属课程
chapterNestedList: [] // 章节嵌套课时列表

created中调用init方法

created() {
    console.log('chapter created')
    this.init()
},

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

init() {
  if (this.$route.params && this.$route.params.id) {
    this.courseId = this.$route.params.id
    // 根据id获取课程基本信息
    this.fetchChapterNestedListByCourseId()
  }
},
​
fetchChapterNestedListByCourseId() {
  chapter.getNestedTreeList(this.courseId).then(response => {
    this.chapterNestedList = response.data.items
  })
},

3、定义组件模板

<el-button type="text">添加章节</el-button>
<!-- 章节 -->
<ul class="chanpterList">
    <li
        v-for="chapter in chapterNestedList"
        :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>

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>

二、章节管理后端接口开发

一、新增章节

web层

@ApiOperation(value = "新增章节")
@PostMapping
public R save(
    @ApiParam(name = "chapterVo", value = "章节对象", required = true)
    @RequestBody Chapter chapter){
​
    chapterService.save(chapter);
    return R.ok();
}

二、根据id查询

web层

@ApiOperation(value = "根据ID查询章节")
@GetMapping("{id}")
public R getById(
    @ApiParam(name = "id", value = "章节ID", required = true)
    @PathVariable String id){
​
    Chapter chapter = chapterService.getById(id);
    return R.ok().data("item", chapter);
}

三、更新

web层

@ApiOperation(value = "根据ID修改章节")
@PutMapping("{id}")
public R updateById(
    @ApiParam(name = "id", value = "章节ID", required = true)
    @PathVariable String id,
​
    @ApiParam(name = "chapter", value = "章节对象", required = true)
    @RequestBody Chapter chapter){
​
    chapter.setId(id);
    chapterService.updateById(chapter);
    return R.ok();
}

四、删除

1、web层

@ApiOperation(value = "根据ID删除章节")
@DeleteMapping("{id}")
public R removeById(
    @ApiParam(name = "id", value = "章节ID", required = true)
    @PathVariable String id){
​
    boolean result = chapterService.removeChapterById(id);
    if(result){
        return R.ok();
    }else{
        return R.error().message("删除失败");
    }
}

2、Service
ChapterService层:接口

boolean removeChapterById(String id);

ChapterService层:实现

@Override
public boolean removeChapterById(String id) {
​
    //根据id查询是否存在视频,如果有则提示用户尚有子节点
    if(videoService.getCountByChapterId(id)){
        throw new GuliException(20001,"该分章节下存在视频课程,请先删除视频课程");
    }
​
    Integer result = baseMapper.deleteById(id);
    return null != result && result > 0;
}

VideoService:接口

boolean getCountByChapterId(String chapterId);

VideoService:实现

@Override
public boolean getCountByChapterId(String chapterId) {
    QueryWrapper<Video> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("chapter_id", chapterId);
    Integer count = baseMapper.selectCount(queryWrapper);
    return null != count && count > 0;
}

五、Swagger测试

三、章节管理前端页面实现

一、定义api

  removeById(id) {
    return request({
      url: `${api_name}/${id}`,
      method: 'delete'
    })
  },
​
  save(chapter) {
    return request({
      url: api_name,
      method: 'post',
      data: chapter
    })
  },
​
  getById(id) {
    return request({
      url: `${api_name}/${id}`,
      method: 'get'
    })
  },
​
  updateById(chapter) {
    return request({
      url: `${api_name}/${chapter.id}`,
      method: 'put',
      data: chapter
    })
  }

二、新增章节页面功能

1、定义data数据

dialogChapterFormVisible: false, //是否显示章节表单
chapter: {// 章节对象
  title: '',
  sort: 0
}

2、添加章节按钮

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

3、章节表单dialog

<!-- 添加和修改章节表单 -->
<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>

4、添加章节methods

saveOrUpdate() {
  this.saveBtnDisabled = true
  if (!this.chapter.id) {
    this.saveData()
  } else {
    this.updateData()
  }
},
​
saveData() {
  this.chapter.courseId = this.courseId
  chapter.save(this.chapter).then(response => {
    this.$message({
      type: 'success',
      message: '保存成功!'
    })
    this.helpSave()
  }).catch((response) => {
    this.$message({
      type: 'error',
      message: response.message
    })
  })
},
​
updateData() {
​
},
    
helpSave(){
  this.dialogChapterFormVisible = false// 如果保存成功则关闭对话框
  this.fetchChapterNestedListByCourseId()// 刷新列表
  this.chapter.title = ''// 重置章节标题
  this.chapter.sort = 0// 重置章节标题
  this.saveBtnDisabled = false
},

三、修改章节信息

1、编辑章节按钮

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

2、定义编辑方法

editChapter(chapterId) {
    this.dialogChapterFormVisible = true
    chapter.getById(chapterId).then(response => {
        this.chapter = response.data.item
    })
},

3、定义更新方法

updateData() {
  chapter.updateById(this.chapter).then(response => {
    this.$message({
      type: 'success',
      message: '修改成功!'
    })
    this.helpSave()
  }).catch((response) => {
    // console.log(response)
    this.$message({
      type: 'error',
      message: response.message
    })
  })
},

四、删除章节

1、按钮

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

2、定义删除方法

removeChapter(chapterId) {
  this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    return chapter.removeById(chapterId)
  }).then(() => {
    this.fetchChapterNestedListByCourseId()// 刷新列表
    this.$message({
      type: 'success',
      message: '删除成功!'
    })
  }).catch((response) => { // 失败
    if (response === 'cancel') {
      this.$message({
        type: 'info',
        message: '已取消删除'
      })
    } else {
      this.$message({
        type: 'error',
        message: response.message
      })
    }
  })
},

四、课时管理后端开发

一、定义Form表单对象

VideoInfoForm.java

package com.guli.edu.form;
​
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
​
/**
 * @author helen
 * @since 2019/3/5
 */
@ApiModel(value = "课时基本信息", description = "编辑课时基本信息的表单对象")
@Data
public class VideoInfoForm {
​
    @ApiModelProperty(value = "视频ID")
    private String id;
​
    @ApiModelProperty(value = "节点名称")
    private String title;
​
    @ApiModelProperty(value = "课程ID")
    private String courseId;
​
    @ApiModelProperty(value = "章节ID")
    private String chapterId;
​
    @ApiModelProperty(value = "视频资源")
    private String videoSourceId;
​
    @ApiModelProperty(value = "显示排序")
    private Integer sort;
    
    @ApiModelProperty(value = "是否可以试听:0默认 1免费")
    private Boolean free;
}

二、课时保存

1、web层接口的定义
VideoAdminController.java

package com.guli.edu.controller.admin;
​
@Api(description="课时管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/admin/edu/video")
public class VideoAdminController {
​
    @Autowired
    private VideoService videoService;
​
    @ApiOperation(value = "新增课时")
    @PostMapping("save-video-info")
    public R save(
            @ApiParam(name = "videoForm", value = "课时对象", required = true)
            @RequestBody VideoInfoForm videoInfoForm){
​
        videoService.saveVideoInfo(videoInfoForm);
        return R.ok();
    }
}

2、业务层
VideoService.java

void saveVideoInfo(VideoInfoForm videoInfoForm);

VideoServiceImpl.java

@Override
public void saveVideoInfo(VideoInfoForm videoInfoForm) {
​
    Video video = new Video();
    BeanUtils.copyProperties(videoInfoForm, video);
    boolean result = this.save(video);
​
    if(!result){
        throw new GuliException(20001, "课时信息保存失败");
    }
}

三、课时的修改

1、web层接口的定义
VideoAdminController.java

@ApiOperation(value = "根据ID查询课时")
@GetMapping("video-info/{id}")
public R getVideInfoById(
    @ApiParam(name = "id", value = "课时ID", required = true)
    @PathVariable String id){
​
    VideoInfoForm videoInfoForm = videoService.getVideoInfoFormById(id);
    return R.ok().data("item", videoInfoForm);
}
​
@ApiOperation(value = "更新课时")
@PutMapping("update-video-info/{id}")
public R updateCourseInfoById(
    @ApiParam(name = "VideoInfoForm", value = "课时基本信息", required = true)
    @RequestBody VideoInfoForm videoInfoForm,
​
    @ApiParam(name = "id", value = "课时ID", required = true)
    @PathVariable String id){
​
    videoService.updateVideoInfoById(videoInfoForm);
    return R.ok();
}

2、业务层
VideoService.java

VideoInfoForm getVideoInfoFormById(String id);
​
void updateVideoInfoById(VideoInfoForm videoInfoForm);

VideoServiceImpl.java

@Override
public VideoInfoForm getVideoInfoFormById(String id) {
    //从video表中取数据
    Video video = this.getById(id);
    if(video == null){
        throw new GuliException(20001, "数据不存在");
    }
​
    //创建videoInfoForm对象
    VideoInfoForm videoInfoForm = new VideoInfoForm();
    BeanUtils.copyProperties(video, videoInfoForm);
​
    return videoInfoForm;
}
​
@Override
public void updateVideoInfoById(VideoInfoForm videoInfoForm) {
    //保存课时基本信息
    Video video = new Video();
    BeanUtils.copyProperties(videoInfoForm, video);
    boolean result = this.updateById(video);
    if(!result){
        throw new GuliException(20001, "课时信息保存失败");
    }
}

四、课时的删除

1、web层接口的定义
VideoAdminController.java

@ApiOperation(value = "根据ID删除课时")
@DeleteMapping("{id}")
public R removeById(
    @ApiParam(name = "id", value = "课时ID", required = true)
    @PathVariable String id){
​
    boolean result = videoService.removeVideoById(id);
    if(result){
        return R.ok();
    }else{
        return R.error().message("删除失败");
    }
}

2、业务层
VideoService.java

boolean removeVideoById(String id);

VideoServiceImpl.java

@Override
public boolean removeVideoById(String id) {
​
    //删除视频资源 TODO
​
    Integer result = baseMapper.deleteById(id);
    return null != result && result > 0;
}

五、课时管理前端开发

一、定义api

创建video.js
参考course.js

import request from '@/utils/request'
​
const api_name = '/admin/edu/video'
​
export default {
​
  saveVideoInfo(videoInfo) {
    return request({
      url: `${api_name}/save-video-info`,
      method: 'post',
      data: videoInfo
    })
  },
​
  getVideoInfoById(id) {
    return request({
      url: `${api_name}/video-info/${id}`,
      method: 'get'
    })
  },
​
  updateVideoInfoById(videoInfo) {
    return request({
      url: `${api_name}/update-video-info/${videoInfo.id}`,
      method: 'put',
      data: videoInfo
    })
  },
​
  removeById(id) {
    return request({
      url: `${api_name}/${id}`,
      method: 'delete'
    })
  }
}

二、新增课时页面功能

1、定义data数据

saveVideoBtnDisabled: false, // 课时按钮是否禁用
dialogVideoFormVisible: false, // 是否显示课时表单
chapterId: '', // 课时所在的章节id
video: {// 课时对象
  title: '',
  sort: 0,
  free: 0,
  videoSourceId: ''
},

2、添加课时按钮

<el-button type="text" @click="dialogVideoFormVisible = true; chapterId = chapter.id">添加课时</el-button>

3、课时表单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>

4、添加课时methods
引入video模块

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

方法的定义

saveOrUpdateVideo() {
  this.saveVideoBtnDisabled = true
  if (!this.video.id) {
    this.saveDataVideo()
  } else {
    this.updateDataVideo()
  }
},
​
saveDataVideo() {
  this.video.courseId = this.courseId
  this.video.chapterId = this.chapterId
  video.saveVideoInfo(this.video).then(response => {
    this.$message({
      type: 'success',
      message: '保存成功!'
    })
    this.helpSaveVideo()
  })
},
​
updateDataVideo() {
​
},
​
helpSaveVideo() {
  this.dialogVideoFormVisible = false// 如果保存成功则关闭对话框
  this.fetchChapterNestedListByCourseId()// 刷新列表
  this.video.title = ''// 重置章节标题
  this.video.sort = 0// 重置章节标题
  this.video.videoSourceId = ''// 重置视频资源id
  this.saveVideoBtnDisabled = false
},

三、修改课时信息

1、编辑课时按钮

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

2、定义编辑方法

editVideo(videoId) {
  this.dialogVideoFormVisible = true
  video.getVideoInfoById(videoId).then(response => {
    this.video = response.data.item
  })
},

3、定义更新方法

updateDataVideo() {
  video.updateVideoInfoById(this.video).then(response => {
    this.$message({
      type: 'success',
      message: '修改成功!'
    })
    this.helpSaveVideo()
  })
},

四、删除课时

1、按钮

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

2、定义删除方法

removeVideo(videoId) {
  this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    return video.removeById(videoId)
  }).then(() => {
    this.fetchChapterNestedListByCourseId()// 刷新列表
    this.$message({
      type: 'success',
      message: '删除成功!'
    })
  }).catch((response) => { // 失败
    if (response === 'cancel') {
      this.$message({
        type: 'info',
        message: '已取消删除'
      })
    }
  })
}

六、课程最终发布前端

一、前端代码

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

getCoursePublishInfoById(id) {
  return request({
    url: `${api_name}/course-publish-info/${id}`,
    method: 'get'
  })
},
​
publishCourse(id) {
  return request({
    url: `${api_name}/publish-course/${id}`,
    method: 'put'
  })
}

2、定义数据模型

data() {
    return {
        saveBtnDisabled: false, // 保存按钮是否禁用
        courseId: '', // 所属课程
        coursePublish: {}
    }
},

3、完善步骤导航
edu/course/chapter.js

previous() {
  console.log('previous')
  this.$router.push({ path: '/edu/course/info/' + this.courseId })
},
​
next() {
  console.log('next')
  this.$router.push({ path: '/edu/course/publish/' + this.courseId })
}

edu/course/pubish.js

<div>
    <el-button @click="previous">返回修改</el-button>
    <el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button>
</div>
previous() {
  console.log('previous')
  this.$router.push({ path: '/edu/course/chapter/' + this.courseId  })
},
​
publish() {
  console.log('publish')
  course.publishCourse(this.courseId).then(response => {
    this.$router.push({ path: '/edu/course/list' })
  })
}

4、组件方法定义
import

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

created

created() {
    console.log('chapter created')
    this.init()
},

获取数据的方法

init() {
  if (this.$route.params && this.$route.params.id) {
    this.courseId = this.$route.params.id
    // 根据id获取课程基本信息
    this.fetchCoursePublishInfoById()
  }
},
​
fetchCoursePublishInfoById() {
  course.getCoursePublishInfoById(this.courseId).then(response => {
    this.coursePublish = response.data.item
  })
},

5、组件模板

<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>
​
    <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>
  </div>
</template>

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

七、课程最终发布后端

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

方式一:业务层组装多个表多次的查询结果
方式二:数据访问层进行关联查询
我们使用第二种方式实现

1、定义vo

package com.guli.edu.vo;
​
@ApiModel(value = "课程发布信息")
@Data
public class CoursePublishVo  implements Serializable {
​
    private static final long serialVersionUID = 1L;
​
    private String title;
    private String cover;
    private Integer lessonNum;
    private String subjectLevelOne;
    private String subjectLevelTwo;
    private String teacherName;
    private String price;//只用于显示
}

2、数据访问层
接口:CourseMapper.java

package com.guli.edu.mapper;
public interface CourseMapper extends BaseMapper<Course> {
    CoursePublishVo selectCoursePublishVoById(String id);
}

实现:CourseMapper.xml

<select id="getCoursePublishVoById" resultType="com.guli.edu.vo.CoursePublishVo">
    SELECT
        c.title,
        c.cover,
        c.lesson_num AS lessonNum,
        CONVERT(c.price, DECIMAL(8,2)) AS price,
        s1.title AS subjectLevelOne,
        s2.title AS subjectLevelTwo,
        t.name AS teacherName
    FROM
        edu_course c
        LEFT JOIN edu_teacher t ON c.teacher_id = t.id
        LEFT JOIN edu_subject s1 ON c.subject_parent_id = s1.id
        LEFT JOIN edu_subject s2 ON c.subject_id = s2.id
    WHERE
        c.id = #{id}
</select>

3、业务层
接口:CourseService.java

CoursePublishVo getCoursePublishVoById(String id);

实现:CourseServiceImpl.java

@Override
public CoursePublishVo getCoursePublishVoById(String id) {
    return baseMapper.getCoursePublishVoById(id);
}

4、web层

@ApiOperation(value = "根据ID获取课程发布信息")
@GetMapping("course-publish-info/{id}")
public R getCoursePublishVoById(
    @ApiParam(name = "id", value = "课程ID", required = true)
    @PathVariable String id){
​
    CoursePublishVo courseInfoForm = courseService.getCoursePublishVoById(id);
    return R.ok().data("item", courseInfoForm);
}

测试:报告异常
AbstractHandlerExceptionResolver.java:194 |org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver |Resolved exception caused by handler execution: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.guli.edu.mapper.CourseMapper.getCoursePublishVoById

问题分析:
dao层编译后只有class文件,没有mapper.xml,因为maven工程在默认情况下src/main/java目录下的所有资源文件是不发布到target目录下的,
在这里插入图片描述

解决方案:
1、在guli_edu的pom中配置如下节点

<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

重新打包项目会发现target目录下出现了xml文件夹

2、在Spring Boot配置文件中添加配置

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

二、根据id发布课程

1、web层

@ApiOperation(value = "根据id发布课程")
@PutMapping("publish-course/{id}")
public R publishCourseById(
    @ApiParam(name = "id", value = "课程ID", required = true)
    @PathVariable String id){
​
    courseService.publishCourseById(id);
    return R.ok();
}

2、service层
接口

void publishCourseById(String id);

实现

@Override
public boolean publishCourseById(String id) {
    Course course = new Course();
    course.setId(id);
    course.setStatus(Course.COURSE_NORMAL);
    Integer count = baseMapper.updateById(course);
    return null != count && count > 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值