携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
1.问题引入
今天在学习尚硅谷的一个项目的时候,有一个接口需要封装数据,再返回给前端同学,过程个人觉得有点复杂,在此记录下自己的思考,欢迎各位同学批评指教。
这个接口的要求是查询课程的大纲,即需要查找课程的章节以及每个章节的每个小节的信息。
需要封装的数据是这样的。第一层大括号封装的是章节信息,第一层大括号里面的大括号封装的是每个章节的每个小节信息。
[
{
id:"1",
title:"",
children:[
{
id:"11",
title:"第一节"
},
{}
]
},
{}
]
从上面的数据组织形式,我们发现最外面是[]。看到这个中括号,意味着我们在controller层返回的应该是一个List集合,这个list集合的元素应该是封装着章节信息的类。我们现在把目光转移到第一层括号里面,我们发现里面有个属性children,这个children属性后面带的也是一个数组,这意味着章节信息应该封装着小节信息,因为一个章节可能有多个小节,所以每个封装着章节信息的类里面应该有一个集合,这个集合的元素应该是封装着小节信息的类。
用代码的形式展现如下:
小节信息的类
@ApiModel("课时信息")
@Data
public class VideoVo {
@ApiModelProperty(value = "课时ID")
private Long id;
@ApiModelProperty(value = "课时标题")
private String title;
@ApiModelProperty(value = "是否可以试听")
private Integer isFree;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "阿里云视频ID")
private String videoSourceId;
}
章节信息的类
@ApiModel("课程章节对象")
@Data
public class ChapterVo {
@ApiModelProperty(value = "章节ID")
private Long id;
@ApiModelProperty(value = "章节标题")
private String title;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "章节下的课时列表")
private List<VideoVo> children = new ArrayList<>();
}
2.解决问题的相关代码
在看代码之前,我们先来看下我们的数据库表。
章节表
这张图希望大家能注意到里面的course_id字段,这个字段记录了当前章节属于那个课程。
小节表
这张表希望大家能注意到两个字段course_id和chapter_id。course_id字段记录了当前小节属于那个课程,chapter_id字段记录当前小节属于那个章节。
具体代码(持久层框架是mybatisplus,所以我这里没写mapper):
controller
//1 大纲列表(章节和小节列表)
@ApiOperation("大纲列表")
@GetMapping("getNestedTreeList/{courseId}")
public Result getNestedTreeList(@PathVariable Long courseId) {
List<ChapterVo> list = chapterService.getTreeList(courseId);
return Result.ok(list);
}
service接口和service接口实现类
public interface ChapterService extends IService<Chapter> {
List<ChapterVo> getTreeList(Long courseId);
}
service接口实现类代码讲解:
- 通过课程id在章节表里面查询出属于这个课程的章节。
- 通过课程id在小节表里面查询出属于这个课程的小节。
- 因为我们是用自己写的实体类来封装信息,并返回给前端同学的,所以我们这里需要将查询到的数据进行封装。我们查询到的数据是保存在Chapter(章节)和Video(小节),我们要将他们封装到ChapterVo和VideoVo。
- 封装章节的思路很简单,通过一个for循环遍历刚刚查找到的章节集合,并信息封装到我们自己写的ChapterVo类(这个类可以看上面)。
- 封装章节的for循环里面,可以顺便封装小节信息。如何判断这个小节属于哪个章节的判断标准就是,小节的chapter_id是否和封装章节信息的类取得的id相同。如果相同,意味着这个小节属于当前章节,并将这个小节保存到List集合,然后将这个集合添加到ChapterVo类,并最终将ChapterVo保存到List集合。
- 返回List集合。
@Service
public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter> implements ChapterService {
@Autowired
private VideoService videoService;
//1 大纲列表(章节和小节列表)
@Override
public List<ChapterVo> getTreeList(Long courseId) {
//定义最终数据list集合
List<ChapterVo> finalChapterList = new ArrayList<>();
//根据courseId获取课程里面所有章节
QueryWrapper<Chapter> wrapperChapter = new QueryWrapper<>();
wrapperChapter.eq("course_id",courseId);
List<Chapter> chapterList = baseMapper.selectList(wrapperChapter);
//根据courseId获取课程里面所有小节
LambdaQueryWrapper<Video> wrapperVideo = new LambdaQueryWrapper<>();
wrapperVideo.eq(Video::getCourseId,courseId);
List<Video> videoList = videoService.list(wrapperVideo);
//封装章节
for (int i = 0; i < chapterList.size();i++){
//得到课程每个章节
Chapter chapter = chapterList.get(i);
// Chapter转换成ChapterVo
ChapterVo chapterVo = new ChapterVo();
BeanUtils.copyProperties(chapter,chapterVo);
//封装章节里面的小节
//创建list集合用于封装章节里面所有小节
List<VideoVo> videoVoList = new ArrayList<>();
//遍历小节list
for (Video video : videoList){
//判断小节是哪个章节下面
//章节id和小节的chapter_id
if (chapter.getId().equals(video.getChapterId())){
//video转换成VideoVo
VideoVo videoVo = new VideoVo();
BeanUtils.copyProperties(video,videoVo);
//放到videoList
videoVoList.add(videoVo);
}
}
//把章节里面所有小节集合放到每个章节里面
chapterVo.setChildren(videoVoList);
//得到每个章节对象放到finalChapterList集合
finalChapterList.add(chapterVo);
}
return finalChapterList;
}
}