1.模块需求分析

1.1什么是模块需求分析

  • 在百度百科中对需求分析的定义如下:
    • 需求分析也称为软件需求分析、系统需求分析或需求分析工程等,是开发人员经过深入细致的调研和分析,准确理解用户和项目的功能、性能、可靠性等具体要求,将用户非形式的需求表述转化为完整的需求定义,从而确定系统必须做什么的过程。
    • 简单理解就是要搞清楚问题域,问题域就是用户的需求,软件要为用户解决什么问题,实现哪些业务功能,满足什么样的性能要求。
  • 如何作需求分析?
  1. 首先确认用户需求
    • 用户需求即用户的原始需求。
    • 通过用户访谈、问卷调查、开会讨论、查阅资料等调研手段梳理用户的原始需求。
    • 产品人员根据用户需求会绘制界面原型,通过界面原型再和用户确认需求。
  2. 确认关键问题
    • 用户的原始需求可能 是含糊不清的,需求分析要从繁杂的问题中梳理出关键问题。
    • 比如:教学机构的老师想要将课程发布到网上,这是原始需求,根据这个用户需求我们需要进行扩展分析,扩展出几下几点:
      • 课程发布需要发布哪些信息
      • 如果发布了不良信息怎么办?
    • 课程发布后用户怎么查看课程?
    • 根据以上几点继续延伸性分析:
      • 课程发布需要发布哪些信息
        • 课程名称、课程介绍、课程价格、课程图片、师资等信息
      • 继续延伸分析:
      • 这么多课程信息进行归类,方便用户编辑,分为课程基本信息、课程营销信息、课程师资等信息。
      • 按照这样的思路对用户需求逐项分析,梳理出若干问题,再从中找到关键问题。比如:上边对课程信息分类后,哪些是关键信息,课程名称、课程图片、课程介绍等基本信息为关键信息,所以发布课程的第一步要编写课程基本信息。
      • 找到了关键问题,下一步就可以进行数据建模,创建课程基本信息表,并设计其中的字段。
  3. 梳理业务流程
    • 业务流程是由一个或多个用户参与完成为了完成一个目标所进行的一系列的业务操作,不论是整个系统还是一个模块通常首先分析核心的业务流程,比如:内容管理模块的核心业务流程是课程发布,本项目的核心业务流程是学生选课学习流程。
  4. 数据建模
    • 数据建模要根据分析的关键问题将其相关的信息全部建模。比如:根据发布课程的用户需求,可创建课程基本信息表、课程营销信息表、课程师资表、课程发布记录表、课程审核记录表等。
  5. 编写需求规格说明书
    • 需求分析阶段的成果物是需求分析规格说明书,针对每一个问题编写需求用例,需求用例包括:功能名称、功能描述、参与者、基本事件流程、可选事件流、数据描述、前置条件、后置条件等内容。

1.2模块介绍

  • 内容管理这个词存在于很多软件系统,什么是内容管理?
  • 通过百度百科查询其意思:
    • 内容管理系统(content management system,CMS),是一种位于WEB前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。
  • 本项目作为一个大型的在线教育平台,其内容管理模块主要对课程及相关内容进行管理,包括:课程的基本信息、课程图片、课程师资信息、课程的授课计划、课程视频、课程文档等内容的管理。

    1.3业务流程

  • 内容管理的业务由教学机构人员和平台的运营人员共同完成。
  • 教学机构人员的业务流程如下:
    1. 登录教学机构。
    2. 维护课程信息,添加一门课程需要编辑课程的基本信息、上传课程图片、课程营销信息、课程计划、上传课程视频、课程师资信息等内容。
    3. 课程信息编辑完成,通过课程预览确认无误后提交课程审核。
    4. 待运营人员对课程审核通过后方可进行课程发布。
  • 运营人员的业务流程如下:
    1. 查询待审核的课程信息。
    2. 审核课程信息。
    3. 提交审核结果。
  • 下图是课程编辑与发布的整体流程。
    code

    1.4界面原型

  • 产品工程师根据用户需求制作产品界面原型,开发工程师除了根据用户需求进行需求分析以外,还会根据界面原型上的元素信息进行需求分析。
    内容管理模块的界面原型如下
    • 课程列表 :
      code
    • 点击添加课程:
      code
    • 选择录播课程 ,填写课程信息:
      code
    • 填写课程计划信息:
      code
    • 填写课程师资信息:
      code
    • 课程填写完毕进行课程发布:
    • 当审核状态为通过时发布按钮点亮,点击发布按钮 即可对该课程进行发布。
      code

    1.5数据原型

  • 内容管理模块的基础表涉及9张,如下:
    code

    2.创建工程模块

    2.1工程模块结构

  • 结合项目父工程、项目基础工程后,如下图:
    code
  • xuecheng-plus-content-api:接口工程,为前端提供接口。
  • xuecheng-plus-content-service: 业务工程,为接口工程提供业务支撑。
  • xuecheng-plus-content-model: 数据模型工程,存储数据模型类、数据传输类型等。
  • xuecheng-plus-content:内容管理模块工程,负责聚合xuecheng-plus-content-api、xuecheng-plus-content-service、xuecheng-plus-content-model。

    2.2创建工程

    创建xuecheng-plus-content
    • 作用聚合三个子工程
    • pom文件
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
      <artifactId>xuecheng-plus-parent</artifactId>
      <groupId>com.xuecheng</groupId>
      <version>0.0.1-SNAPSHOT</version>
      <relativePath>../xuecheng-plus-parent</relativePath>
      </parent>
      <artifactId>xuecheng-plus-content</artifactId>
      <name>xuecheng-plus-content</name>
      <description>xuecheng-plus-content</description>
      <packaging>pom</packaging>

      <modules>
      <module>xuecheng-plus-content-api</module>
      <module>xuecheng-plus-content-model</module>
      <module>xuecheng-plus-content-service</module>
      </modules>
      </project>
    创建xuecheng-plus-content-model
    • pom文件
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
      <artifactId>xuecheng-plus-content</artifactId>
      <groupId>com.xuecheng</groupId>
      <version>0.0.1-SNAPSHOT</version>
      </parent>
      <artifactId>xuecheng-plus-content-model</artifactId>


      <dependencies>
      <dependency>
      <groupId>com.xuecheng</groupId>
      <artifactId>xuecheng-plus-base</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      </dependency>
      </dependencies>

      </project>
    创建xuecheng-plus-content-service
    • pom文件
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
      <artifactId>xuecheng-plus-content</artifactId>
      <groupId>com.xuecheng</groupId>
      <version>0.0.1-SNAPSHOT</version>
      </parent>
      <artifactId>xuecheng-plus-content-service</artifactId>

      <dependencies>
      <dependency>
      <groupId>com.xuecheng</groupId>
      <artifactId>xuecheng-plus-content-model</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      </dependency>
      </dependencies>
      </project>
    创建xuecheng-plus-content-api
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <artifactId>xuecheng-plus-content</artifactId>
    <groupId>com.xuecheng</groupId>
    <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>xuecheng-plus-content-api</artifactId>


    <dependencies>
    <dependency>
    <groupId>com.xuecheng</groupId>
    <artifactId>xuecheng-plus-content-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    </dependency>
    </dependencies>

    </project>
  • 目前结构
    code

    2.3前后端联调

  • 这里进行前后联调的目的是体会前后端联调的流程,测试的功能为课程查询功能。
  1. 启动前端工程,再启内容管理服务端。
  2. 修改服务端地址
  • 前端默认连接的是项目的网关地址,由于现在网关工程还没有创建,这里需要更改前端工程的参数配置文件 ,修改网关地址为内容管理服务的地址。
    code
  • npm install 下载依赖
  • npm run serve 启动前端工程
  • http://localhost:8601/#/organization/course-list 访问

3.课程查询

