1、前端请求后端服务提供的接口。
2、后端服务的控制层Controller接收前端的请求。
3、Contorller层调用Service层进行业务处理。
4、Service层调用Dao持久层对数据持久化。
XXX-api:接口工程,为前端提供接口。
XXX-service: 业务工程,为接口工程提供业务支撑。
XXX-model: 数据模型工程,存储数据模型类、数据传输类型等。
XXX-content:内容管理模块工程,负责聚合XXX-api、XXX-service、XXX-model
1、分析数据模型
下边从查询条件、查询列表两个方面分析数据模型
①查询条件:
包括:课程名称、课程审核状态、课程发布状态
课程名称:可以模糊搜索
课程审核状态:未提交、已提交、审核通过、审核未通过
课程发布状态:未发布、已发布、已下线
因为是分页查询所以查询条件中还要包括当前页码、每页显示记录数。
②查询结果:
查询结果中包括:课程id、课程名称、任务数、创建时间、是否付费、审核状态、类型,操作
任务数:该课程所包含的课程计划数,即课程章节数。
是否付费:课程包括免费、收费两种。
类型:录播、直播。
因为是分页查询所以查询结果中还要包括总记录数、当前页、每页显示记录数。
2、生成PO类
PO即持久对象(Persistent Object),它们是由一组属性和属性的get和set方法组成,PO对应于数据库的表。使用mybatis-plus的generator工程生成PO类、Mapper接口、Mapper的xml文件。
①Dto(Data Access Object):配合PO进行增删改查。
②VO( View Object) :与前端进行交互的Java对象。
③Bo( Business Object)表示一个业务对象。
④PO(Persistant Object):数据库中的一条记录映射成的Java对象。
3、设计查询课程接口
①协议
采用HTTP,查询类接口通常为get或post,查询条件较少的使用get,较多的使用post。确定content-type,参数以什么数据格式提交,结果以json格式响应。
②分析请求参数
根据前边对数据模型的分析,请求参数为:课程名称、课程审核状态、当前页码、每页显示记录数。
根据分析的请求参数定义模型类。
③分析响应结果
根据前边对数据模型的分析,响应结果为数据列表加一些分页信息(总记录数、当前页、每页显示记录数)。
数据列表中数据的属性包括:课程id、课程名称、任务数、创建时间、审核状态、类型。
注意:查询结果中的审核状态为数据字典中的代码字段,前端会根据审核状态代码找到对应的名称显示。
根据分析的响应结果定义模型类。
④分析完成,使用SpringBoot注解开发一个Http接口。
⑤使用接口文档工具查看接口的内容。
⑥ 接口中调用Service方法完成业务处理。
4、生成接口文档
来利用Swagger开源项目,Swagger是一个在线接口文档的生成工具。
写完接口以后,写一个controller类,用Swagger生成接口文档,把接口文档发给前端工程师。
5、实现查询课程接口
先写mapper层,再写service接口。创建PO类、Mapper接口、Mapper的xml文件,每个PO类对应数据库的每张表,每张表需要创建一个Mapper接口和Mapper的xml映射文件 。
持久层使用MyBatis-Plus,使用generator工程生成的mapper接口和mapper映射文件 拷贝到service工程。(自动生成mapper接口与mapper映射文件)
测试mapper
①在service工程的pom.xml中添加依赖
②在config包下配置Mybatis-Plus
/**
* <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每页大小和当前页的数据,是否为首页,是否为尾页,总页数等。
③单元测试需要的配置文件
在test/resources下创建 log4j2-dev.xml、bootstrap.yml:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/xcplus_content?serverTimezone=UTC&userUnicode=true&useSSL=false&
username: root
password: mysql
# 日志文件配置路径
logging:
config: classpath:log4j2-dev.xml
④编写启动类
单元测试工作在test目录,在test下添加启动类
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);
}
}
⑤编写测试类
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.Course.base.model.PageParams;
import com.Course.base.model.PageResult;
import com.Course.content.mapper.CourseBaseMapper;
import com.Course.content.model.dto.QueryCourseParamsDto;
import com.Course.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
class CourseBaseMapperTests {
@Autowired
CourseBaseMapper courseBaseMapper;
@Test
void testCourseBaseMapper() {
CourseBase courseBase = courseBaseMapper.selectById(74L);
Assertions.assertNotNull(courseBase);
//测试查询接口
LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
//查询条件
QueryCourseParamsDto queryCourseParamsDto = new QueryCourseParamsDto();
queryCourseParamsDto.setCourseName("java");
queryCourseParamsDto.setAuditStatus("202004");
queryCourseParamsDto.setPublishStatus("203001");
//拼接查询条件(重点)
//根据课程名称模糊查询 name like '%名称%'
queryWrapper.like(StringUtils.isNotEmpty(queryCourseParamsDto.getCourseName()),CourseBase::getName,queryCourseParamsDto.getCourseName());
//根据课程审核状态
queryWrapper.eq(StringUtils.isNotEmpty(queryCourseParamsDto.getAuditStatus()),CourseBase::getAuditStatus,queryCourseParamsDto.getAuditStatus());
//todo:按课程发布状态查询
//分页参数
PageParams pageParams = new PageParams();
pageParams.setPageNo(1L);//页码
pageParams.setPageSize(3L);//每页记录数
Page<CourseBase> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize());
//分页查询E page 分页参数, @Param("ew") Wrapper<T> queryWrapper 查询条件
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<>(items, total, pageParams.getPageNo(), pageParams.getPageSize());
System.out.println(courseBasePageResult);
}
}
难点
写查询接口发现数据不对。
把SQL语句格式化,找出参数(把SQL补齐),贴到MySQL客户端看看能不能拿到数据,看SQL哪里写错了。
6、开发业务层
①数据字典表
如果客户需要频繁修改数据库里的某一个字段的内容,比如把所有的“审核未通过”改成“未通过”,过一段时间又要改成“XXX”,在原表上改太麻烦,建立一个数据字典表,把数据和字段连接起来。需要修改的时候,修改字段对应的那个数据即可。
[
{"code":"202001","desc":"审核未通过"},
{"code":"202002","desc":"未审核"},
{"code":"202003","desc":"审核通过"}
]
②编写service接口
package com.course.content.service;
import com.course.base.model.PageParams;
import com.course.base.model.PageResult;
import com.course.content.model.dto.QueryCourseParamsDto;
import com.course.content.model.po.CourseBase;
/**
* @description 课程基本信息管理业务接口
* @author Mr.M
* @date 2022/9/6 21:42
* @version 1.0
/
public interface CourseBaseInfoService {
/*
* @description 课程查询接口
* @param pageParams 分页参数
* @param queryCourseParamsDto 条件条件
* @return com.course.base.model.PageResult<com.xuecheng.content.model.po.CourseBase>
*/
PageResult<CourseBase> queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto queryCourseParamsDto);
}
创建接口实现类
package com.course.content.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.course.base.model.PageParams;
import com.course.base.model.PageResult;
import com.course.content.mapper.CourseBaseMapper;
import com.course.content.model.dto.QueryCourseParamsDto;
import com.course.content.model.po.CourseBase;
import com.course.content.service.CourseBaseInfoService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @description 课程信息管理业务接口实现类
* @version 1.0
*/
@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());
//构建查询条件,根据课程发布状态查询
//todo:根据课程发布状态查询
//分页对象
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;
}
}
前后端联调,nginx解决跨域访问
7、课程分类查询
查询树形结构的表(多级),使用递归实现课程分类的查询。
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