开发者学堂课程【微服务+全栈在线教育实战项目演练(SpringCloud Alibaba+SpringBoot):课程管理-章节后端接口开发】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/667/detail/11383
课程管理-章节后端接口开发
内容介绍
一、本章介绍
二、章节添加页面部分
三、章节添加,删除和修改接口部分
一、本章介绍
首先做的第一个功能,将课程大纲列表中章节做操作,因为之前仅仅做了一个列表功能。章节有它的添加,删除和修改功能。接下来我们对章节的添加,删除和修改功能做实现。同时小节中也有添加,删除和修改功能,但仅仅对小节的删除和修改进行讲解,其他按照章节留给同学自行完成。接下来要完成的第一部分就是章节添加,删除和修改功能;
第二部分为即第三步最终发布。
也是课程信息的确认,即该图的效果,还有课程最终发布。将之前填写的课程信息做个显示,做个确认目录,最后点最终发布,课程就真正进行发布。
所以之后主要完成,第一部分章节小节的增加、修改和删除功能,第二部分课程信息确认和最终发布。
二、章节添加页面部分
首先做第一个功能,课程章节的添加,删除和修改功能。
1. 分析
为了效果明显,添加一条数据:标题为测试下午内容,一级分类为前端开发,二级分类为 vue,课程讲师为李四,总课时为10,课程简介为111,课程封面默认,课程价格为10。
点击保存并下一步,课程信息就添加进去。
到达第二步:创建课程大纲。
同时注意,当填写课程基本信息后,课程中还没有章节小节,因为还没有添加,上午显示内容是为了巩固创建课程大纲部分的内容,按照正确顺序,填写填写课程基本信息后,课程中没有章节小节。
而在第二步:创建课程大纲中要添加章节,首先应该有添加章节的按钮。我们将添加章节的按钮加在如图位置,这就是要做的第一部分内容。
接下来第二部分,当点击添加章节后要对应添加章节信息,需要在表单中进行添加,可以做成和之前添加讲师的效果,但在此采取使用弹框的方式,使得点击按钮后能够弹出一个页面,在弹出界面中输入章节的名称包括章节数据,最后点击保存作添加。这就是第二步,添加一个弹框,在弹框中输入课程信息最后保存。
2. 添加按钮
按照分析过程,对其做实现,首先添加一个添加章节的按钮或超链接。在此添加按钮,只需要赋值01-课程大纲列表中的按钮代码放在 chapter.vue 课程大纲页面代码12行即可。
l 代码如下:
<el-button type="text">添加章节</el-button>
添加代码后,刷新页面,就可以看见页面中显示了添加章节按钮。
3. 添加弹框
① 分析
之后再使得点击按钮能够弹出一个弹框,再在弹框中输入章节名称。打开 Element网站查找弹框如何实现。找到 MUI,进入页面点击组件部分的查看详情,查看其中关于弹框的组件 MessageBox 弹框,在其中查看是否为对应的弹框形式。
消息提示显然不是,因为我们需要能够输入的地方。
提交内容,显示的效果接近,打开源代码后发现它是直接写了一段JavaScript代码实现,该方法可实现,但一般更倾向于使用标签来实现。
l 代码如下:
<tesplate>
<el-button type="text " @click="open">点击打开Hessage Box</el-button>
</templat e>
<
script>
export default
{
methods :
{
open( ) i
this.$prompt ('请输入邮箱'
,
'提示',iconfirmButtonText :‘确定',
cancelButtonText :'取消',
inputPattern:/[w!#$88'*+/m?^_`K/)--]+(? \.[ \w I#$88'*+/日?_`{/--]+)*[P(?:[\w](?:[ \w-]*[\w])?入.)+[\w](?:[\
....
inputErrorHessage:'邮箱格式不正确‘
}).then((i value }) =>
{
this.$message(
{
type: 'success ',
message: ‘你的邮箱是:' + value
});
).catch(() =>ithis.$message(itype: 'info ',
message:‘取消输入′
});
});
}
}
}
<f s cript>
② 表单代码
所以再向下翻找到,Dialog 对话框,将其点开其中有各种内容,找到其中的自定义内容,点击效果展示,发现与所期望的一致,点击查看源码。
该部分代码为表格,并不需要所以删除即可。
<!-- Table -->
<el-button type="text" @click="dialogTableVisible = true">打开嵌套表格的 Dialog</el-button>
<el-dialog title="收货地址" :visible.sync="dialogTablevisible">
<el-table :data="gridData">
<el-table-column property="date" label="日期"width=*150"></el-table-column>
<el-table-column property="name" label="姓名" width="200"></el-table-column>
<el-table-column property="address" label="地址"></el-table-column>
</el-table>
</el-dialog>
查看表单部分如何实现即可。表单部分表示,当点击页面中的打开嵌套表单的Dialog 后会弹出弹框,就是我们需要在添加章节中做的内容。
l 代码如下:
<!-- Form -->
//在打开开嵌套表单的Dialog按钮button中有个@click事件,在事件中有个dialogFormVisible值为true
<el-button type="text" @click="dialogFormVisible = true">打开嵌套表单的 Dialog</el-button>
//el-dialog表示对话框,visible属性表示是否显示,显示的值为dialogFormVisible,所以当我们要显示弹框的代码结构和其一致。
<el-dialog title="收货地址” :visible.sync="dialogFormVisible">
//表示dialogFormVisible为true就弹出页面,为false就隐藏
//之后再添加el-form表单部分即可。
<el-form :model="form">
<el-form-item label="活动名称" :label-width="formLabelwidth">
<el-input v-model="form.name " autocomplete="off"></el-input>
</ el-form-item>
<el-form-item label="活动区域” :label-width="formLabelwidth">
<el-select v-model="form.region" placeholder="请选择活动区域
<el-option label="区域一" value="shanghai"></ el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item></el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormvisible = false">取消</el-button>
<el-button type="primary" @click="dialogFormvisible = false">确定</el-button>
</ div>
</el-dialog>
根据以上 Dialog 对话框的写法,直接在课件03-章节管理器阿奴单页面实现中的章节表单 diglog 复制表单代码到 chapter.vue 页面代码即可,不必自行编写。弹框代码位置任意,只要放在</div></div>标签中即可
l 代码如下:
<! --添加和修改章节表单-->
<el-dillog :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="o" 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>
③ 赋初始值
表单代码中的 dialogChapterFormVisible 为其在 data()中赋初值,默认为 false 表示章节弹框值。同时其中还有一个 v-model=chapter.title,将其中的 chapter 也定义,方便之后操作。
export default {
data() {
return {
saveBtnDisabled:false,
courseId: ' ',
//
课程
idchaptervideoList:[],
chapter:{//封装章节数据,其中的属性之后再完善
},
dialogChapterFormvisible:false
//
章节弹框
}
},
④ 显示弹框
之后让 dialogChapterFormVisible 为 ture 使得弹框出现。首先将 chaoter.vue13行代码,绑定弹框出现事件为 dialogChapterFormVisible为ture,在此也可以编写一个方法实现。
l 代码如下:
<el-button type="text" aclick="dialogChapterFormVisible=true">添加章节</el-button>
保存并运行代码后,此时的效果则为点击添加章节按钮时,dialogChapterFormVisible 值就会为 ture,弹框就会显示
⑤ 效果展示
运行代码之后,刷新页面,当点击添加章节时,就会出现添加章节的弹框。同时还能输入章节标题和设置排序。
假设此时输入章节标题为第一章,章节排序为1。点击确定之后数据就应该加在数据库中。
⑥ 小问题
在复制课程大纲页面代码时,要全部复制来覆盖原来页面包括最后代码的上一步下一步实现完整替换,否则页面样式会出现问题。
l 完整代码如下:
<el-button type="text”>添加章节</el-button>
<! --章节-->
//class为设置的样式
<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 classmchanpterList videoList">
<
li
v-form" 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>
到此页面效果就全部完成。最后编写接口部分,通过接口来实现功能。
三、开发章节添加,删除和修改接口部分
最后来写章节添加的接口,最终通过页面调用将功能实现出来。因为是章节所以先来到 EduChapterController 中,在课程大纲列表,根据课程 id 进行查询的代码下继续添加。再写个添加章节
1. 添加章节
l 代码如下:
//添加章节
//使用post提交方式,叫做addChapter
@PostMapping(" addChapter")
//编写方法addChapter(),章节中有许多数据,最后使用对象chapter进行提交,所以用@RequestBody通过路径获取eduChapter
public R addChapter(@RequestBody EduChapter eduChapter){
//调用chapterService中的save添加方法,传入参数eduChapter,完成添加操作。
chapterService. save(eduChapter) ;
//最后return
return R. ok() ;
}
以上就是添加的所有操作。
2. 根据章节 id 查询
l 代码如下:
//根据章节id查询
//get提交方式,重命名为getChapterInfo,传入章节id
@GetMapping("getChapterInfo/ {chapterId}")
//编写方法getChapterInfo的具体内容,同时使用@PathVariable注解,通过路径将章节id值在方法中取到
public R getChapterInfo(@PathVariable String chapterId){
//调chapterService中的方法-getById根据id查询,将chapterId传入,返回一个章节对象eduChapter
EduChapter eduChapter = chapterService. getById(chapterId);
//最后return,命名为chapter值为eduChapter。
return R. ok() .data("chapter" , eduChapter);
}
这是第二个根据 id 查询。
3.修改章节
是根据回显,再进行修改。修改与添加代码差不多,复制添加章节代码进行修改即可。
l 代码如下:
//修改章节
//使用post提交方式,叫做updateChapter
@PostMapping ("updateChapter")
//编写方法updateChapter(),用@RequestBody通过路径获取eduChapter
public R updateChapter(@RequestBody EduChapter eduChapter){
/调用chapterService中的updateById根据id查询方法
chapterService. updateById(eduChapter) ;
//返回一个值
return R.ok() ;
}
到此就快速将添加,修改和根据 id 查询给完成了。最后还有一个删除接口。
4.删除章节
在方法中调用chapterService中的删除方法时,有一些问题。来到页面,ctrl+f5 刷新。
假设此时要将第七章:I/O 流删除,因为章节之中有小节,所以直接删章节,小节的存在就毫无意义。所以,如果章节之中没有小节,可以直接删除;但如果章节中有小节时,该如果删除?
章节中有小节或一级分类下有二级分类,在开发时通常有两种实现方式。
第一种,在删除章节时,将其中所有的小节都删掉。
第二种,如果删除的章节中有小节,不让进行删除。
即当删除第七章:I/O 流时,其中有小节,则不让其进行删除。我们在此选用第二种方式,当有小节时选择不删。
当做到课程删除时,再采用第一种方式。当课程中有章节、小节、描述视频就都删掉。
l 代码如下:
//删除的方法
//使用delete提交,传入参数chapterId课程id值
@DeleteMapping(" {chapterId}")
//定义deleteChapter方法,在其中得到id
public R deleteChapter(@PathVariable String chapterId){
//添加一个Service中的deleteChapter方法删除,传入课程id。
chapterService.deleteChapter(chapterId);
//return一个值
return R.ok() ;
(1)首先创建方法
(2)定义方法
在 service 的 interface 中定义删除的方法。
l 代码如下:
void deleteChapter(String chapterId);
(3)EduChapterServiceImpl 实现类
创建后来到它的实现类中编写这个方法,思路为如果删除的章节中有小节,不让其进行删除。
如果没有小节才删除。首先根据章节 id 在小节表做个查询,如果有小节,则不删除,反之则删除。而小节表中有 chapter_id 字段,所以该字段进行查询是否有小节。
l 代码如下:
/
//
/删除章节的方法
@Override
public void deleteChapter(String chapterId){
//根据chapterid章节id查询小节表,如果查询数据,不进行删除
QueryWrapper<EduVideo> wrapper = new QueryWrapper<>(
)
;
//设置
wrapper
查询条件,根据查小节表的chapter_id与chapterId的值是否一致,来看其中有没有小节。
wrapper.eq( column:" chapter_id" , chapterId) ;
}
调用注入的 videoService 中的 count 方法,若使用 list 方法,写作:List<Eduvideo> list = videoService.list(wrapper);
由集合判断也可以实现,但是并不好。
因为此时想要查询的为章节下是否有小节,而并非想将小节的数据全部取到,所以使用 list 查出的小节数据并没有用处。所以使用 videoService的count (Wrapper<EduVideo> wrapper)方法最优,里面就只传了 wrapper 对象。
返回的int类型值表示根据条件能查出来几条记录。如此时要查出章节 id 为3的小节,有一条记录,返回值就是1;假设根据11章节 id 查小节,有两条记录,就是2。
所以 count 方法比较准确,不需得到数据,只得到值即可。
l 代码如下:
int count = videoService.count (wrapper) ;
最后做一个判断,如果 count 中值不为,0有数据,表示能查询出小节。那么不进行删除。else 表示 count 值为0,不能查询数据,进行删除。
l 代码如下:
//判断
if(count >0){V//查询出小节,不进行删除
//直接throw一个
GuliException
异常,状态码20001提示不能删除
throw new GuliException (20001,"不能删除");
}else { //不能查询数据,进行删除
//删除章节
,调用
baseMapper
中的
deleteById
()方法,同时传入章节id,最后赋值给result
lint result = baseMapper. deleteById(chapterId);
Ø 更改为布尔类型
为了方便判断,在删除的接口代码 EduChapterController.java 中添加 boolean 类型的返回值。同时添加一个判断,如果为 true,则 return R.ok();如果为 false,则return R.error()
l 代码如下:
//删除的方法
//使用delete提交,传入参数chapterId课程id值
@DeleteMapping(" {chapterId}")
//定义deleteChapter方法,在其中得到id
public R deleteChapter(@PathVariable String chapterId){
//添加boolean类型返回值
boolean flag = chapterService.deleteChapter(chapterId);
//同时添加一个判断,如果为true,则return R.ok();如果为false,则return R.error()
if(flag){
return R.ok() ;
}else{
return R.error();
}
包括 interface 中,即 EduChapterService.java 中的删除章节的方法,都变为返回boolean 类型
l 代码如下:
boolean deleteChapter(String chapterId);
实现类中也返回布尔类型
l 代码如下:
public
boolean
deleteChapter(String chapterId){
}
最后在删除方法的实现类中对最后的值 result 作个判断,因为 result 表示影响行数,所以如果成功删除值为1,删除失败值为0。所以大于0为成功,0为失败。
当删除成功 result为1,1>0为 true,则 return true;如果为失败,result为0,0>0为false,则 return false;
l 代码如下:
//返回值,表示查不出章节中的小节则删除
return result>0;
删除方法的整体思路就是,根据章节 id 查小节,count>就是有小节,不能删。如果等于0,就是没有小节,删除章节,最后返回false。
l 完整代码如下-删除的方法:
////
删除章节的方法
@Override
public boolean deleteChapter(String chapterId){
//根据chapterid章节id查询小节表,如果查询数据,不进行删除
QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();
//设置
wrapper
查询条件,根据查小节表的chapter_id与chapterId的值是否一致,来看其中有没有小节。
wrapper.eq( column: " chapter_id" , chapterId) ;
//调用注入的
videoService
中的count方法,返回的int类型值count表示根据条件能查出来几条记录
int count = videoService.count(wrapper);
//判断
if(count >0){
/
/查询出小节,不进行删除
//直接throw一个
GuliException
异常,状态码20001提示不能删除
throw new GuliException(20001,"不能删除");
} else {//不能查询数据,进行删除
//删除
章节
int result = baseMapper.deleteById(chapterId);
//成功1>0
0>0
return result>0;
}