3.0生成po类

  • 导入xuecheng-plus-generator
  • 添加为maven
  • 找到ContentCodeGenerator类,修改数据库及要生成的表,执行方法
  • 把生成的generator里面类拷贝到modle的po里面
    出现mp注解爆红
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     <!--存在mybatisplus注解添加相关注解保证不报错-->  
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-annotation</artifactId>
                <version>${mybatis-plus-boot-starter.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-core</artifactId>
                <version>${mybatis-plus-boot-starter.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>

    3.1需求分析

    code
    下边从查询条件、查询列表两个方面分析数据模型
  1. 查询条件:
  • 包括:课程名称、课程审核状态、课程发布状态
  • 课程名称:可以模糊搜索
  • 课程审核状态:未提交、已提交、审核通过、审核未通过
  • 课程发布状态:未发布、已发布、已下线
  • 因为是分页查询所以查询条件中还要包括当前页码、每页显示记录数。
  1. 查询结果:
  • 查询结果中包括:课程id、课程名称、任务数、创建时间、是否付费、审核状态、类型,操作
  • 任务数:该课程所包含的课程计划数,即课程章节数。
  • 是否付费:课程包括免费、收费两种。
  • 类型:录播、直播。
  • 因为是分页查询所以查询结果中还要包括总记录数、当前页、每页显示记录数。
    course_base表

    code

    3.2接口定义

  • 接口设计
    接口设计
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    POST /content/course/list?pageNo=2&pageSize=1  
    Content-Type: application/json

    {
      "auditStatus": "202002",
      "courseName": "",
      "publishStatus":""
    }
    ###成功响应结果
    {
      "items": [
        {
          "id": 26,
          "companyId": 1232141425,
          "companyName": null,
          "name": "spring cloud实战",
          "users": "所有人",
          "tags": null,
          "mt": "1-3",
          "mtName": null,
          "st": "1-3-2",
          "stName": null,
          "grade": "200003",
          "teachmode": "201001",
          "description": "本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心eureka。",
          "pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
          "createDate": "2019-09-04 09:56:19",
          "changeDate": "2021-12-26 22:10:38",
          "createPeople": null,
          "changePeople": null,
          "auditStatus": "202002",
          "auditMind": null,
          "auditNums": 0,
          "auditDate": null,
          "auditPeople": null,
          "status": 1,
          "coursePubId": null,
          "coursePubDate": null
        }
      ],
      "counts": 23,
      "page": 2,
      "pageSize": 1
    }
  • 定义模型类
    接收分页查询模型类 --由于多个地方用到所有定义在base里
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.xuecheng.base.model;  
    //分页查询参数
    import lombok.Data;
    import lombok.ToString;

    @Data
    @ToString
    public class PageParams {
    //当前页码
    private Long pageNo = 1L;
    //每页记录数默认值
    private Long pageSize =10L;

    public PageParams(){
    }
    public PageParams(long pageNo,long pageSize){
    this.pageNo = pageNo;
    this.pageSize = pageSize;
    }
    }
    接收课程查询特有的参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.xuecheng.content.model.dto;  
    import lombok.Data;
    import lombok.ToString;
    //课程查询条件模型类
    @Data
    @ToString
    public class QueryCourseParamsDto {
    //审核状态
    private String auditStatus;
    //课程名称
    private String courseName;
    //发布状态
    private String publishStatus;

    }
    创建接收响应体参数 --针对分页查询结果经过分析也存在固定的数据和格式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.xuecheng.base.model;  
    import lombok.Data;
    import lombok.ToString;
    import java.io.Serializable;
    import java.util.List;

    //分页查询结果模型类
    @Data
    @ToString
    public class PageResult<T> implements Serializable {
    // 数据列表
    private List<T> items;
    //总记录数
    private long counts;
    //当前页码
    private long page;
    //每页记录数
    private long pageSize;
    public PageResult(List<T> items, long counts, long page, long pageSize) {
    this.items = items;
    this.counts = counts;
    this.page = page;
    this.pageSize = pageSize;
    }
    }
  1. 导入依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <!--cloud的基础环境包-->  
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-context</artifactId>
        </dependency>
        <!-- Spring Boot 的 Spring Web MVC 集成 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        
        <!-- 排除 Spring Boot 依赖的日志包冲突 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
            </dependency>
        <!-- Spring Boot 集成 log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <!-- Spring Boot 集成 swagger -->
        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>
  2. 创建CourseBaseInfoController
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.xuecheng.content.api;  

    import com.xuecheng.base.model.PageParams;
    import com.xuecheng.base.model.PageResult;
    import com.xuecheng.content.model.dto.QueryCourseParamsDto;
    import com.xuecheng.content.model.po.CourseBase;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    public class CourseBaseInfoController {
    @PostMapping("/course/list")
    public PageResult<CourseBase> list(PageParams pageParams,@RequestBody QueryCourseParamsDto queryCourseParamsDto){
    return null;
    }
    }
  3. 创建启动类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.xuecheng;  
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    @SpringBootApplication
    public class ContentApplication {
    public static void main(String[] args) {
    SpringApplication.run(ContentApplication.class,args);
    }
    }
  4. 创建配置类
  • 把日志文件复制到resource
  • 创建bootstrap.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    server:  
    servlet:
    context-path: /content
    port: 63040
    #微服务配置
    spring:
    application:
    name: content-api
    datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.101.65:3306/xcplus_content?serverTimezone=UTC&userUnicode=true&useSSL=false&
    username: root
    password: mysql
    # 日志文件配置路径
    logging:
    config: classpath:log4j2-dev.xml

    3.3开发持久层

  • 导入上回生成的mapper文件
  • 导入依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <!-- MySQL 驱动 -->  
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatis plus的依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
       <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-context</artifactId>
      </dependency>
        <!-- Spring Boot 集成 Junit -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 排除 Spring Boot 依赖的日志包冲突 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Spring Boot 集成 log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
  • 把api模块的启动类和recourse拷贝到service模块中
  • 修改yaml文件 —去掉端口
  • 配置分页插件的配置类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.xuecheng.content.config;  
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    @MapperScan("com.xuecheng.content.mapper")
    public class MybatisPlusConfig {
       /**
        * 定义分页拦截器
        */
       @Bean
       public MybatisPlusInterceptor mybatisPlusInterceptor() {
          MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
          interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
          return interceptor;
       }
    }
  • 分页插件的原理

    • 首先分页参数放到ThreadLocal中,拦截执行的sql,根据数据库类型添加对应的分页语句重写sql,例如:(select from table where a) 转换为 (select count(※) from table where a)和(select from table where a limit ,)
      计算出了total总条数、pageNum当前第几页、pageSize每页大小和当前页的数据,是否为首页,是否为尾页,总页数等。
  • 创建测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    package com.xuecheng.content;  

    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.xuecheng.base.model.PageParams;
    import com.xuecheng.base.model.PageResult;
    import com.xuecheng.content.mapper.CourseBaseMapper;
    import com.xuecheng.content.model.dto.QueryCourseParamsDto;
    import com.xuecheng.content.model.po.CourseBase;
    import org.apache.commons.lang.StringUtils;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;

    import java.util.List;


    @SpringBootTest
    public class CourseBaseMapperTests {

    @Autowired
    CourseBaseMapper courseBaseMapper;
    @Test
    public void testCourseBaseMapper(){
    CourseBase courseBase = courseBaseMapper.selectById(18);
    Assertions.assertNotNull(courseBase);

    //详细进行分页查询的单元测试
    //查询条件
    QueryCourseParamsDto courseParamsDto = new QueryCourseParamsDto();
    courseParamsDto.setCourseName("java");//课程名称查询条件
    //拼装查询条件
    LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
    //根据名称模糊查询,在sql中拼接 course_base.name like '%值%'
    queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName())
    ,CourseBase::getName
    ,courseParamsDto.getCourseName());
    //根据课程审核状态查询,在sql中拼接 course_base.audit_status = ? queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus())
    ,CourseBase::getAuditStatus
    ,courseParamsDto.getAuditStatus());

    //分页参数对象
    PageParams pageParams = new PageParams();
    pageParams.setPageNo(1L);
    pageParams.setPageSize(2L);

    //创建Page分页参数对象 ,参数:页码和每页记录数
    Page<CourseBase> Page = new Page<>(pageParams.getPageNo(),pageParams.getPageSize());
    //开始进行分页查询
    Page<CourseBase> pageResult = courseBaseMapper.selectPage(Page, queryWrapper);
    //数据列表
    List<CourseBase> items = pageResult.getRecords();
    //总记录数
    long total = pageResult.getTotal();

    //List<T> items , Long counts , Long page , Long ,pageSize
    PageResult<CourseBase> courseBasePageResult =
    new PageResult<CourseBase>(items,total,pageParams.getPageNo()
    ,pageParams.getPageSize());
    System.out.println(courseBasePageResult);
    }
    }

    3.4开发业务层

    数字词典
    • 把数字定义为专门的汉字,方便以后修改
      code
    创建CourseBaseInfoService
    1
    2
    3
    4
    public interface CourseBaseInfoService {  
    //课程分页查询
    public PageResult<CourseBase> queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto queryCourseParamsDto);
    }
    CourseBaseInfoServiceImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    package com.xuecheng.content.service.impl;    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.xuecheng.base.model.PageParams;
    import com.xuecheng.base.model.PageResult;
    import com.xuecheng.content.mapper.CourseBaseMapper;
    import com.xuecheng.content.model.dto.QueryCourseParamsDto;
    import com.xuecheng.content.model.po.CourseBase;
    import com.xuecheng.content.service.CourseBaseInfoService;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    import java.util.List;
    @Slf4j
    @Service
    public class CourseBaseInfoServiceImpl implements CourseBaseInfoService {
    @Autowired
    CourseBaseMapper courseBaseMapper;

    //课程分页查询和条件查询
    @Override
    public PageResult<CourseBase> queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto courseParamsDto) {
    //拼装查询条件
    LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
    //根据名称模糊查询,在sql中拼接 course_base.name like '%值%'
    queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName())
    ,CourseBase::getName
    ,courseParamsDto.getCourseName());
    //根据课程审核状态查询,在sql中拼接 course_base.audit_status = ? queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus())
    ,CourseBase::getAuditStatus
    ,courseParamsDto.getAuditStatus());
    //根据课程状态查询
    queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getPublishStatus())
    ,CourseBase::getStatus
    ,courseParamsDto.getPublishStatus());


    //创建Page分页参数对象 ,参数:页码和每页记录数
    Page<CourseBase> Page = new Page<>(pageParams.getPageNo(),pageParams.getPageSize());
    //开始进行分页查询
    Page<CourseBase> pageResult = courseBaseMapper.selectPage(Page, queryWrapper);
    //数据列表
    List<CourseBase> items = pageResult.getRecords();
    //总记录数
    long total = pageResult.getTotal();

    //List<T> items , Long counts , Long page , Long ,pageSize
    PageResult<CourseBase> courseBasePageResult =
    new PageResult<CourseBase>(items,total,pageParams.getPageNo()
    ,pageParams.getPageSize());
    System.out.println(courseBasePageResult);
    return courseBasePageResult;
    }
    }
    创建CourseBaseInfoServiceTests测试类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @SpringBootTest  
    public class CourseBaseInfoServiceTests {

    @Autowired
    CourseBaseInfoService courseBaseInfoService;
    @Test
    public void testCourseBaseInfoService() {
    //查询条件
    QueryCourseParamsDto courseParamsDto = new QueryCourseParamsDto();
    courseParamsDto.setCourseName("java");//课程名称查询条件
    //分页参数对象
    PageParams pageParams = new PageParams();
    pageParams.setPageNo(1L);
    pageParams.setPageSize(2L);

    PageResult<CourseBase> courseBasePageResult = courseBaseInfoService.queryCourseBaseList(pageParams, courseParamsDto);
    System.out.println(courseBasePageResult);
    }
    }
    完善接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Api(value = "课程信息管理接口",tags = "课程信息管理接口")  
    @RestController
    public class CourseBaseInfoController {

    @Autowired
    CourseBaseInfoService courseBaseInfoService;

    @ApiOperation("课程查询接口")
    @PostMapping("/course/list")
    public PageResult<CourseBase> list(PageParams pageParams,
    @RequestBody(required = false) QueryCourseParamsDto queryCourseParamsDto){
    PageResult<CourseBase> courseBasePageResult =
    courseBaseInfoService.queryCourseBaseList(pageParams,queryCourseParamsDto);
    return courseBasePageResult;
    }
    }
    Httpclient测试

    code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    {
    "dev": {
    "access_token": "",
    "gateway_host": "localhost:63010",
    "content_host": "localhost:63040",
    "system_host": "localhost:63110",
    "media_host": "localhost:63050",
    "search_host": "localhost:63080",
    "auth_host": "localhost:63070",
    "checkcode_host": "localhost:63075",
    "learning_host": "localhost:63020"
    }
    }

    ### 查询课程信息
    POST {{gateway_host}}/content/course/list?pageNo=1&pageSize=2
    Content-Type: application/json

    {
    "auditStatus": "202004",
    "courseName": "java",
    "publishStatus": "203001"
    }

  • 前后端联调
    • 解压xuecheng-plus-system.zip
    • pplication.yml修改数据库连接参数。
    • 启动服务 —数据字典

      4.课程分类查询

      4.1需求分析

      code
    • 课程等级、课程类型来源于数据字典表,此部分的信息前端已从系统管理服务读取。
    • 课程分类信息没有在数据字典表中存储,而是由单独一张课程分类表,存储在内容管理数据库中。
      课程分类表

      code

  • 通过前端查看的请求记录
    1
    2
    3
    4
    请求网址: http://localhost:8601/api/content/course-category/tree-nodes
    请求方法: GET
    状态代码: 404 Not Found
    远程地址: 127.0.0.1:8601
  • 接口返回值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
     [  
             {
                "childrenTreeNodes" : [
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-1",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "HTML/CSS",
                      "name" : "HTML/CSS",
                      "orderby" : 1,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-2",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "JavaScript",
                      "name" : "JavaScript",
                      "orderby" : 2,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-3",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "jQuery",
                      "name" : "jQuery",
                      "orderby" : 3,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-4",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "ExtJS",
                      "name" : "ExtJS",
                      "orderby" : 4,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-5",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "AngularJS",
                      "name" : "AngularJS",
                      "orderby" : 5,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-6",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "ReactJS",
                      "name" : "ReactJS",
                      "orderby" : 6,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-7",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "Bootstrap",
                      "name" : "Bootstrap",
                      "orderby" : 7,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-8",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "Node.js",
                      "name" : "Node.js",
                      "orderby" : 8,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-9",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "Vue",
                      "name" : "Vue",
                      "orderby" : 9,
                      "parentid" : "1-1"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-1-10",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "其它",
                      "name" : "其它",
                      "orderby" : 10,
                      "parentid" : "1-1"
                   }
                ],
                "id" : "1-1",
                "isLeaf" : null,
                "isShow" : null,
                "label" : "前端开发",
                "name" : "前端开发",
                "orderby" : 1,
                "parentid" : "1"
             },
             {
                "childrenTreeNodes" : [
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-1",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "微信开发",
                      "name" : "微信开发",
                      "orderby" : 1,
                      "parentid" : "1-2"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-2",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "iOS",
                      "name" : "iOS",
                      "orderby" : 2,
                      "parentid" : "1-2"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-3",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "手游开发",
                      "name" : "手游开发",
                      "orderby" : 3,
                      "parentid" : "1-2"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-4",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "Swift",
                      "name" : "Swift",
                      "orderby" : 4,
                      "parentid" : "1-2"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-5",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "Android",
                      "name" : "Android",
                      "orderby" : 5,
                      "parentid" : "1-2"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-6",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "ReactNative",
                      "name" : "ReactNative",
                      "orderby" : 6,
                      "parentid" : "1-2"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-7",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "Cordova",
                      "name" : "Cordova",
                      "orderby" : 7,
                      "parentid" : "1-2"
                   },
                   {
                      "childrenTreeNodes" : null,
                      "id" : "1-2-8",
                      "isLeaf" : null,
                      "isShow" : null,
                      "label" : "其它",
                      "name" : "其它",
                      "orderby" : 8,
                      "parentid" : "1-2"
                   }
                ],
                "id" : "1-2",
                "isLeaf" : null,
                "isShow" : null,
                "label" : "移动开发",
                "name" : "移动开发",
                "orderby" : 2,
                "parentid" : "1"
             }
       ]
  • 根据以上接收数据构建出模型类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.xuecheng.content.model.dto;  
    import com.xuecheng.content.model.po.CourseCategory;
    import lombok.Data;
    import java.util.List;

    @Data
    public class CourseCategoryTreeDto extends CourseCategory implements java.io.Serializable{
    List<CourseCategoryTreeDto> childrenTreeNodes;
    }

    4.2接口定义

  • 创建CourseCategoryController类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.xuecheng.content.api;  
    import com.xuecheng.content.model.dto.CourseCategoryTreeDto;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.List;

    @RestController
    public class CourseCategoryController {
    @GetMapping("/course-category/tree-nodes")
    public List<CourseCategoryTreeDto> queryTreeNodes() {
    return null;
    }
    }

    4.3接口实现

  • 树型表的查询方法
    • 如果树的层级固定可以使用表的自链接去查询—-(2层)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      select  
             one.id            one_id,
             one.name          one_name,
             one.parentid      one_parentid,
             one.orderby       one_orderby,
             one.label         one_label,
             two.id            two_id,
             two.name          two_name,
             two.parentid      two_parentid,
             two.orderby       two_orderby,
             two.label         two_label
         from course_category one
                  inner join course_category two on one.id = two.parentid
         where one.parentid = 1
           and one.is_show = 1
           and two.is_show = 1
         order by one.orderby,
                  two.orderby
    • 如果树的层级不确定,此时可以使用MySQL递归实现
      1
      2
      3
      4
      5
      6
      with recursive t1 as (  
      select * from  course_category p where  id= '1' //初始数据
      union all //不断将每次递归得到的数据加入到表中
      select t.* from course_category t inner join t1 on t1.id = t.parentid
      )
      select * from t1 order by t1.id, t1.orderby
      code
  • Mapper层
    • CourseCategoryMapper接口
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      package com.xuecheng.content.mapper;  
      import com.baomidou.mybatisplus.core.mapper.BaseMapper;
      import com.xuecheng.content.model.dto.CourseCategoryTreeDto;
      import com.xuecheng.content.model.po.CourseCategory;
      import org.apache.ibatis.annotations.Mapper;
      import java.util.List;

      public interface CourseCategoryMapper extends BaseMapper<CourseCategory> {
      //使用递归查询分类
      public List<CourseCategoryTreeDto> selectTreeNodes(String id);
      }
    • CourseCategoryMapper.xml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
        <?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.CourseCategoryMapper">

      <!-- 通用查询映射结果 -->
      <resultMap id="BaseResultMap" type="com.xuecheng.content.model.po.CourseCategory">
      <id column="id" property="id" />
      <result column="name" property="name" />
      <result column="label" property="label" />
      <result column="parentid" property="parentid" />
      <result column="is_show" property="isShow" />
      <result column="orderby" property="orderby" />
      <result column="is_leaf" property="isLeaf" />
      </resultMap>

      <!-- 通用查询结果列 -->
      <sql id="Base_Column_List">
      id, name, label, parentid, is_show, orderby, is_leaf </sql>

      <select id="selectTreeNodes" parameterType="string" resultType="com.xuecheng.content.model.dto.CourseCategoryTreeDto">
      with recursive t1 as (
      select * from course_category p where id= #{id}
      union all
      select t.* from course_category t inner join t1 on t1.id = t.parentid
      )
      select * from t1 order by t1.id, t1.orderby
      </select>
      </mapper>
    • CourseCategoryMapperTests测试类
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      @SpringBootTest  
      public class CourseCategoryMapperTests {
      @Autowired
      CourseCategoryMapper courseCategoryMapper;

      @Test
      public void testCourseCategoryMapper(){
      List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes("1");
      System.out.println(courseCategoryTreeDtos);
      }

      }
  • Server层
    • CourseCategoryService接口
      1
      2
      3
      4
      public interface CourseCategoryService {  
      //课程分类树型结构查询
      public List<CourseCategoryTreeDto> queryTreeNodes(String id);
      }
    • CourseCategoryServiceImpl
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      @Service  
      @Slf4j
      public class CourseCategoryServiceImpl implements CourseCategoryService {

      @Autowired
      CourseCategoryMapper courseCategoryMapper;
      @Override
      public List<CourseCategoryTreeDto> queryTreeNodes(String id) {
      //调用mapper递归查询出分类信息
      List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes(id);

      //找到每个节点的子节点,最终封装成List<CourseCategoryTreeDto>
      //先将list转成map,key就是结点的id,value就是CourseCategoryTreeDto对象,目的就是为了方便从map获取结点,filter(item->!id.equals(item.getId()))把根结点排除
      Map<String, CourseCategoryTreeDto> mapTemp = courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2));
      //定义一个list作为最终返回的list
      List<CourseCategoryTreeDto> courseCategoryList = new ArrayList<>();
      //从头遍历 List<CourseCategoryTreeDto> ,一边遍历一边找子节点放在父节点的childrenTreeNodes
      courseCategoryTreeDtos.stream().filter(item -> !id.equals(item.getId())).forEach(item -> {
      if (item.getParentid().equals(id)) {
      courseCategoryList.add(item);
      }
      //找到节点的父节点
      CourseCategoryTreeDto courseCategoryParent = mapTemp.get(item.getParentid());
      if(courseCategoryParent!=null){
      if(courseCategoryParent.getChildrenTreeNodes()==null){
      //如果该父节点的ChildrenTreeNodes属性为空要new一个集合,因为要向该集合中放它的子节点
      courseCategoryParent.setChildrenTreeNodes(new ArrayList<CourseCategoryTreeDto>());
      }
      //到每个节点的子节点放在父节点的childrenTreeNodes属性中
      courseCategoryParent.getChildrenTreeNodes().add(item);
      }
      });
      return courseCategoryList;
      }
      }
    • 测试
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      @SpringBootTest  
      public class CourseCategoryServiceTests {
      @Autowired
      CourseCategoryService categoryService;
      @Test
      public void CourseCategoryServiceTests() {
      List<CourseCategoryTreeDto> courseCategoryTreeDtos = categoryService.queryTreeNodes("1");
      System.out.println(courseCategoryTreeDtos);
      }
      }

      api-text
      ### 查询课程分类
      GET {{content_host}}/content/course-category/tree-nodes
  • Controller层
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RestController  
    public class CourseCategoryController {
    @Autowired
    CourseCategoryService categoryService;
    @GetMapping("/course-category/tree-nodes")
    public List<CourseCategoryTreeDto> queryTreeNodes() {
    return categoryService.queryTreeNodes("1");
    }
    }

