3.1 内容管理模块 - 工程搭建、课程查询、配置Swagger、数据字典

内容管理模块

在此模块完成课程及其相关内容管理

一、基础工程搭建

1.1 需求分析

内容管理系统(content management system,CMS),是一种位于WEB前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。

本项目作为一个大型的在线教育平台,其内容管理模块主要对课程及相关内容管理,包括:课程的基本信息、课程图片、课程师资信息、课程的授课计划、课程视频、课程文档等内容的管理。

1.2 业务流程

内容管理的业务由教学机构人员和平台的运营人员共同完成。

教学机构人员的业务流程如下

1、登录教学机构。

2、维护课程信息,添加一门课程需要编辑课程的基本信息、上传课程图片、课程营销信息、课程计划、上传课程视频、课程师资信息等内容。

3、课程信息编辑完成,通过课程预览确认无误后提交课程审核。

4、待运营人员对课程审核通过后方可进行课程发布。

运营人员的业务流程如下

1、查询待审核的课程信息。

2、审核课程信息。

3、提交审核结果。

下图是课程编辑与发布的整体流程。

img

1.3 数据模型

共设计到9张表

image-20231029210826042

1.4 创建模块工程

1.4.1 介绍

我们要创建的就是下面标红的内容管理模块

image-20231029211540123

本项目是一个前后端分离项目,前端与后端开发人员之间主要依据接口进行开发。

下图是前后端交互的流程图

1、前端请求后端服务提供的接口。(通常为http协议 )

2、后端服务的控制层Controller接收前端的请求。

3、Contorller层调用Service层进行业务处理。

4、Service层调用Dao持久层对数据持久化。

image-20231029211908225

对于一个Springcloud工程,对于此模块我们会单独创建一个Controller接口层工程xuecheng-plus-content-api,这个模块里面只是接口,这个工程会调用Service

xuecheng-plus-content-service模块式Service模块,处理业务逻辑

Controller工程调用Service工程时需要传输一些数据,而完成此传输功能的模块式xuecheng-plus-content-mode模块工程,里面都是一些传输对象

总结

xuecheng-plus-content-api:接口工程,为前端提供接口。

xuecheng-plus-content-service: 业务工程,为接口工程提供业务支撑。

xuecheng-plus-content-model: 数据模型工程,存储数据模型类、数据传输类型等。

image-20231029212238183

结合项目父工程、项目基础工程后

假如说我们以后部署jar包时,部署xuecheng-plus-content的jar包即可

xuecheng-plus-parent父工程会管理整个大项目的依赖

xuecheng-plus-content负责聚合xuecheng-plus-content-api、xuecheng-plus-content-service、xuecheng-plus-content-model,而且只负责模块的聚合

image-20231029212840781

1.4.2 xuecheng-plus-content 聚合工程

  • 首先在项目根目录创建内容管理模块的父工程xuecheng-plus-content

image-20231029214827080

image-20231029215219848

  1. 创建完成,只保留pom.xml文件,删除多余的文件

    image-20231029215423159

image-20231029214903484

  1. Maven坐标。内容管理父工程的主要职责是聚合内容管理接口和内容管理接口实现两个工程,它的父工程是xuecheng-plus-parent
<?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下创建xuecheng-plus-content-model数据模型工程

image-20231029215647710

  1. 创建完成,只保留包和pom.xml文件 ,删除多余的文件。

    比如删除启动类和配置文件

  2. 修改pom.xml文件

    我们在配置文件中并没有配置relativePath,因为此项目和父工程的pom.xml文件在同级目录之中

<?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下创建xuecheng-plus-content-service接口实现工程

    这个工程主要是写Service层和Mapper与数据库交互层

image-20231029220130200

  1. 创建完成,只保留包和pom.xml文件 ,删除多余的文件

    比如删除启动类和配置文件

  2. pom.xml如下:

    我们在配置文件中并没有配置relativePath,因为此项目和父工程的pom.xml文件在同级目录之中

<?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下创建xuecheng-plus-content-api接口工程

xuecheng-plus-content-api接口工程的父工程是xuecheng-plus-content,它依赖了xuecheng-plus-base基础工程

image-20231029220529121

  1. 编辑pom.xml

    我们在配置文件中并没有配置relativePath,因为此项目和父工程的pom.xml文件在同级目录之中

<?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>

1.4.3 模块演示

