[学成在线]04-课程计划管理

业务分析

课程计划即课程的大纲目录。课程计划分为两级:大章节和不小章节。

从课程计划查询界面上可以看出整体上是一个树型结构,课程计划表teachplan如下:

每个课程计划都有所属课程。

每个课程的课程计划有两个级别,第一级为大章节,grade为1、第二级为小章节,grade为2

第二级的parentid为第一级的id。

课程计划的显示顺序根据排序字段去显示。

课程计划列表展示时还有课程计划关联的视频信息。

课程计划关联的视频信息在teachplan_media表,结构如下

两张表是一对一关系,每个课程计划只能在teachplan_media表中存在一个视频。

课程计划查询

接口设计

接口示例如下:

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

定义DTO

响应结果需要自定义模型类

/**
 * @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;

}

定义接口

定义接口如下:

/**
 * @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;
    }

}

DAO开发

Mapper接口使用sql查询课程计划,组成一个树型结构。

在TeachplanMapper自定义方法:

/**
 * <p>
 * 课程计划 Mapper 接口
 * </p>
 *
 * @author itcast
 */
public interface TeachplanMapper extends BaseMapper<Teachplan> {

    /**
     * @param courseId
     * @return com.xuecheng.content.model.dto.TeachplanDto
     * @description 查询某课程的课程计划,组成树型结构
     * @author Mr.M
     * @date 2022/9/9 11:10
     */
    public List<TeachplanDto> selectTreeNodes(long courseId);

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xuecheng.content.mapper.TeachplanMapper">

    <!-- 查询树形结构  -->
    <select id="selectTreeNodes" resultType="com.xuecheng.content.model.dto.TeachplanDto">

    </select>

</mapper>

定义mapper.xml中的sql语句,分析如下:

1、一级分类和二级分类通过teachplan表的自链接进行,如果只有一级分类其下边没有二级分类,此时也需要显示一级分类,这里使用左连接,左边是一级分类,右边是二级分类。

2、由于当还没有关联视频时teachplan_media对应的记录为空,所以需要teachplan和teachplan_media左链接。

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
         left join teachplan two on two.parentid = one.id
         left join teachplan_media m1 on two.id = m1.teachplan_id
where one.parentid = 0
  and one.course_id = 117
order by one.orderby,
         two.orderby

把写好的sql语句放到xml文件中

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xuecheng.content.mapper.TeachplanMapper">

    <!-- 课程分类树型结构查询映射结果 -->
    <!-- type是指定封装结果的实体类的全类名   -->
    <resultMap id="treeNodeResultMap" type="com.xuecheng.content.model.dto.TeachplanDto">
        <!-- 一级数据映射 -->
        <!-- 主键字段必须使用id标签进行映射,  把数据库的one_id字段映射到实体类的id字段 -->
        <id column="one_id" property="id"/>
        <!--  普通字段使用result标签进行映射, 把数据库的one_pname字段映射到实体类的parentid字段     -->
        <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"/>
        <!-- 一级中包含多个二级数据 -->
        <!-- 映射子节点, 一对多映射,  ofType用于指定list中的对象类型 -->
        <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"/>
            <!--  映射子节点: 一对一映射,   javaType用于指定映射的对象类型        -->
            <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>

    <!--课程计划树型结构查询-->
    <!--  parameterType 用于指定传递过来的参数类型  -->
    <!--  结果封装   -->
    <!-- 1.如果数据库的字段和实体类一致,使用 resultTyp="实体类全类名" 自动封装就行   -->
    <!-- 2.如果数据库的字段和实体类不一致, 使用 resultMap="查询映射结果" 手动封装   -->
    <select id="selectTreeNodes" parameterType="long" resultMap="treeNodeResultMap" >
        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
                 LEFT 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 = #{id}
        order by one.orderby,
                 two.orderby

    </select>

</mapper>

单元测试自定义的mapper

@SpringBootTest
class TeachplanMapperTests {

    @Autowired
    TeachplanMapper teachplanMapper;