5.新增课程

5.1需求分析

code

课程信息表和营销表

code
code

5.2接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
### 创建课程  
POST {{content_host}}/content/course
Content-Type: application/json

{

  "mt": "",
  "st": "",
  "name": "",
  "pic": "",
  "teachmode": "200002",
  "users": "初级人员",
  "tags": "",
  "grade": "204001",
  "description": "",
  "charge": "201000",
  "price": 0,
  "originalPrice":0,
  "qq": "",
  "wechat": "",
  "phone": "",
  "validDays": 365
}

###响应结果如下
#成功响应结果如下
{
  "id": 109,
  "companyId": 1,
  "companyName": null,
  "name": "测试课程103",
  "users": "初级人员",
  "tags": "",
  "mt": "1-1",
  "mtName": null,
  "st": "1-1-1",
  "stName": null,
  "grade": "204001",
  "teachmode": "200002",
  "description": "",
  "pic": "",
  "createDate": "2022-09-08 07:35:16",
  "changeDate": null,
  "createPeople": null,
  "changePeople": null,
  "auditStatus": "202002",
  "status": 1,
  "coursePubId": null,
  "coursePubDate": null,
  "charge": "201000",
  "price": null,
  "originalPrice":0,
  "qq": "",
  "wechat": "",
  "phone": "",
  "validDays": 365
}
  • 创建DTO
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    package com.xuecheng.content.model.dto;  
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    import javax.validation.constraints.NotEmpty;
    import javax.validation.constraints.Size;
    import java.math.BigDecimal;

    //@description 添加课程dto

    @Data
    @ApiModel(value="AddCourseDto", description="新增课程基本信息")
    public class AddCourseDto {

    @NotEmpty(message = "课程名称不能为空")
    @ApiModelProperty(value = "课程名称", required = true)
    private String name;

    @NotEmpty(message = "适用人群不能为空")
    @Size(message = "适用人群内容过少",min = 10)
    @ApiModelProperty(value = "适用人群", required = true)
    private String users;

    @ApiModelProperty(value = "课程标签")
    private String tags;

    @NotEmpty(message = "课程分类不能为空")
    @ApiModelProperty(value = "大分类", required = true)
    private String mt;

    @NotEmpty(message = "课程分类不能为空")
    @ApiModelProperty(value = "小分类", required = true)
    private String st;

    @NotEmpty(message = "课程等级不能为空")
    @ApiModelProperty(value = "课程等级", required = true)
    private String grade;

    @ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true)
    private String teachmode;

    @ApiModelProperty(value = "课程介绍")
    private String description;

    @ApiModelProperty(value = "课程图片", required = true)
    private String pic;

    @NotEmpty(message = "收费规则不能为空")
    @ApiModelProperty(value = "收费规则,对应数据字典", required = true)
    private String charge;

    @ApiModelProperty(value = "价格")
    private Float price;
    @ApiModelProperty(value = "原价")
    private Float originalPrice;


    @ApiModelProperty(value = "qq")
    private String qq;

    @ApiModelProperty(value = "微信")
    private String wechat;
    @ApiModelProperty(value = "电话")
    private String phone;

    @ApiModelProperty(value = "有效期")
    private Integer validDays;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package com.xuecheng.content.model.dto;  
    import com.xuecheng.content.model.po.CourseBase;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    import java.math.BigDecimal;

    //@description 课程基本信息dto
    @Data
    public class CourseBaseInfoDto extends CourseBase {
    //收费规则,对应数据字典
    private String charge;
    //价格
    private Float price;
    //原价
    private Float originalPrice;
    //咨询qq
    private String qq;
    //微信
    private String wechat;
    //电话
    private String phone;
    //有效期天数
    private Integer validDays;
    //大分类名称
    private String mtName;
    //小分类名称
    private String stName;
    }
  • 定义接口如下:
    1
    2
    3
    4
    5
    @ApiOperation("新增课程")  
    @PostMapping("/content/course")
    public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto){
    return null;
    }