四个功能模块如下所示

image-20231029221637895

二、课程查询准备

2.1 需求分析

2.1.1 业务流程

课程查询的业务流程如下:

1、教学机构人员点击课程管理首先进入课程查询界面,如下:

image-20231029224218893

2.在课程进行列表查询页面输入查询条件查询课程信息

当不输入查询条件时输入全部课程信息。

输入查询条件查询符合条件的课程信息。

约束:本教学机构查询本机构的课程信息。

img

2.1.2 数据模型

下边从查询条件、查询列表两个方面分析数据模型

  • 查询条件

包括:课程名称、课程审核状态、课程发布状态

课程名称:可以模糊搜索

课程审核状态:未提交、已提交、审核通过、审核未通过

课程发布状态:未发布、已发布、已下线

因为是分页查询所以查询条件中还要包括当前页码、每页显示记录数。

  • 查询结果

查询结果中包括:课程id、课程名称、任务数、创建时间、是否付费、审核状态、类型,操作

任务数:该课程所包含的课程计划数,即课程章节数。

是否付费:课程包括免费、收费两种。

类型:录播、直播。

因为是分页查询所以查询结果中还要包括总记录数、当前页、每页显示记录数。

2.2 生成PO类

PO即持久对象(Persistent Object),它们是由一组属性和属性的get和set方法组成,PO对应于数据库的表

我们对应的这些类应该添加在xuecheng-plus-content-model工程中

2.2.1 新增Maven配置

xuecheng-plus-content-model模块引入mybatis的核心包和注解包,保证代码不出错即可,不用将Mybatis的包全部引进来

<!--存在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>

2.2.2 课程基本信息表

image-20231029231721091

/**
 * <p>
 * 课程基本信息
 * </p>
 */
@Data
@TableName("course_base")
public class CourseBase implements Serializable {
    
    

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 机构ID
     */
    private Long companyId;

    /**
     * 机构名称
     */
    private String companyName;

    /**
     * 课程名称
     */
    private String name;

    /**
     * 适用人群
     */
    private String users;

    /**
     * 课程标签
     */
    private String tags;

    /**
     * 大分类
     */
    private String mt;

    /**
     * 小分类
     */
    private String st;

    /**
     * 课程等级
     */
    private String grade;

    /**
     * 教育模式(common普通,record 录播,live直播等)
     */
    private String teachmode;

    /**
     * 课程介绍
     */
    private String description;

    /**
     * 课程图片
     */
    private String pic;

    /**
     * 创建时间
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate;

    /**
     * 修改时间
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime changeDate;

    /**
     * 创建人
     */
    private String createPeople;

    /**
     * 更新人
     */
    private String changePeople;

    /**
     * 审核状态
     */
    private String auditStatus;

    /**
     * 课程发布状态 未发布  已发布 下线
     */
    private String status;


}

2.3 接口设计分析

设计一个接口需要包括以下几个方面

  • 协议

通常协议采用HTTP,查询类接口通常为get或post,查询条件较少的使用get,较多的使用post。

本接口使用 http post。

还要确定content-type,参数以什么数据格式提交,结果以什么数据格式响应。

一般情况没有特殊情况结果以json 格式响应。

  • 分析请求参数

根据前边对数据模型的分析,请求参数为:课程名称、课程审核状态、当前页码、每页显示记录数。

根据分析的请求参数定义模型类。

  • 分析响应结果

根据前边对数据模型的分析,响应结果为数据列表加一些分页信息(总记录数、当前页、每页显示记录数)。

数据列表中数据的属性包括:课程id、课程名称、任务数、创建时间、审核状态、类型。

注意查询结果中的审核状态为数据字典中的代码字段,前端会根据审核状态代码 找到对应的名称显示

根据分析的响应结果定义模型类。

  • 分析完成,使用SpringBoot注解开发一个Http接口

  • 使用接口文档工具查看接口的内容

  • 接口中调用Service方法完成业务处理

2.4 创建模型类

2.4.1 分页查询公用参数

据接口分析需要定义模型类接收请求的参数,并定义模型类用于响应结果

这些一般是公共使用的,所以放在xuecheng-plus-base工程中

/**
 * @description 分页查询通用参数
 */
@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;
  }
  
}

2.4.2 分页查询结果模型类

针对分页查询结果经过分析也存在固定的数据和格式,所以在base工程定义一个基础的模型类

