2.3-学成在线内容管理之修改课程+查询课程计划

内容管理

解决了内连接导致的无法显示新增章节的bug,见最后

6 修改课程

6.1 需求分析

6.1.1 业务流程

1、进入课程列表查询
在这里插入图片描述

2、点击编辑
因为课程审核通过方可发布,任何时候都 可以编辑,下图是编辑课程的界面:
在这里插入图片描述

进入编辑界面显示出当前课程的信息。
3、修改成功自动进入课程计划编辑页面。
6.1.2 数据模型
修改课程的涉及到的数据表是课程基本信息表
在这里插入图片描述

课程营销信息表:
在这里插入图片描述

1、进入课程编辑界面
界面中显示了课程的当前信息,需要根据课程id查询课程基本和课程营销信息,显示在表单上。
2、编辑、提交
修改课程提交的数据比新增课程多了一项课程id,因为修改课程需要针对某个课程进行修改。
3、保存数据
编辑完成保存课程基础信息和课程营销信息。
更新课程基本信息表中的修改人、修改时间。

6.2 接口定义

6.2.1 查询课程信息

定义根据课程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;
}
6.2.2 修改课程信息

根据前边的数据模型分析,修改课程提交的数据比新增多了课程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){
    
}

6.3 接口开发

6.3.1 查询课程信息

查询课程信息的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);
}

6.4 接口测试

接口开发完成进行测试,使用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
#}

前端开发完毕进行前后端接口联调。
过程略。

7 查询课程计划

7.1 需求分析

7.1.1 业务流程

课程基本信息添加或修改成功将自动进入课程计划编辑器界面,如下图:
在这里插入图片描述

课程计划即课程的大纲目录。
课程计划分为两级:大章节和不小章节。
本小节完成课程计划信息的查询。

7.1.2 数据模型

从课程计划查询界面上可以看出整体上是 一个树型结构,课程计划表teachplan如下:
在这里插入图片描述

每个课程计划都有所属课程。
每个课程的课程计划有两个级别,第一级为大章节,grade为1、第二级为小章节,grade为2
3。第二级的parentid为第一级的id。
课程计划的显示顺序根据排序字段去显示。
根据业务流程中的界面原型,课程计划列表展示时还有课程计划关联的视频信息。
课程计划关联的视频信息在teachplan_media表,结构如下:
在这里插入图片描述

两张表是一对一关系,每个课程计划只能在teachplan_media表中存在一个视频。
从课程资料目录下的db目录中,从xcplus_content.sql文件中找到课程计划表teachplan、teachplan_media表的建表语句以及数据初始化语句,并通过mysql客户端去执行脚本。
这里使用DataGrid 客户端工具连接mysql并执行脚本。

7.2 接口定义

接口示例如下:

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

}

7.3 接口开发

7.3.1 DAO开发

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>

单元测试方法,略。

7.3.2 Service开发

定义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<TeachplanDto>
 * @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);
 }
}

7.4 接口测试

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)新增课程成功,自动进入课程计划编辑界面。
由于是新增的课程,课程计划为空。

8 新增/修改计划

8.1 需求分析

8.1.1 业务流程

1、进入课程计划界面
在这里插入图片描述

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){
        
    }

8.3 接口开发

8.3.1 Mapper开发

根据业务的分析,Mapper使用自动生成的mapper即可满足要求。

8.3.2 Service开发

定义保存课程计划的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;
 }

8.4 接口测试

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、前后端联调
分别联调新增章、新增小节、保存计划信息。

8.5 Bug修改

通过接口测试我们发现:
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
  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值