5.3接口实现

  1. 定义service接口,这里额外需要一个机构id,因为我们的业务是教学机构登录账号,然后添加该教学机构的下属课程
    1
    2
    3
    4
    5
    6
    7
    /**
    * 新增课程基本信息
    * @param companyId 教学机构id
    * @param addCourseDto 课程基本信息
    * @return
    */
    CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto addCourseDto);
  2. 编写service接口实现类
    • 首先我们需要对请求参数做合法性校验,判断一下用户是否输入了必填项,还有一些项的默认值
    • 然后对请求参数进行封装,调用mapper进行数据持久化
    • 组装返回结果
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      @Resource
      CourseBaseMapper courseBaseMapper;

      @Resource
      CourseMarketMapper courseMarketMapper;

      @Resource
      CourseCategoryMapper courseCategoryMapper;

      @Override
      @Transactional
      public CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto addCourseDto) {
      // 1. 合法性校验
      if (StringUtils.isBlank(addCourseDto.getName())) {
      throw new RuntimeException("课程名称为空");
      }
      if (StringUtils.isBlank(addCourseDto.getMt())) {
      throw new RuntimeException("课程分类为空");
      }
      if (StringUtils.isBlank(addCourseDto.getSt())) {
      throw new RuntimeException("课程分类为空");
      }
      if (StringUtils.isBlank(addCourseDto.getGrade())) {
      throw new RuntimeException("课程等级为空");
      }
      if (StringUtils.isBlank(addCourseDto.getTeachmode())) {
      throw new RuntimeException("教育模式为空");
      }
      if (StringUtils.isBlank(addCourseDto.getUsers())) {
      throw new RuntimeException("适应人群为空");
      }
      if (StringUtils.isBlank(addCourseDto.getCharge())) {
      throw new RuntimeException("收费规则为空");
      }
      // 2. 封装请求参数
      // 封装课程基本信息
      CourseBase courseBase = new CourseBase();
      BeanUtils.copyProperties(addCourseDto, courseBase);
      // 2.1 设置默认审核状态(去数据字典表中查询状态码)
      courseBase.setAuditStatus("202002");
      // 2.2 设置默认发布状态
      courseBase.setStatus("203001");
      // 2.3 设置机构id
      courseBase.setCompanyId(companyId);
      // 2.4 设置添加时间
      courseBase.setCreateDate(LocalDateTime.now());
      // 2.5 插入课程基本信息表
      int baseInsert = courseBaseMapper.insert(courseBase);
      Long courseId = courseBase.getId();
      // 封装课程营销信息
      CourseMarket courseMarket = new CourseMarket();
      BeanUtils.copyProperties(addCourseDto, courseMarket);
      courseMarket.setId(courseId);
      // 2.6 判断收费规则,若课程收费,则价格必须大于0
      String charge = courseMarket.getCharge();
      if ("201001".equals(charge)) {
      Float price = addCourseDto.getPrice();
      if (price == null || price.floatValue() <= 0) {
      throw new RuntimeException("课程设置了收费,价格不能为空,且必须大于0");
      }
      }
      // 2.7 插入课程营销信息表
      int marketInsert = courseMarketMapper.insert(courseMarket);
      if (baseInsert <= 0 || marketInsert <= 0) {
      throw new RuntimeException("新增课程基本信息失败");
      }
      // 3. 返回添加的课程信息
      return getCourseBaseInfo(courseId);
      }

      private CourseBaseInfoDto getCourseBaseInfo(Long courseId) {
      CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto();
      // 1. 根据课程id查询课程基本信息
      CourseBase courseBase = courseBaseMapper.selectById(courseId);
      if (courseBase == null)
      return null;
      // 1.1 拷贝属性
      BeanUtils.copyProperties(courseBase, courseBaseInfoDto);
      // 2. 根据课程id查询课程营销信息
      CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
      // 2.1 拷贝属性
      if (courseMarket != null)
      BeanUtils.copyProperties(courseMarket, courseBaseInfoDto);
      // 3. 查询课程分类名称,并设置属性
      // 3.1 根据小分类id查询课程分类对象
      CourseCategory courseCategoryBySt = courseCategoryMapper.selectById(courseBase.getSt());
      // 3.2 设置课程的小分类名称
      courseBaseInfoDto.setStName(courseCategoryBySt.getName());
      // 3.3 根据大分类id查询课程分类对象
      CourseCategory courseCategoryByMt = courseCategoryMapper.selectById(courseBase.getMt());
      // 3.4 设置课程大分类名称
      courseBaseInfoDto.setMtName(courseCategoryByMt.getName());
      return courseBaseInfoDto;
      }
  3. 完善controller
    1
    2
    3
    4
    5
    6
    7
    @ApiOperation("新增课程基础信息接口")
    @PostMapping("/course")
    public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto) {
    // 机构id,暂时硬编码模拟假数据
    Long companyId = 1232141425L;;
    return courseBaseInfoService.createCourseBase(companyId, addCourseDto);
    }
  4. 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ### 新增课程  
    POST {{content_host}}/content/course
    Content-Type: application/json

    {
    "charge": "201001",
    "price": 10,
    "originalPrice":100,
    "qq": "22333",
    "wechat": "223344",
    "phone": "13333333",
    "validDays": 365,
    "mt": "1-1",
    "st": "1-1-1",
    "name": "java网络编程高级",
    "pic": "asd",
    "teachmode": "200002",
    "users": "初级人员",
    "tags": "aaa",
    "grade": "204001",
    "description": "java网络编程高级"
    }