据接口分析需要定义模型类接收请求的参数,并定义模型类用于响应结果

这些一般是公共使用的,所以放在xuecheng-plus-base工程中

/**
 * @description 分页查询结果模型类
 */
@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;
    }

}

2.4.2 查询条件模型类

这个类只有在是在xuecheng-plus-content工程模块中使用,所以我们把下面这个类添加在xuecheng-plus-content-model模块即可

/**
 * @description 课程查询参数Dto
 */
 @Data
 @ToString
public class QueryCourseParamsDto {
    
    

  //审核状态
 private String auditStatus;
 //课程名称
 private String courseName;
  //发布状态
 private String publishStatus;

}

2.5 LocalDataTime工具类

不管在前端向后端传输时间相关参数,还是后端向前端响应参数,时间格式都是如下所示:

image-20231030232503171

为了解决上面默认的时间格式,我们在xuecheng-plus-base工程中添加如下所示配置类

@Configuration
public class LocalDateTimeConfig {
    
    

    /*
     * 序列化内容
     *   LocalDateTime -> String
     * 服务端返回给客户端内容
     * */
    @Bean
    public LocalDateTimeSerializer localDateTimeSerializer() {
    
    
        return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    /*
     * 反序列化内容
     *   String -> LocalDateTime
     * 客户端传入服务端数据
     * */
    @Bean
    public LocalDateTimeDeserializer localDateTimeDeserializer() {
    
    
        return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }


    // 配置
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    
    
        return builder -> {
    
    
            builder.serializerByType(LocalDateTime.class, localDateTimeSerializer());
            builder.deserializerByType(LocalDateTime.class, localDateTimeDeserializer());
        };
    }

}

三、课程查询

3.1Maven依赖

3.1.1 api工程

<dependencies>
    <dependency>
        <groupId>com.xuecheng</groupId>
        <artifactId>xuecheng-plus-content-service</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <!--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>


</dependencies>

3.1.2 service工程

<dependencies>
    <dependency>
        <groupId>com.xuecheng</groupId>
        <artifactId>xuecheng-plus-content-model</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <!-- 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>

</dependencies>

3.2 课程查询Controller

我们定义的接口要写在xuecheng-plus-content-api工程中

接口描述图

PageParams对象接收pageNo字段和pageSize字段

QueryCourseParamsDto对象接收请求体中的auditStatus审核状态字段,courseName课程名称字段,publishStatus发布状态字段

image-20231030213209269

/**
 * 课程内容管理
 */
@Api(value = "课程信息编辑接口",tags = "课程信息编辑接口")
@Slf4j
@RequestMapping
@RestController //@Controller+@Response
public class CourseBaseInfoController {
    
    

    @Autowired
    private CourseBaseInfoService courseBaseInfoService;
    
    /**
     * 课程分页查询接口
     *
     * @RequestBody(required = false) 含义:不传QueryCourseParamsDto请求体也行
     *
     * @param pageParams           pageNo字段和pageSize字段
     * @param queryCourseParamsDto auditStatus审核状态字段,courseName课程名称字段,publishStatus发布状态字段
     * @return 分页结果
     */
    @ApiOperation("课程查询接口")
    @PostMapping("/course/list")
    public PageResult<CourseBase> list(PageParams pageParams, @RequestBody(required = false) QueryCourseParamsDto queryCourseParamsDto) {
    
    
        PageResult<CourseBase> pageResult = courseBaseInfoService.queryCourseBaseList(pageParams, queryCourseParamsDto);
        return pageResult;

    }

}

3.3 Service

在xuecheng-plus-content-service层

/**
 * 课程信息管理业务接口实现类
 */
@Service
public class CourseBaseInfoServiceImpl implements CourseBaseInfoService {
    
    


    @Autowired
    CourseBaseMapper courseBaseMapper;

