解决了内连接导致的无法显示新增章节的bug,见最后
2、点击编辑
因为课程审核通过方可发布,任何时候都 可以编辑,下图是编辑课程的界面:
进入编辑界面显示出当前课程的信息。
3、修改成功自动进入课程计划编辑页面。
6.1.2 数据模型
修改课程的涉及到的数据表是课程基本信息表
1、进入课程编辑界面
界面中显示了课程的当前信息,需要根据课程id查询课程基本和课程营销信息,显示在表单上。
2、编辑、提交
修改课程提交的数据比新增课程多了一项课程id,因为修改课程需要针对某个课程进行修改。
3、保存数据
编辑完成保存课程基础信息和课程营销信息。
更新课程基本信息表中的修改人、修改时间。
定义根据课程id查询课程信息接口。
接口示例如下:
Java
GET /content/course/40
Content-Type: application/json
#响应结果
#{
# "id": 40,
# "companyId": 1232141425,
# "companyName": null,
# "name": "SpringBoot核心",
# "users": "Spring Boot初学者",
# "tags": "Spring项目的快速构建",
# "mt": "1-3",
# "mtName": null,
# "st": "1-3-2",
# "stName": null,
# "grade": "200003",
# "teachmode": "201001",
# "description": "课程系统性地深度探讨 Spring Boot 核心特性,引导小伙伴对 Java 规范的重视,启发对技术原理性的思考,掌握排查问题的技能,以及学习阅读源码的方法和技巧,全面提升研发能力,进军架构师队伍。",
# "pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
# "createDate": "2019-09-10 16:05:39",
# "changeDate": "2022-09-09 07:27:48",
# "createPeople": null,
# "changePeople": null,
# "auditStatus": "202004",
# "status": "203001",
# "coursePubId": 21,
# "coursePubDate": null,
# "charge": "201001",
# "price": 0.01
#}
查询结果为单条课程信息,内容和新增课程返回结果一致,所以采用与新增课程一致的模型类。
接口定义如下:
Java
@ApiOperation("根据课程id查询课程基础信息")
@GetMapping("/course/{courseId}")
public CourseBaseInfoDto getCourseBaseById(@PathVariable Long courseId){
return null;
}
根据前边的数据模型分析,修改课程提交的数据比新增多了课程id,接口示例如下:
Java
### 修改课程
PUT /content/course
Content-Type: application/json
{
"id": 40,
"companyName": null,
"name": "SpringBoot核心",
"users": "Spring Boot初学者",
"tags": "Spring项目的快速构建",
"mt": "1-3",
"st": "1-3-2",
"grade": "200003",
"teachmode": "201001",
"description": "课程系统性地深度探讨 Spring Boot 核心特性,引导小伙伴对 Java 规范的重视,启发对技术原理性的思考,掌握排查问题的技能,以及学习阅读源码的方法和技巧,全面提升研发能力,进军架构师队伍。",
"pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
"charge": "201001",
"price": 0.01
}
###修改成功响应结果如下
#{
# "id": 40,
# "companyId": 1232141425,
# "companyName": null,
# "name": "SpringBoot核心",
# "users": "Spring Boot初学者",
# "tags": "Spring项目的快速构建",
# "mt": "1-3",
# "mtName": null,
# "st": "1-3-2",
# "stName": null,
# "grade": "200003",
# "teachmode": "201001",
# "description": "课程系统性地深度探讨 Spring Boot 核心特性,引导小伙伴对 Java 规范的重视,启发对技术原理性的思考,掌握排查问题的技能,以及学习阅读源码的方法和技巧,全面提升研发能力,进军架构师队伍。",
# "pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
# "createDate": "2019-09-10 16:05:39",
# "changeDate": "2022-09-09 07:27:48",
# "createPeople": null,
# "changePeople": null,
# "auditStatus": "202004",
# "status": "203001",
# "coursePubId": 21,
# "coursePubDate": null,
# "charge": "201001",
# "price": 0.01
#}
这里定义修改课程提交的数据模型。
Java
package com.xuecheng.content.model.dto;
import com.xuecheng.base.execption.ValidationGroups;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
/**
* @description 添加课程dto
* @author Mr.M
* @date 2022/9/7 17:40
* @version 1.0
*/
@Data
@ApiModel(value="EditCourseDto", description="修改课程基本信息")
public class EditCourseDto extends AddCourseDto {
@ApiModelProperty(value = "课程id", required = true)
private Long id;
}
修改后返回最新课程信息,采用与新增课程接口返回类型一致的数据模型。
接口定义如下:
Java
@ApiOperation("修改课程基础信息")
@PutMapping("/course")
public CourseBaseInfoDto modifyCourseBase(@RequestBody @Validated EditCourseDto editCourseDto){
}
查询课程信息的Service方法在新增课程接口开发中已实现,无需实现,如下:
Java
//根据课程id查询课程基本信息,包括基本信息和营销信息
public CourseBaseInfoDto getCourseBaseInfo(long courseId){
//查询课程信息
CourseBase courseBase = courseBaseMapper.selectById(courseId);
if(courseBase == null){
return null;
}
//查询营销信息
CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
//要返回的对象
CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto();
BeanUtils.copyProperties(courseBase,courseBaseInfoDto);
if(courseMarket != null){
BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);
}
//查询分类名称
CourseCategory courseCategoryBySt = courseCategoryMapper.selectById(courseBase.getSt());
courseBaseInfoDto.setStName(courseCategoryBySt.getName());
CourseCategory courseCategoryByMt = courseCategoryMapper.selectById(courseBase.getMt());
courseBaseInfoDto.setMtName(courseCategoryByMt.getName());
return courseBaseInfoDto;
}
需要将查询课程信息的方法提到接口上,这样在controller中通过接口调用此方法。
Java
public interface CourseBaseInfoService {
....
/**
* @description 根据id查询课程基本信息
* @param courseId 课程id
* @return com.xuecheng.content.model.dto.CourseBaseInfoDto
* @author Mr.M
* @date 2022/10/9 8:13
*/
public CourseBaseInfoDto getCourseBaseInfo(long courseId);
...
完善接口层代码 :
Java
@ApiOperation("根据课程id查询课程基础信息")
@GetMapping("/course/{courseId}")
public CourseBaseInfoDto getCourseBaseById(@PathVariable Long courseId){
return courseBaseInfoService.getCourseBaseInfo(courseId);
}
测试查询课程
用httpclient测试查询课程接口:
Java
### 查询课程信息
GET /content/course/40
6.3.2 修改课程信息
修改Service修改课程的接口与方法:
Java
/**
* @description 修改课程信息
* @param companyId 机构id
* @param dto 课程信息
* @return com.xuecheng.content.model.dto.CourseBaseInfoDto
* @author Mr.M
* @date 2022/9/8 21:04
*/
public CourseBaseInfoDto updateCourseBase(Long companyId,EditCourseDto dto);
实现方法如下:
Java
@Transactional
@Override
public CourseBaseInfoDto updateCourseBase(Long companyId, EditCourseDto dto) {
//课程id
Long courseId = dto.getId();
CourseBase courseBase = courseBaseMapper.selectById(courseId);
if(courseBase==null){
XueChengPlusException.cast("课程不存在");
}
//校验本机构只能修改本机构的课程
if(!courseBase.getCompanyId().equals(companyId)){
XueChengPlusException.cast("本机构只能修改本机构的课程");
}
//封装基本信息的数据
BeanUtils.copyProperties(dto,courseBase);
courseBase.setChangeDate(LocalDateTime.now());
//更新课程基本信息
int i = courseBaseMapper.updateById(courseBase);
//封装营销信息的数据
CourseMarket courseMarket = new CourseMarket();
BeanUtils.copyProperties(dto,courseMarket);
saveCourseMarket(courseMarket);
//查询课程信息
CourseBaseInfoDto courseBaseInfo = this.getCourseBaseInfo(courseId);
return courseBaseInfo;
}
最后完善接口层代码:
Java
@ApiOperation("修改课程基础信息")
@PutMapping("/course")
public CourseBaseInfoDto modifyCourseBase(@RequestBody @Validated EditCourseDto editCourseDto){
//机构id,由于认证系统没有上线暂时硬编码
Long companyId = 1232141425L;
return courseBaseInfoService.updateCourseBase(companyId,editCourseDto);
}
接口开发完成进行测试,使用httpclient测试
Java
### 根据课程id查询课程信息
GET {{content_host}}/content/course/40
Content-Type: application/json
#响应结果
#{
# "id": 40,
# "companyId": 1232141425,
# "companyName": null,
# "name": "SpringBoot核心",
# "users": "Spring Boot初学者",
# "tags": "Spring项目的快速构建",
# "mt": "1-3",
# "mtName": null,
# "st": "1-3-2",
# "stName": null,
# "grade": "200003",
# "teachmode": "201001",
# "description": "课程系统性地深度探讨 Spring Boot 核心特性,引导小伙伴对 Java 规范的重视,启发对技术原理性的思考,掌握排查问题的技能,以及学习阅读源码的方法和技巧,全面提升研发能力,进军架构师队伍。",
# "pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
# "createDate": "2019-09-10 16:05:39",
# "changeDate": "2022-09-09 07:27:48",
# "createPeople": null,
# "changePeople": null,
# "auditStatus": "202004",
# "status": "203001",
# "coursePubId": 21,
# "coursePubDate": null,
# "charge": "201001",
# "price": 0.01
#}
### 修改课程
PUT {{content_host}}/content/course
Content-Type: application/json
{
"id": 40,
"name": "SpringBoot核心",
"users": "Spring Boot初学者",
"tags": "Spring项目的快速构建",
"mt": "1-3",
"st": "1-3-2",
"grade": "200003",
"teachmode": "201001",
"description": "课程系统性地深度探讨 Spring Boot 核心特性,引导小伙伴对 Java 规范的重视,启发对技术原理性的思考,掌握排查问题的技能,以及学习阅读源码的方法和技巧,全面提升研发能力,进军架构师队伍。",
"pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
"charge": "201001",
"price": 0.01
}
###修改成功响应结果如下
#{
# "id": 40,
# "companyId": 1232141425,
# "companyName": null,
# "name": "SpringBoot核心",
# "users": "Spring Boot初学者",
# "tags": "Spring项目的快速构建",
# "mt": "1-3",
# "mtName": null,
# "st": "1-3-2",
# "stName": null,
# "grade": "200003",
# "teachmode": "201001",
# "description": "课程系统性地深度探讨 Spring Boot 核心特性,引导小伙伴对 Java 规范的重视,启发对技术原理性的思考,掌握排查问题的技能,以及学习阅读源码的方法和技巧,全面提升研发能力,进军架构师队伍。",
# "pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
# "createDate": "2019-09-10 16:05:39",
# "changeDate": "2022-09-09 07:27:48",
# "createPeople": null,
# "changePeople": null,
# "auditStatus": "202004",
# "status": "203001",
# "coursePubId": 21,
# "coursePubDate": null,
# "charge": "201001",
# "price": 0.01
#}
前端开发完毕进行前后端接口联调。
过程略。
课程基本信息添加或修改成功将自动进入课程计划编辑器界面,如下图:
课程计划即课程的大纲目录。
课程计划分为两级:大章节和不小章节。
本小节完成课程计划信息的查询。
从课程计划查询界面上可以看出整体上是 一个树型结构,课程计划表teachplan如下:
每个课程计划都有所属课程。
每个课程的课程计划有两个级别,第一级为大章节,grade为1、第二级为小章节,grade为2
3。第二级的parentid为第一级的id。
课程计划的显示顺序根据排序字段去显示。
根据业务流程中的界面原型,课程计划列表展示时还有课程计划关联的视频信息。
课程计划关联的视频信息在teachplan_media表,结构如下:
两张表是一对一关系,每个课程计划只能在teachplan_media表中存在一个视频。
从课程资料目录下的db目录中,从xcplus_content.sql文件中找到课程计划表teachplan、teachplan_media表的建表语句以及数据初始化语句,并通过mysql客户端去执行脚本。
这里使用DataGrid 客户端工具连接mysql并执行脚本。
接口示例如下:
Java
GET /teachplan/22/tree-nodes
[
{
"changeDate" : null,
"courseId" : 74,
"cousePubId" : null,
"createDate" : null,
"endTime" : null,
"grade" : "2",
"isPreview" : "0",
"mediaType" : null,
"orderby" : 1,
"parentid" : 112,
"pname" : "第1章基础知识",
"startTime" : null,
"status" : null,
"id" : 113,
"teachPlanTreeNodes" : [
{
"changeDate" : null,
"courseId" : 74,
"cousePubId" : null,
"createDate" : null,
"endTime" : null,
"grade" : "3",
"isPreview" : "1",
"mediaType" : "001002",
"orderby" : 1,
"parentid" : 113,
"pname" : "第1节项目概述",
"startTime" : null,
"status" : null,
"id" : 115,
"teachPlanTreeNodes" : null,
"teachplanMedia" : {
"courseId" : 74,
"coursePubId" : null,
"mediaFilename" : "2.avi",
"mediaId" : 41,
"teachplanId" : 115,
"id" : null
}
}
],
"teachplanMedia" : null
},
{
"changeDate" : null,
"courseId" : 74,
"cousePubId" : null,
"createDate" : null,
"endTime" : null,
"grade" : "2",
"isPreview" : "0",
"mediaType" : "",
"orderby" : 1,
"parentid" : 112,
"pname" : "第2章快速入门",
"startTime" : null,
"status" : null,
"id" : 242,
"teachPlanTreeNodes" : [
{
"changeDate" : null,
"courseId" : 74,
"cousePubId" : null,
"createDate" : null,
"endTime" : null,
"grade" : "3",
"isPreview" : "1",
"mediaType" : "001002",
"orderby" : 2,
"parentid" : 242,
"pname" : "第1节搭建环境",
"startTime" : null,
"status" : null,
"id" : 244,
"teachPlanTreeNodes" : null,
"teachplanMedia" : {
"courseId" : 74,
"coursePubId" : null,
"mediaFilename" : "3.avi",
"mediaId" : 42,
"teachplanId" : 244,
"id" : null
}
},
{
"changeDate" : null,
"courseId" : 74,
"cousePubId" : null,
"createDate" : null,
"endTime" : null,
"grade" : "3",
"isPreview" : "0",
"mediaType" : "001002",
"orderby" : 3,
"parentid" : 242,
"pname" : "第2节项目概述",
"startTime" : null,
"status" : null,
"id" : 245,
"teachPlanTreeNodes" : null,
"teachplanMedia" : {
"courseId" : 74,
"coursePubId" : null,
"mediaFilename" : "1a.avi",
"mediaId" : 39,
"teachplanId" : 245,
"id" : null
}
}
],
"teachplanMedia" : null
}
]
查询课程计划的请求参数:课程id
响应结果需要自定义模型类:
Java
package com.xuecheng.content.model.dto;
import com.xuecheng.content.model.po.Teachplan;
import com.xuecheng.content.model.po.TeachplanMedia;
import lombok.Data;
import lombok.ToString;
import java.util.List;
/**
* @description 课程计划树型结构dto
* @author Mr.M
* @date 2022/9/9 10:27
* @version 1.0
*/
@Data
@ToString
public class TeachplanDto extends Teachplan {
//课程计划关联的媒资信息
TeachplanMedia teachplanMedia;
//子结点
List<TeachplanDto> teachPlanTreeNodes;
}
定义接口如下:
Java
package com.xuecheng.content.api;
import com.xuecheng.content.model.dto.TeachplanDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @description 课程计划编辑接口
* @author Mr.M
* @date 2022/9/6 11:29
* @version 1.0
*/
@Api(value = "课程计划编辑接口",tags = "课程计划编辑接口")
@RestController
public class TeachplanController {
@ApiOperation("查询课程计划树形结构")
@ApiImplicitParam(value = "courseId",name = "课程Id",required = true,dataType = "Long",paramType = "path")
@GetMapping("/teachplan/{courseId}/tree-nodes")
public List<TeachplanDto> getTreeNodes(@PathVariable Long courseId){
return null;
}
}
Mapper接口使用sql查询课程计划,组成一个树型结构。
在TeachplanMapper自定义方法:
Java
public interface TeachplanMapper extends BaseMapper<Teachplan> {
/**
* @description 查询某课程的课程计划,组成树型结构
* @param courseId
* @return com.xuecheng.content.model.dto.TeachplanDto
* @author Mr.M
* @date 2022/9/9 11:10
*/
public List<TeachplanDto> selectTreeNodes(long courseId);
}
定义mapper.xml中的sql语句,分析如下:
1、一级分类和二级分类通过teachplan表的自链接进行,如果只有一级分类其下边没有二级分类,此时也需要显示一级分类,这里使用左连接,左边是一级分类,右边是二级分类。
2、由于当还没有关联 视频时teachplan_media对应的记录为空,所以需要teachplan和teachplan_media左链接。
sql如下:
select
one.id one_id,
one.pname one_pname,
one.parentid one_parentid,
one.grade one_grade,
one.media_type one_mediaType,
one.start_time one_stratTime,
one.end_time one_endTime,
one.orderby one_orderby,
one.course_id one_courseId,
one.course_pub_id one_coursePubId,
two.id two_id,
two.pname two_pname,
two.parentid two_parentid,
two.grade two_grade,
two.media_type two_mediaType,
two.start_time two_stratTime,
two.end_time two_endTime,
two.orderby two_orderby,
two.course_id two_courseId,
two.course_pub_id two_coursePubId,
m1.media_fileName mediaFilename,
m1.id teachplanMeidaId,
m1.media_id mediaId
from teachplan one
INNER JOIN teachplan two on one.id = two.parentid
LEFT JOIN teachplan_media m1 on m1.teachplan_id = two.id
where one.parentid = 0 and one.course_id=#{value}
order by one.orderby,
two.orderby
定义mapper.xml
<resultMap id="treeNodeResultMap" type="com.xuecheng.content.model.dto.TeachplanDto">
<id column="one_id" property="id" />
<result column="one_pname" property="pname" />
<result column="one_parentid" property="parentid" />
<result column="one_grade" property="grade" />
<result column="one_mediaType" property="mediaType" />
<result column="one_stratTime" property="stratTime" />
<result column="one_endTime" property="endTime" />
<result column="one_orderby" property="orderby" />
<result column="one_courseId" property="courseId" />
<result column="one_coursePubId" property="coursePubId" />
<collection property="teachPlanTreeNodes" ofType="com.xuecheng.content.model.dto.TeachplanDto">
<id column="two_id" property="id" />
<result column="two_pname" property="pname" />
<result column="two_parentid" property="parentid" />
<result column="two_grade" property="grade" />
<result column="two_mediaType" property="mediaType" />
<result column="two_stratTime" property="stratTime" />
<result column="two_endTime" property="endTime" />
<result column="two_orderby" property="orderby" />
<result column="two_courseId" property="courseId" />
<result column="two_coursePubId" property="coursePubId" />
<association property="teachplanMedia" javaType="com.xuecheng.content.model.po.TeachplanMedia">
<result column="teachplanMeidaId" property="id" />
<result column="mediaFilename" property="mediaFilename" />
<result column="mediaId" property="mediaId" />
<result column="two_id" property="teachplanId" />
<result column="two_courseId" property="courseId" />
<result column="two_coursePubId" property="coursePubId" />
association>
collection>
resultMap>
<select id="selectTreeNodes" resultMap="treeNodeResultMap" parameterType="long" >
select
one.id one_id,
one.pname one_pname,
one.parentid one_parentid,
one.grade one_grade,
one.media_type one_mediaType,
one.start_time one_stratTime,
one.end_time one_endTime,
one.orderby one_orderby,
one.course_id one_courseId,
one.course_pub_id one_coursePubId,
two.id two_id,
two.pname two_pname,
two.parentid two_parentid,
two.grade two_grade,
two.media_type two_mediaType,
two.start_time two_stratTime,
two.end_time two_endTime,
two.orderby two_orderby,
two.course_id two_courseId,
two.course_pub_id two_coursePubId,
m1.media_fileName mediaFilename,
m1.id teachplanMeidaId,
m1.media_id mediaId
from teachplan one
INNER JOIN teachplan two on one.id = two.parentid
LEFT JOIN teachplan_media m1 on m1.teachplan_id = two.id
where one.parentid = 0 and one.course_id=#{value}
order by one.orderby,
two.orderby
select>
单元测试方法,略。
定义service接口
Java
package com.xuecheng.content.service;
import com.xuecheng.base.model.PageParams;
import com.xuecheng.base.model.PageResult;
import com.xuecheng.content.model.dto.*;
import com.xuecheng.content.model.po.CourseBase;
/**
* @description 课程基本信息管理业务接口
* @author Mr.M
* @date 2022/9/6 21:42
* @version 1.0
*/
public interface TeachplanService {
/**
* @description 查询课程计划树型结构
* @param courseId 课程id
* @return List
* @author Mr.M
* @date 2022/9/9 11:13
*/
public List<TeachplanDto> findTeachplanTree(long courseId);
}
定义service接口实现类
Java
package com.xuecheng.content.service.impl;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.model.dto.TeachplanDto;
import com.xuecheng.content.service.TeachplanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @description 课程计划service接口实现类
* @author Mr.M
* @date 2022/9/9 11:14
* @version 1.0
*/
@Service
public class TeachplanServiceImpl implements TeachplanService {
@Autowired
TeachplanMapper teachplanMapper;
@Override
public List<TeachplanDto> findTeachplanTree(long courseId) {
return teachplanMapper.selectTreeNodes(courseId);
}
}
1、完善接口层代码
Java
@Autowired
TeachplanService teachplanService;
@ApiOperation("查询课程计划树形结构")
@ApiImplicitParam(value = "courseId",name = "课程基础Id值",required = true,dataType = "Long",paramType = "path")
@GetMapping("teachplan/{courseId}/tree-nodes")
public List<TeachplanDto> getTreeNodes(@PathVariable Long courseId){
return teachplanService.findTeachplanTree(courseId);
}
2、使用httpclient测试
找一个有课程计划的课程进行测试
Java
### 查询某个课程的课程计划
GET {{content_host}}/content/teachplan/74/tree-nodes
3、前后端联调
1)进入课程编辑页面
2)保存进入下一步
观察课程计划获取是否成功。
1)进入新增课程页面
2)新增课程成功,自动进入课程计划编辑界面。
由于是新增的课程,课程计划为空。
2、点击“添加章”新增第一级课程计划。
新增成功自动刷新课程计划列表。
3、点击“添加小节”向某个第一级课程计划下添加小节。
新增成功自动刷新课程计划列表。
新增的课程计划自动排序到最后。
4、点击“章”、“节”的名称,可以修改名称、选择是否免费。
8.1.2 数据模型
1、新增第一级课程计划
名称默认为:新章名称 [点击修改]
grade:1
orderby: 所属课程中同级别下排在最后
2、新增第二级课程计划
名称默认为:新小节名称 [点击修改]
grade:2
orderby: 所属课程计划中排在最后
3、修改第一级、第二级课程计划的名称,修改第二级课程计划是否免费
8.2 接口定义
接口示例如下:
Java
### 新增课程计划--章,当grade为1时parentid为0
POST /teachplan
Content-Type: application/json
{
"courseId" : 74,
"parentid": 0,
"grade" : 1,
"pname" : "新章名称 [点击修改]"
}
### 新增课程计划--节
POST {{content_host}}/content/teachplan
Content-Type: application/json
{
"courseId" : 74,
"parentid": 247,
"grade" : 2,
"pname" : "小节名称 [点击修改]"
}
同一个接口接收新增和修改两个业务请求,以是否传递课程计划id 来判断是新增还是修改。
如果传递了课程计划id说明当前是要修改该课程计划,否则是新增一个课程计划。
定义接收请求参数的数据模型类:
定义SaveTeachplanDto
Java
package com.xuecheng.content.model.dto;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.xuecheng.content.model.po.Teachplan;
import com.xuecheng.content.model.po.TeachplanMedia;
import lombok.Data;
import lombok.ToString;
import java.time.LocalDateTime;
import java.util.List;
/**
* @description 保存课程计划dto,包括新增、修改
* @author Mr.M
* @date 2022/9/9 10:27
* @version 1.0
*/
@Data
@ToString
public class SaveTeachplanDto {
/***
* 教学计划id
*/
private Long id;
/**
* 课程计划名称
*/
private String pname;
/**
* 课程计划父级Id
*/
private Long parentid;
/**
* 层级,分为1、2、3级
*/
private Integer grade;
/**
* 课程类型:1视频、2文档
*/
private String mediaType;
/**
* 课程标识
*/
private Long courseId;
/**
* 课程发布标识
*/
private Long coursePubId;
/**
* 是否支持试学或预览(试看)
*/
private String isPreview;
}
定义接口如下:
@ApiOperation("课程计划创建或修改")
@PostMapping("/teachplan")
public void saveTeachplan( @RequestBody SaveTeachplanDto teachplan){
}
根据业务的分析,Mapper使用自动生成的mapper即可满足要求。
定义保存课程计划的Service接口。
Java
/**
* @description 只在课程计划
* @param teachplanDto 课程计划信息
* @return void
* @author Mr.M
* @date 2022/9/9 13:39
*/
public void saveTeachplan(SaveTeachplanDto teachplanDto);
编写接口实现:
Java
@Transactional
@Override
public void saveTeachplan(SaveTeachplanDto teachplanDto) {
//课程计划id
Long id = teachplanDto.getId();
//修改课程计划
if(id!=null){
Teachplan teachplan = teachplanMapper.selectById(id);
BeanUtils.copyProperties(teachplanDto,teachplan);
teachplanMapper.updateById(teachplan);
}else{
//取出同父同级别的课程计划数量
int count = getTeachplanCount(teachplanDto.getCourseId(), teachplanDto.getParentid());
Teachplan teachplanNew = new Teachplan();
//设置排序号
teachplanNew.setOrderby(count+1);
BeanUtils.copyProperties(teachplanDto,teachplanNew);
teachplanMapper.insert(teachplanNew);
}
}
/**
* @description 获取最新的排序号
* @param courseId 课程id
* @param parentId 父课程计划id
* @return int 最新排序号
* @author Mr.M
* @date 2022/9/9 13:43
*/
private int getTeachplanCount(long courseId,long parentId){
LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Teachplan::getCourseId,courseId);
queryWrapper.eq(Teachplan::getParentid,parentId);
Integer count = teachplanMapper.selectCount(queryWrapper);
return count;
}
1、完善接口的代码 ,调用service方法完成课程计划的创建和修改。
Java
@ApiOperation("课程计划创建或修改")
@PostMapping("/teachplan")
public void saveTeachplan( @RequestBody SaveTeachplanDto teachplan){
teachplanService.saveTeachplan(teachplan);
}
2、首先使用httpclient做以下测试。
添加章
Java
### 新增课程计划--章
POST {{content_host}}/content/teachplan
Content-Type: application/json
{
"courseId" : 74,
"parentid": 0,
"grade" : 1,
"pname" : "新章名称 [点击修改]"
}
2、添加小节
Java
### 新增课程计划--节,从数据库找到第一级的课程计划id向其下边添加计划
POST {{content_host}}/content/teachplan
Content-Type: application/json
{
"courseId" : 74,
"parentid": 247,
"grade" : 2,
"pname" : "小节名称 [点击修改]"
}
3、保存课程计划
Java
### 课程课程计划,需要从数据库找到修改的课程计划id
POST {{content_host}}/content/teachplan
Content-Type: application/json
{
"changeDate" : null,
"courseId" : 22,
"cousePubId" : null,
"createDate" : null,
"ctlBarShow" : false,
"ctlEditTitle" : true,
"endTime" : null,
"grade" : "2",
"isPreview" : "1",
"mediaType" : "",
"orderby" : 1,
"parentid" : 237,
"pname" : "第1节修改名称",
"startTime" : null,
"status" : null,
"teachPlanId" : 240
}
4、前后端联调
分别联调新增章、新增小节、保存计划信息。
通过接口测试我们发现:
1、使用httpclient测试没有问题
2、前后端联调时发现新增的第一级目录不能显示在列表中。
请自己分析并修复。
3、思考添加课程计划的实现方式是否存在bug?
如有bug进行修改。
bug
:
BUG是看不到添加的章节信息,看不到章节信息是因为SQL语句有问题,不应该用内连接,得用左外连接,新增章节下面没有小节信息,用内连接必然查不出来,因为连接条件是c.parentid = p.id,用左外连接(章节当左),会显示左表的全部内容,那么不管章节下有没有小节信息,都会显示章节
SELECT * FROM teachplan p
LEFT JOIN teachplan c ON c.parentid = p.id
LEFT JOIN teachplan_media tm ON tm.teachplan_id = c.id