6.自定义异常处理

6.1设置通用信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.xuecheng.base.exception;  

public enum CommonError {
UNKOWN_ERROR("执行过程异常,请重试。"),
PARAMS_ERROR("非法参数"),
OBJECT_NULL("对象为空"),
QUERY_NULL("查询结果为空"),
REQUEST_NULL("请求参数为空");

private String errMessage;

public String getErrMessage() {
return errMessage;
}
private CommonError( String errMessage) {
this.errMessage = errMessage;
}
}

6.2自定义异常类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.xuecheng.base.exception;  
public class XueChengPlusException extends RuntimeException {
private String errMessage;
public XueChengPlusException() {
super();
}
public XueChengPlusException(String errMessage) {
super(errMessage);
this.errMessage = errMessage;
}
public String getErrMessage() {
return errMessage;
}
public static void cast(CommonError commonError){
throw new XueChengPlusException(commonError.getErrMessage());
}
public static void cast(String errMessage){
throw new XueChengPlusException(errMessage);
}
}

6.3响应用户的统一类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.xuecheng.base.exception;  
import java.io.Serializable;
//和前端约定返回的异常信息模型
public class RestErrorResponse implements Serializable {
private String errMessage;
public RestErrorResponse(String errMessage){
this.errMessage= errMessage;
}
public String getErrMessage() {
return errMessage;
}
public void setErrMessage(String errMessage) {
this.errMessage = errMessage;
}
}