    @Override
    public PageResult<CourseBase> queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto queryCourseParamsDto) {
    
    


        //构建查询条件对象
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
        //构建查询条件,根据课程名称查询
        queryWrapper.like(StringUtils.isNotEmpty(queryCourseParamsDto.getCourseName()), CourseBase::getName, queryCourseParamsDto.getCourseName());
        //构建查询条件,根据课程审核状态查询
        queryWrapper.eq(StringUtils.isNotEmpty(queryCourseParamsDto.getAuditStatus()), CourseBase::getAuditStatus, queryCourseParamsDto.getAuditStatus());
        //构建查询条件,根据课程发布状态查询
        queryWrapper.eq(StringUtils.isNotEmpty(queryCourseParamsDto.getPublishStatus()), CourseBase::getStatus, queryCourseParamsDto.getPublishStatus());


        //分页对象
        Page<CourseBase> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize());
        // 查询数据内容获得结果
        Page<CourseBase> pageResult = courseBaseMapper.selectPage(page, queryWrapper);
        // 获取数据列表
        List<CourseBase> list = pageResult.getRecords();
        // 获取数据总数
        long total = pageResult.getTotal();
        // 构建结果集
        PageResult<CourseBase> courseBasePageResult = new PageResult<>(list, total, pageParams.getPageNo(), pageParams.getPageSize());
        
        return courseBasePageResult;
        
    }
}

3.4 Mapper接口

在xuecheng-plus-content-service层,不在model层

我们要实现课程查询的功能

3.4.1 分页插件

/**
 * <P>
 *        Mybatis-Plus 配置
 * </p>
 */
@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每页大小和当前页的数据,是否为首页,是否为尾页,总页数等。

3.4.2 Mapper接口

public interface CourseBaseMapper extends BaseMapper<CourseBase> {
    
    

}

3.5 效果图

image-20231101235720152

四、api工程配置文件

4.1 log4j2-dev.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="180" packages="">
    <properties>
        <property name="logdir">logs</property>
        <property name="PATTERN">%date{YYYY-MM-dd HH:mm:ss,SSS} %level [%thread][%file:%line] - %msg%n%throwable</property>
    </properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${PATTERN}"/>
        </Console>

        <RollingFile name="ErrorAppender" fileName="${logdir}/error.log"
            filePattern="${logdir}/$${date:yyyy-MM-dd}/error.%d{yyyy-MM-dd-HH}.log" append="true">
            <PatternLayout pattern="${PATTERN}"/>
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
            </Policies>
        </RollingFile>

        <RollingFile name="DebugAppender" fileName="${logdir}/info.log"
            filePattern="${logdir}/$${date:yyyy-MM-dd}/info.%d{yyyy-MM-dd-HH}.log" append="true">
            <PatternLayout pattern="${PATTERN}"/>
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
            </Policies>
        </RollingFile>
        
        <!--异步appender-->
         <Async name="AsyncAppender" includeLocation="true">
            <AppenderRef ref="ErrorAppender"/>
            <AppenderRef ref="DebugAppender"/>
        </Async>
    </Appenders>
    
    <Loggers>
         <!--过滤掉spring和mybatis的一些无用的debug信息
        <logger name="org.springframework" level="INFO">
        </logger>
        <logger name="org.mybatis" level="INFO">
        </logger>-->
        <logger name="cn.itcast.wanxinp2p.consumer.mapper" level="DEBUG">
        </logger>

        <logger name="springfox" level="INFO">
        </logger>
      <logger name="org.apache.http" level="INFO">
        </logger>
        <logger name="com.netflix.discovery" level="INFO">
        </logger>
        
        <logger name="RocketmqCommon"  level="INFO" >
      </logger>
      
      <logger name="RocketmqRemoting" level="INFO"  >
      </logger>
      
      <logger name="RocketmqClient" level="WARN">
      </logger>

        <logger name="org.dromara.hmily" level="WARN">
        </logger>

        <logger name="org.dromara.hmily.lottery" level="WARN">
        </logger>

        <logger name="org.dromara.hmily.bonuspoint" level="WARN">
        </logger>
      
        <!--OFF   0-->
        <!--FATAL   100-->
        <!--ERROR   200-->
        <!--WARN   300-->
        <!--INFO   400-->
        <!--DEBUG   500-->
        <!--TRACE   600-->
        <!--ALL   Integer.MAX_VALUE-->
        <Root level="DEBUG" includeLocation="true">
            <AppenderRef ref="AsyncAppender"/>
            <AppenderRef ref="Console"/>
            <AppenderRef ref="DebugAppender"/>
        </Root>
    </Loggers>
</Configuration>

4.2 bootstrap.yml

下面的文件是springcloud识别出来的文件

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

五、Swagger接口文档

这是之前在Springboot学习的swaggerSpringboot集成Swagger_springboot项目集成swagger