    @Test
    void testSelectTreeNodes() {
        List<TeachplanDto> teachplanDtos = teachplanMapper.selectTreeNodes(117L);
        System.out.println(teachplanDtos);
    }
}

Service开发

定义service接口

public interface TeachplanService {
    /**
     * 查询课程计划树型结构
     * @param courseId 课程id
     * @return 课程计划
     */
    public List<TeachplanDto> findTeachplanTree(long courseId);
}

定义service接口实现类

public class TeachplanServiceImpl implements TeachplanService {

    @Autowired
    TeachplanMapper teachplanMapper;


    /**
     * 根据课程id查询课程计划
     * @param courseId 课程id
     * @return
     */
    @Override
    public List<TeachplanDto> findTeachplanTree(long courseId) {
        return teachplanMapper.selectTreeNodes(courseId);
    }
}

Controller开发

调用service完成功能

/**
 * @description 课程计划管理接口
 * @author Mr.M
 * @date 2022/9/6 11:29
 * @version 1.0
 */
@Api(value = "课程计划管理接口",tags = "课程计划管理接口")
@RestController
public class TeachplanController {

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

}

使用httpclient测试

### 查询某个课程的课程计划
GET {{content_host}}/content/teachplan/74/tree-nodes

前后端联调

课程计划新增

业务分析

进入课程计划界面

  1. 点击“添加章”新增第一级课程计划。

新增成功自动刷新课程计划列表。

  1. 点击“添加小节”向某个第一级课程计划下添加小节。

新增成功自动刷新课程计划列表。

新增的课程计划自动排序到最后。

  1. 点击“章”、“节”的名称,可以修改名称、选择是否免费。

数据模型: 定义SaveTeachplanDto, 用来接收请求的参数

  1. 新增第一级课程计划
  • 名称默认为:新章名称 [点击修改]
  • grade:1
  • orderby: 所属课程中同级别下排在最后
  1. 新增第二级课程计划
  • 名称默认为:新小节名称 [点击修改]
  • grade:2
  • orderby: 所属课程计划中排在最后
  1. 修改第一级、第二级课程计划的名称,修改第二级课程计划是否免费
package com.xuecheng.content.model.dto;

/**
 * 新增大章节, 小章节, 修改章节的信息
 */

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

}

接口设计

### 新增课程计划--章,当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" : "小节名称 [点击修改]"
}
  1. 同一个接口接收新增和修改两个业务请求,以是否传递课程计划id 来判断是新增还是修改。
  2. 如果传递了课程计划id说明当前是要修改该课程计划,否则是新增一个课程计划。

接口定义

package com.xuecheng.content.api;

/**
 * @description 课程计划管理接口
 * @author Mr.M
 * @date 2022/9/6 11:29
 * @version 1.0
 */
@Api(value = "课程计划管理接口",tags = "课程计划管理接口")
@RestController
public class TeachplanController {

    @ApiOperation("课程计划创建或修改")
    @PostMapping("/teachplan")
    public void saveTeachplan( @RequestBody SaveTeachplanDto teachplan){

    }
}

Service开发

定义保存课程计划的Service接口。

/**
 * 课程计划管理接口
 */
public interface TeachplanService {
    /**
     * @description 添加/修改课程计划
     * @param teachplanDto  课程计划信息
     * @return void
     * @author Mr.M
     * @date 2022/9/9 13:39
     */
    public void saveTeachplan(SaveTeachplanDto teachplanDto);

}

编写接口实现:

public class TeachplanServiceImpl implements TeachplanService {

    @Autowired
    TeachplanMapper teachplanMapper;


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

}

Controller完善

controller中调用service方法

/**
 * @description 课程计划管理接口
 * @author Mr.M
 * @date 2022/9/6 11:29
 * @version 1.0
 */
@Api(value = "课程计划管理接口",tags = "课程计划管理接口")
@RestController
public class TeachplanController {

    @Autowired
    TeachplanService teachplanService;

    @ApiOperation("课程计划创建或修改")
    @PostMapping("/teachplan")
    public void saveTeachplan( @RequestBody SaveTeachplanDto teachplan){
        teachplanService.saveTeachplan(teachplan);
    }
}

前端端联调

添加大章节, 添加小章节, 修改大章节, 修改小章节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值