6.4全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.xuecheng.base.exception;  
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@Slf4j
@ControllerAdvice
//@RestControllerAdvice
public class GlobalExceptionHandler {
//对项目的自定义异常类型进行处理
@ResponseBody
@ExceptionHandler(XueChengPlusException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse customException(XueChengPlusException e){
//记录异常
log.error("系统异常{}",e.getErrMessage(),e);
//..
//解析出异常信息
String errMessage = e.getErrMessage();
RestErrorResponse restErrorResponse = new RestErrorResponse(errMessage);
return restErrorResponse;
}

@ResponseBody
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse exception(Exception e){
//记录异常
log.error("系统异常{}",e.getMessage(),e);
//解析出异常信息
RestErrorResponse restErrorResponse = new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());
return restErrorResponse;
}
}

7.JSR303校验

  1. Contoller中校验请求参数的合法性,包括:必填项校验,数据格式校验,比如:是否是符合一定的日期格式,等。
  2. Service中要校验的是业务规则相关的内容,比如:课程已经审核通过所以提交失败。
  3. Service中根据业务规则去校验不方便写成通用代码,Controller中则可以将校验的代码写成通用代码。
  • 依赖
    1
    2
    3
    4
    <dependency>  
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    在实体类添加注解

    code

  • 在参数前面添加@Validated注解
  • 在全局异常处理器中捕获异常,解析出异常信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     @ResponseBody  
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse exception(Exception e){

    //记录异常
    log.error("系统异常{}",e.getMessage(),e);

    //解析出异常信息
    RestErrorResponse restErrorResponse = new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());
    return restErrorResponse;
    }

    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse MethodArgumentNotValidException(MethodArgumentNotValidException e){

    //存储错误信息
    List<String> errors = new ArrayList<>();
    BindingResult bindingResult = e.getBindingResult();
    bindingResult.getFieldErrors().stream().forEach(item->{
    errors.add(item.getDefaultMessage());
    });

    //将list中的错误信息拼接起来
    String errMessage = StringUtils.join(errors,",");

    //记录异常
    log.error("系统异常{}",e.getMessage(),errMessage);

    //解析出异常信息
    RestErrorResponse restErrorResponse = new RestErrorResponse(errMessage);
    return restErrorResponse;
    }
  • 当有多个接口同时使用一个模型类会出现冲突 —分组校验
  • 定义不同分组
    1
    2
    3
    4
    5
    public class ValidationGroups {  
     public interface Inster{};
     public interface Update{};
     public interface Delete{};
    }
  • 模型类
    1
    2
    3
    4
    5
    @NotEmpty(groups = {ValidationGroups.Inster.class},message = "添加课程名称不能为空")  
     @NotEmpty(groups = {ValidationGroups.Update.class},message = "修改课程名称不能为空")
    // @NotEmpty(message = "课程名称不能为空")
     @ApiModelProperty(value = "课程名称", required = true)
     private String name;
  • 使用
    1
    2
    3
    4
    5
    6
    7
    @ApiOperation("新增课程基础信息")  
    @PostMapping("/course")
    public CourseBaseInfoDto createCourseBase(@RequestBody @Validated({ValidationGroups.Inster.class}) AddCourseDto addCourseDto){
        //机构id,由于认证系统没有上线暂时硬编码
        Long companyId = 1L;
      return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
    }

    8.修改课程

    8.1需求分析

  1. 点击课程列表查询
  2. 点击编辑,此时应该看到对应数据的回显数据
  3. 点击保存,完成修改
  • 修改课程还是涉及到之前的课程基本信息表和课程营销信息表
  • 但是修改课程提交的数据比新增课程多了一项课程id,因为修改课程需要对某个具体的课程进行修改,新增课程的课程id是系统生成的
  • 修改完成保存数据时,还需要更新课程基本信息表中的修改时间
    课程信息表和营销表

    code
    code

    8.2接口定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    ###查询课程信息
    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
    #}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    ### 修改课程  
    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
    #}

    8.3接口实现

    查询接口
    • 由于在前面的新增课程中已经实现了查询方法,所有只需要在service中暴露接口即可
    1. CourseBaseInfoController
      1
      2
      3
      4
      5
      6
      @ApiOperation("根据课程id查询接口")  
      @GetMapping("/course/{courseId}")
      public CourseBaseInfoDto getCourseBaseInfo(@PathVariable Long courseId){
      CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.getCourseBaseInfo(courseId);
      return courseBaseInfo;
      }
    2. 暴露接口
      1
      2
      //根据id查询课程信息  
      public CourseBaseInfoDto getCourseBaseInfo(Long couseId);
    3. 前面实现的方法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      //查询课程信息  
      public CourseBaseInfoDto getCourseBaseInfo(Long couseId){
      //从课程基本信息表查询
      CourseBase courseBase = courseBaseMapper.selectById(couseId);
      if (courseBase == null){
      return null;
      }
      //从课程营销表查询
      CourseMarket courseMarket = courseMarketMapper.selectById(couseId);
      //组装在一起
      CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto();
      BeanUtils.copyProperties(courseBase,courseBaseInfoDto);
      if (courseMarket!=null){
      BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);
      }
      //通过courseCategoryMapper查询分类信息,将分类名称放在对象中
      //todo:课程分类的名称设置到courseBaseInfoDto
      return courseBaseInfoDto;
      }
修改接口
  1. 创建一个Dto
    1
    2
    3
    4
    5
    6
    7
    8
    package com.xuecheng.content.model.dto;  
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    @Data
    public class EditCourseDto extends AddCourseDto{
    @ApiModelProperty(value = "课程id", required = true)
    private Long id;
    }
  2. CourseBaseInfoController
    1
    2
    3
    4
    5
    6
    7
    8
    @ApiOperation("修改课程接口")  
    @PutMapping("/course")
    public CourseBaseInfoDto modifyCourseBase(@RequestBody EditCourseDto editCourseDto){
    //获取到用户所属机构的id
    Long companyId = 1232141425L;
    CourseBaseInfoDto courseBaseInfoDto = courseBaseInfoService.updateCourseBase(companyId,editCourseDto);
    return courseBaseInfoDto;
    }
  3. CourseBaseInfoService
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //修改课程  
    public CourseBaseInfoDto updateCourseBase(Long companyId, EditCourseDto editCourseDto);

    //修改课程
    @Override
    public CourseBaseInfoDto updateCourseBase(Long companyId, EditCourseDto editCourseDto) {

    //拿到课程id
    Long courseId = editCourseDto.getId();
    //查询课程信息
    CourseBase courseBase = courseBaseMapper.selectById(courseId);
    if (courseId == null) {
    XueChengPlusException.cast("课程不存在");
    }
    //数据合法性校验
    //根据具体的业务逻辑去校验(本机构只能修改本机构的课程)
    if (!companyId.equals(courseBase.getCompanyId())){
    XueChengPlusException.cast("本机构只能修改本机构的课程");
    }
    //封装数据
    BeanUtils.copyProperties(editCourseDto,courseBase);
    //修改时间
    courseBase.setChangeDate(LocalDateTime.now());
    //更新数据库
    int i = courseBaseMapper.updateById(courseBase);
    if (i <= 0){
    XueChengPlusException.cast("修改课程失败");
    }
    //更新营销信息
    //todo:...
    //查询课程信息
    CourseBaseInfoDto courseBaseInfo = getCourseBaseInfo(courseId);
    return courseBaseInfo;
    }

9.查询课程计划

9.1需求分析

code

课程计划表teachplan

code


课程计划与媒资表关联表teachplan_media

code


媒资表teachplan_work

code