在前后端分离开发中通常由后端程序员设计接口,完成后需要编写接口文档,最后将文档交给前端工程师,前端工程师参考文档进行开发

可以通过Swagger工具快速生成接口文档

5.1 Maven坐标

添加到内容管理api工程

<!-- Spring Boot 集成 swagger -->
<dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>

5.2 配置swagger

在 bootstrap.yml中配置swagger的扫描包路径及其它信息,base-package为扫描的包路径,扫描Controller类

swagger:
  title: "学成在线内容管理系统"
  description: "内容系统管理系统对课程相关信息进行管理"
  base-package: com.xuecheng.content
  enabled: true
  version: 1.0.0

在启动类中添加@EnableSwagger2Doc注解

@EnableSwagger2Doc
@SpringBootApplication
public class ContentApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ContentApplication.class, args);
    }

}

工程启动起来,访问http://localhost:63040/content/swagger-ui.html

image-20231030225305069

这个文档存在两个问题:

1、接口名称显示course-base-info-controller名称不直观

2、课程查询是post方式只显示post /course/list即可。

5.3 添加接口说明

这是之前在Springboot学习的swaggerSpringboot集成Swagger_springboot项目集成swagger

5.3.1 @Api与@ApiOperation接口注释

/**
 * 课程内容管理
 */
@Api(value = "课程信息编辑接口",tags = "课程信息编辑接口")
@Slf4j
@RequestMapping
@RestController //@Controller+@Response
public class CourseBaseInfoController {
    
    

    /**
     * 课程分页查询接口
     *
     * @RequestBody(required = false) 含义:不传QueryCourseParamsDto请求体也行
     *
     * @param pageParams           pageNo字段和pageSize字段
     * @param queryCourseParamsDto auditStatus审核状态字段,courseName课程名称字段,publishStatus发布状态字段
     * @return 分页结果
     */
    @ApiOperation("课程查询接口")
    @PostMapping("/course/list")
    public PageResult<CourseBase> list(PageParams pageParams, @RequestBody(required = false) QueryCourseParamsDto queryCourseParamsDto) {
    
    
        return null;
    }

}

再次启动服务,工程启动起来,访问http://localhost:63040/content/swagger-ui.html查看接口信息

image-20231030225842129

5.3.2 @ApiModel与@ApiModelProperty字段注释

@ApiModel注解与@ApiModelProperty注解

@ApiModel("分页查询通用参数")
@Data
@ToString
public class PageParams {
    
    

  //当前页码
  @ApiModelProperty("当前页码")
  private Long pageNo = 1L;

  //每页记录数默认值
  @ApiModelProperty("每页记录数默认值")
  private Long pageSize =10L;

  public PageParams(){
    
    

  }

  public PageParams(long pageNo,long pageSize){
    
    
      this.pageNo = pageNo;
      this.pageSize = pageSize;
  }

}
/**
 * @description 课程查询参数Dto
 */
@Data
@ApiModel("课程查询参数Dto")
@ToString
public class QueryCourseParamsDto {
    
    

    //审核状态
    @ApiModelProperty("审核状态")
    private String auditStatus;
    //课程名称
    @ApiModelProperty("课程名称")
    private String courseName;
    //发布状态
    @ApiModelProperty("发布状态")
    private String publishStatus;

}

再次启动服务,工程启动起来,访问http://localhost:63040/content/swagger-ui.html查看接口信息

5.3.3 其他参数

@Api:修饰整个类,描述Controller的作用

@ApiOperation:描述一个类的一个方法,或者说一个接口

@ApiParam:单个参数描述

@ApiModel:用对象来接收参数

@ApiModelProperty:用对象接收参数时,描述对象的一个字段

@ApiResponse:HTTP响应其中1个描述

@ApiResponses:HTTP响应整体描述

@ApiIgnore:使用该注解忽略这个API

@ApiError :发生错误返回的信息

@ApiImplicitParam:一个请求参数

@ApiImplicitParams:多个请求参数


@ApiImplicitParam属性如下

属性 取值 作用
paramType 查询参数类型
path 以地址的形式提交数据
query 直接跟参数完成自动映射赋值
body 以流的形式提交 仅支持POST
header 参数在request headers 里边提交
form 以form表单的形式提交 仅支持POST
dataType 参数的数据类型 只作为标志说明,并没有实际验证
Long
String
name 接收参数名
value 接收参数的意义描述
required 参数是否必填
true 必填
false 非必填
defaultValue 默认值