9.2接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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
      }
   ]
  • 定义数据模型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.xuecheng.content.model.dto;  
    import com.xuecheng.content.model.po.Teachplan;
    import com.xuecheng.content.model.po.TeachplanMedia;
    import lombok.Data;
    import java.util.List;
    @Data
    public class TeachplanDto extends Teachplan {
    //与媒资管理的信息
    private TeachplanMedia teachplanMedia;
    //小章节list
    private List<TeachplanDto> teachplanDtos;
    }
  • TeachplanController
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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;
    import java.util.List;

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

    9.3接口实现

  • 树型结构的实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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接口
    1
    2
    //课程计划查询  
    public List<TeachplanDto> selectTreeNodes(Long courseId);
  • 实现mapper接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    <!-- 课程分类树型结构查询映射结果 -->  
    <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="startTime" />
    <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="teachplanDtos" 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="startTime" />
    <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" 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
                     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>
  • 单元测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //课程计划mapper测试  
    @SpringBootTest
    public class TeachplanMapperTests {

    @Autowired
    TeachplanMapper teachplanMapper;

    @Test
    public void testSelectTreeNodes(){
    List<TeachplanDto> teachplanDtos = teachplanMapper.selectTreeNodes(117L);
    System.out.println(teachplanDtos);
    }
    }
  • service层实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    T//根据课程id查询课程计划  
    public List<TeachplanDto> findTeachplanTree(Long courseId);

    @Service
    public class findTeachplanTreeImpl implements TeachplanService {
    @Autowired
    TeachplanMapper teachplanMapper;

    @Override
    public List<TeachplanDto> findTeachplanTree(Long courseId) {
    List<TeachplanDto> teachplanDtos = teachplanMapper.selectTreeNodes(courseId);
    return teachplanDtos;
    }

    }

    10.新增修改课程计划

    10.1需求分析

  • 添加大章节小章节修改章节信息
    code
    课程计划表

    code

    10.2接口定义

  • 接口分析
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ### 新增课程计划--章,当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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.xuecheng.content.model.dto;  
    import lombok.Data;
    //新增大章节,小章节,修改章节信息
    @Data
    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;
    }
  • 创建TeachplanController
    1
    2
    3
    4
    5
    @ApiOperation("课程计划创建或修改")  
    @PostMapping("/teachplan")
    public void saveTeachplan( @RequestBody SaveTeachplanDto teachplan){
    teachplanService.saveTeachplan(teachplan);
    }

    10.3接口实现

  • TeachplanService接口
    1
    2
    //新增,修改,保存课程计划  
    public void saveTeachplan(SaveTeachplanDto saveTeachplanDto);
  • findTeachplanTreeImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //新增,修改,保存课程计划  
    @Override
    public void saveTeachplan(SaveTeachplanDto saveTeachplanDto) {
    //通过课程计划id判断是新增还是修改
    Long teachplanId = saveTeachplanDto.getId();
    if (teachplanId == null){
    //新增
    Teachplan teachplan = new Teachplan();
    BeanUtils.copyProperties(saveTeachplanDto,teachplan);
    //确定排序字段,找到同级节点个数,n+1 (select count(1) from teachplan where course-id=117 and parentid=268)
    Long parentid = saveTeachplanDto.getParentid();
    Long courseId = saveTeachplanDto.getCourseId();
    LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper = queryWrapper.eq(Teachplan::getCourseId, courseId).eq(Teachplan::getParentid, parentid);
    Integer count = teachplanMapper.selectCount(queryWrapper);
    teachplan.setOrderby(count+1);

    teachplanMapper.insert(teachplan);
    }else {
    //修改
    Teachplan teachplan = teachplanMapper.selectById(teachplanId);
    //将参数复制到teachplan
    BeanUtils.copyProperties(saveTeachplanDto,teachplan);
    teachplanMapper.updateById(teachplan);
    }
    }
  • 出现问题
  • 出现点击添加大章节时,页面没有显示
  • 原因:新增大章节时,里面没有小章节,mapper查询不出来
  • 解决:把查询sql语句的one.id=two.parentid改为左连接

    11.删除课程计划

    11.1需求分析

  • 删除第一级别的大章节时要求大章节下边没有小章节时方可删除
  • 删除第二级别的小章节的同时需要将teachplan_media表关联的信息也删除
    code

    11.2接口定义

    1
    2
    3
    4
    5
    6
    7
    Request URL: /content/teachplan/246  
    Request Method: DELETE

    如果失败返回:
    {"errCode":"120409","errMessage":"课程计划信息还有子级信息,无法操作"}

    如果成功:状态码200,不返回信息
  • TeachplanController
    1
    2
    3
    4
    5
    @ApiOperation("课程计划删除")  
    @DeleteMapping("/teachplan/{teachplanId}")
    public void deleteTeachplan(@PathVariable Long teachplanId){
    teachplanService.deleteTeachplan(teachplanId);
    }

    11.3接口实现

  • TeachplanService接口
    1
    2
    //删除课程计划  
    public void deleteTeachplan(Long teachplanId);
  • TeachplanServiceImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //删除课程计划  
    @Override
    public void deleteTeachplan(Long teachplanId) {
    //判断课程id是否为空
    if (teachplanId == null){
    XueChengPlusException.cast("课程id为空");
    }
    //获取章节的子级信息
    LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Teachplan::getParentid, teachplanId);
    Integer count = teachplanMapper.selectCount(queryWrapper);

    if (count > 0){
    XueChengPlusException.cast("课程计划信息还有子级信息,无法操作");
    }else {//无小节,直接删除
    //删除课程计划信息
    teachplanMapper.deleteById(teachplanId);
    //删除课程的媒资信息
    LambdaQueryWrapper<TeachplanMedia> mediaQueryWrapper = new LambdaQueryWrapper<>();
    mediaQueryWrapper.eq(TeachplanMedia::getTeachplanId,teachplanId);
    teachplanMediaMapper.delete(mediaQueryWrapper);
    }
    }

    12.课程计划排序

    12.1需求分析

    code

    12.2接口定义

  • 每次移动传递两个参数:
    1. 移动类型: movedown和moveup
    2. 课程计划id
  • 向下移动
    1
    2
    Request URL: http://localhost:8601/api/content/teachplan/movedown/43  
    Request Method: POST
  • 向上移动
    1
    2
    Request URL: http://localhost:8601/api/content/teachplan/moveup/43  
    Request Method: POST
  • TeachplanController
    1
    2
    3
    4
    5
    @ApiOperation("课程计划移动")  
    @PostMapping("/teachplan/{moveType}/{teachplanId}")
    public void moveupTeachplan(@PathVariable String moveType, @PathVariable Long teachplanId){
    teachplanService.moveTeachplan(moveType,teachplanId);
    }

    12.3接口实现

  • TeachplanService接口
    1
    2
    //课程计划移动  
    public void moveTeachplan(String moveType,Long teachplanId);
  • TeachplanServiceImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    	@Override  
    public void moveTeachplan(String moveType, Long teachplanId) {
    Teachplan teachplan = teachplanMapper.selectById(teachplanId);
    //获取层级和orderby
    Integer grade = teachplan.getGrade();
    Integer orderby = teachplan.getOrderby();
    //章节移动是比较同一课程id下的orderby
    Long courseId = teachplan.getCourseId();
    //小节移动是比较同一章节下的orderby
    Long parentid = teachplan.getParentid();
    //判断移动方向
    if ("moveup".equals(moveType)){
    //章节上移,找到上一章节的orderby并交换
    if (grade == 1){
    LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Teachplan::getGrade,1)
    .eq(Teachplan::getCourseId,courseId)
    .lt(Teachplan::getOrderby,orderby)
    .orderByDesc(Teachplan::getOrderby)
    .last("limit 1");
    Teachplan tmp = teachplanMapper.selectOne(queryWrapper);
    exchangeOrderby(teachplan,tmp);
    }
    //小节上移
    if (grade == 2){
    LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Teachplan::getParentid,parentid)
    .lt(Teachplan::getOrderby,orderby)
    .orderByDesc(Teachplan::getOrderby)
    .last("limit 1");
    Teachplan tmp = teachplanMapper.selectOne(queryWrapper);
    exchangeOrderby(teachplan,tmp);
    }
    }else if("movedown".equals(moveType)){
    //章节下移
    if (grade == 1){
    LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Teachplan::getGrade,1)
    .eq(Teachplan::getCourseId,courseId)
    .gt(Teachplan::getOrderby,orderby)
    .orderByAsc(Teachplan::getOrderby)
    .last("limit 1");
    Teachplan tmp = teachplanMapper.selectOne(queryWrapper);
    exchangeOrderby(teachplan,tmp);
    }
    //小节下移
    if (grade == 2){
    LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(Teachplan::getParentid,parentid)
    .gt(Teachplan::getOrderby,orderby)
    .orderByAsc(Teachplan::getOrderby)
    .last("limit 1");
    Teachplan tmp = teachplanMapper.selectOne(queryWrapper);
    exchangeOrderby(teachplan,tmp);
    }
    }
    }

    private void exchangeOrderby(Teachplan teachplan,Teachplan tmp){
    if (tmp == null){
    XueChengPlusException.cast("已经到头了,不能在移动");
    }
    //交换orderby并刷新
    Integer orderby = teachplan.getOrderby();
    Integer tmpOrderby = tmp.getOrderby();
    teachplan.setOrderby(tmpOrderby);
    tmp.setOrderby(orderby);
    teachplanMapper.updateById(tmp);
    teachplanMapper.updateById(teachplan);
    }

    13.师资管理

    13.1需求分析

    只允许向机构自己的课程中添加老师、删除老师。(前面已经完成—不允许修改机构外的课程)
    机构id统一使用:1232141425L
    code

    13.2接口定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    get /courseTeacher/list/75  
    75为课程id,请求参数为课程id

    响应结果
    [
    {
    "id":23,
    "courseId":75,
    "teacherName":"张老师",
    "position":"讲师",
    "introduction":"张老师教师简介张老师教师简介张老师教师简介张老师教师简介",
    "photograph":null,"createDate":null
    }
    ]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    post  /courseTeacher  

    请求参数:
    {
      "courseId": 75,
      "teacherName": "王老师",
      "position": "教师职位",
      "introduction": "教师简介"
    }
    响应结果:
    {
    "id":24,
    "courseId":75,
    "teacherName":"王老师",
    "position":"教师职位",
    "introduction":"教师简介",
    "photograph":null,"createDate":null
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    put /courseTeacher  
    请求参数:
    {
      "id": 24,
      "courseId": 75,
      "teacherName": "王老师",
      "position": "教师职位",
      "introduction": "教师简介",
      "photograph": null,
      "createDate": null
    }
    响应:
    {
    "id":24,
    "courseId":75,
    "teacherName":"王老师",
    "position":"教师职位",
    "introduction":"教师简介",
    "photograph":null,"createDate":null
    }
    1
    2
    3
    4
    5
    6
    7
    delete /courseTeacher/course/75/26  

    75:课程id
    26:教师id,即course_teacher表的主键
    请求参数:课程id、教师id

    响应:状态码200,不返回信息
  • CourseTeacherController
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package com.xuecheng.content.api;  
    import com.xuecheng.content.model.po.CourseTeacher;
    import com.xuecheng.content.service.CourseTeacherService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    import java.util.List;

    @Api(value = "师资信息管理接口",tags = "师资信息管理接口")
    @RestController
    public class CourseTeacherController {
    @Autowired
    CourseTeacherService courseTeacherService;

    @ApiOperation("查询教师接口")
    @GetMapping("/courseTeacher/list/{courseId}")
    public List<CourseTeacher> selectcourseTeacher(@PathVariable Long courseId){
    return courseTeacherService.selectCourseTeacher(courseId);
    }

    @ApiOperation("添加/修改教师接口")
    @PostMapping("/courseTeacher")
    public CourseTeacher savacourseTeacher(@RequestBody CourseTeacher courseTeacher){
    return courseTeacherService.savacourseTeacher(courseTeacher);
    }

    @ApiOperation("删除课程接口")
    @DeleteMapping("/courseTeacher/course/{courseId}/{teacherId}")
    public void deleteCourseTeacher(@PathVariable Long courseId,@PathVariable Long teacherId){
    courseTeacherService.deleteCourseTeacher(courseId,teacherId);
    }

    }

    13.3接口实现

  • CourseTeacherService
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package com.xuecheng.content.service;  
    import com.xuecheng.content.model.po.CourseTeacher;
    import java.util.List;

    public interface CourseTeacherService {
    //查询教师接口
    public List<CourseTeacher> selectCourseTeacher(Long courseId);

    //添加/修改教师接口
    public CourseTeacher savacourseTeacher(CourseTeacher courseTeacher);

    //删除教师接口
    public void deleteCourseTeacher(Long courseId,Long teacherId);
    }
  • CourseTeacherServiceImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    package com.xuecheng.content.service.impl;  
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.xuecheng.base.exception.XueChengPlusException;
    import com.xuecheng.content.mapper.CourseTeacherMapper;
    import com.xuecheng.content.model.po.CourseTeacher;
    import com.xuecheng.content.service.CourseTeacherService;
    import org.springframework.beans.BeanUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import java.time.LocalDateTime;
    import java.util.List;

    @Service
    public class CourseTeacherServiceImpl implements CourseTeacherService {

    @Autowired
    CourseTeacherMapper courseTeacherMapper;
    //查询教师
    @Override
    public List<CourseTeacher> selectCourseTeacher(Long courseId) {
    LambdaQueryWrapper<CourseTeacher> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(CourseTeacher::getCourseId,courseId);
    List<CourseTeacher> CourseTeacher = courseTeacherMapper.selectList(queryWrapper);
    return CourseTeacher;
    }
    //添加/修改教师
    @Override
    public CourseTeacher savacourseTeacher(CourseTeacher courseTeacher) {
    //获取id
    Long id = courseTeacher.getId();
    //判断是否有id
    if (id == null){
    //添加
    CourseTeacher teacher = new CourseTeacher();
    BeanUtils.copyProperties(courseTeacher,teacher);
    teacher.setCreateDate(LocalDateTime.now());
    int insert = courseTeacherMapper.insert(teacher);
    if (insert <= 0){
    XueChengPlusException.cast("添加失败");
    }
    return courseTeacherMapper.selectById(teacher.getId());
    }else {
    //修改
    CourseTeacher teacher = courseTeacherMapper.selectById(id);
    BeanUtils.copyProperties(courseTeacher,teacher);
    int update = courseTeacherMapper.updateById(teacher);
    if (update <= 0){
    XueChengPlusException.cast("修改失败");
    }
    return courseTeacherMapper.selectById(teacher.getId());
    }
    }
    //删除教师
    @Override
    public void deleteCourseTeacher(Long courseId, Long teacherId) {
    LambdaQueryWrapper<CourseTeacher> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(CourseTeacher::getCourseId,courseId)
    .eq(CourseTeacher::getId,teacherId);
    int delete = courseTeacherMapper.delete(queryWrapper);
    if (delete <= 0){
    XueChengPlusException.cast("删除失败");
    }
    }
    }

    14.删除课程

    14.1需求分析

  • 课程的审核状态为未提交时方可删除。只能删除本机构的
  • 删除课程需要删除课程相关的基本信息、营销信息、课程计划、课程教师信息
    code

    14.2接口定义

    1
    2
    3
    4
    delete  /course/87  
    87为课程id
    请求参数:课程id
    响应:状态码200,不返回信息
  • CourseBaseInfoController
    1
    2
    3
    4
    5
    6
    @ApiOperation("删除课程")  
    @DeleteMapping("/course/{courseId}")
    public void deleteCourse(@PathVariable Long courseId){
    Long companyId = 1232141425L;
    courseBaseInfoService.deleteCourse(companyId,courseId);
    }

    14.3接口实现

  • CourseBaseInfoService
    1
    2
    //删除课程  
    public void deleteCourse(Long companyId,Long courseId);
  • CourseBaseInfoServiceImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //课程删除  
    @Override
    public void deleteCourse(Long companyId,Long courseId) {
    CourseBase courseBase = courseBaseMapper.selectById(courseId);
    if (!companyId.equals(courseBase.getCompanyId())){
    XueChengPlusException.cast("只能删除本机构的课程");
    }
    //删除课程基本信息
    courseBaseMapper.deleteById(courseId);
    //删除课程营销信息
    courseMarketMapper.deleteById(courseId);
    //删除课程计划
    LambdaQueryWrapper<Teachplan> teachplanLambdaQueryWrapper = new LambdaQueryWrapper<>();
    teachplanLambdaQueryWrapper.eq(Teachplan::getCourseId, courseId);
    teachplanMapper.delete(teachplanLambdaQueryWrapper);
    //删除课程教师信息
    LambdaQueryWrapper<CourseTeacher> teacherLambdaQueryWrapper = new LambdaQueryWrapper<>();
    teacherLambdaQueryWrapper.eq(CourseTeacher::getCourseId, courseId);
    courseTeacherMapper.delete(teacherLambdaQueryWrapper);
    }