六、数据字典

6.1 介绍

审核状态在查询条件和查询结果中都存在,审核状态包括:未审核、审核通过、审核未通过三种

思考一个问题:一个课程的审核状态如果是“审核未通过”那么在课程基本信息表记录“审核未通过”三个字合适吗

如下图所示,合适嘛?

显然是不合适的,万一客户要求把“审核未通过”改成“通过”呢?我们要通过update修改,但是入了库的数据再修改会存在一定的风险,就算成功修改了,那以后客户在要求修改呢?

image-20231031224641775

和审核状态同类的有好多这样的信息,比如:课程状态、课程类型、用户类型等等,这一类数据有一个共同点就是它有一些分类项,且这些分类项较为固定

针对这些数据,为了提高系统的可扩展性,专门定义数据字典表去维护

image-20231031225148581

比如课程审核是否通过

[{
    
    "code":"202001","desc":"审核未通过"},{
    
    "code":"202002","desc":"未提交"},{
    
    "code":"202003","desc":"已提交"},{
    
    "code":"202004","desc":"审核通过"}]

image-20231031225247347

比如课程发布情况

[{
    
    "code":"203001","desc":"未发布"},{
    
    "code":"203002","desc":"已发布"},{
    
    "code":"203003","desc":"下线"}]

image-20231031225530355

而在我们课程表中会有两个字段与之对应

image-20231031225705184

image-20231031225721109

6.2 创建工程

与之前一样,创建工程

image-20231101224223002

6.3 Controller

@Slf4j
@RestController
public class DictionaryController  {
    
    

    @Autowired
    private DictionaryService  dictionaryService;
    
    //查询数据字典的所有内容
    @GetMapping("/dictionary/all")
    public List<Dictionary> queryAll() {
    
    
        return dictionaryService.queryAll();
    }
    //查询数据字典代码查询数据字典
    @GetMapping("/dictionary/code/{code}")
    public Dictionary getByCode(@PathVariable String code) {
    
    
        return dictionaryService.getByCode(code);
    }
}

6.4 Service

@Slf4j
@Service
public class DictionaryServiceImpl extends ServiceImpl<DictionaryMapper, Dictionary> implements DictionaryService {
    
    

    @Override
    public List<Dictionary> queryAll() {
    
    

        List<Dictionary> list = this.list();


        return list;
    }

    @Override
    public Dictionary getByCode(String code) {
    
    


        LambdaQueryWrapper<Dictionary> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Dictionary::getCode, code);

        Dictionary dictionary = this.getOne(queryWrapper);


        return dictionary;
    }
}

6.5 Mapper

public interface DictionaryMapper extends BaseMapper<Dictionary> {
    
    

}

6.6 实体类

@Data
@TableName("dictionary")
public class Dictionary implements Serializable {
    
    

    private static final long serialVersionUID = 1L;

    /**
     * id标识
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 数据字典名称
     */
    private String name;

    /**
     * 数据字典代码
     */
    private String code;

    /**
     * 数据字典项--json格式
            [{
                  "sd_name": "低级",
                  "sd_id": "200001",
                  "sd_status": "1"
               }, {
                  "sd_name": "中级",
                  "sd_id": "200002",
                  "sd_status": "1"
               }, {
                  "sd_name": "高级",
                  "sd_id": "200003",
                  "sd_status": "1"
               }]
     */
    private String itemValues;


}

七、解决Swagger报错

swagger2报错Illegal DefaultValue null for parameter type integer…

虽然是报错了,但是并不影响swagger功能的使用,但是每次刷新swagger控制台就会报这个错误,看着控制台的异常比较难受,便搜索了一下

百度的原因是说swagger2官方有bug,下面是解决方法

用到swagger的地方可以酌情的复制下面的坐标或继承

<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>${swagger-spring-boot-starter.version}</version>
    <exclusions>
        <exclusion>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
        </exclusion>
        <exclusion>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-annotations</artifactId>
    <version>${swagger-annotations.version}</version>
</dependency>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-models</artifactId>
    <version>1.5.22</version>
</dependency>

猜你喜欢

转载自blog.csdn.net/weixin_51351637/article/details/135032194
3.1
今日推荐