1、mybatis-plus分页
pom:
<?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>
<!-- 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>
</project>
配置类:
package com.xuecheng.content.config;
import com.baomidou.mybatisplus.annotation.DbType;
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;
("com.xuecheng.content.mapper")
public class MybatisPlusConfig {
/**
* 定义分页拦截器
*/
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每页大小和当前页的数据,是否为首页,是否为尾页,总页数等。
2、课程查询DAO接口
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.lang3.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;
public class CourseBaseMapperTests {
CourseBaseMapper courseBaseMapper;
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());
//根据课程审核状态查询 course_base.audit_status = ?
queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus()), CourseBase::getAuditStatus,courseParamsDto.getAuditStatus());
//根据课程的发布状态查询
queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getPublishStatus()),CourseBase::getStatus,courseParamsDto.getPublishStatus());
//todo:按课程发布状态查询
//分页参数对象
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、数据字典
为什么要有数据字典呢,是因为课程的审核状态如果要改的话(比如审核为通过改成审核通过),那么将会改动整张数据库表,如果我们建立一个数据字典,比如0代表未提交,1代表已提交,那么后续想改动只需要改动数据字典表即可。由于数据字典属于系统模块,不属于内容管理模块,所以不能和内容管理数据库一起。
4、课程查询-service
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.exception.XueChengPlusException;
import com.xuecheng.base.model.PageParams;
import com.xuecheng.base.model.PageResult;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.mapper.CourseCategoryMapper;
import com.xuecheng.content.mapper.CourseMarketMapper;
import com.xuecheng.content.model.dto.AddCourseDto;
import com.xuecheng.content.model.dto.CourseBaseInfoDto;
import com.xuecheng.content.model.dto.QueryCourseParamsDto;
import com.xuecheng.content.model.po.CourseBase;
import com.xuecheng.content.model.po.CourseMarket;
import com.xuecheng.content.service.CourseBaseInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resources;
import java.time.LocalDateTime;
import java.util.List;
public class CourseBaseInfoServiceImpl implements CourseBaseInfoService {
CourseBaseMapper courseBaseMapper;
CourseMarketMapper courseMarketMapper;
CourseCategoryMapper courseCategoryMapper;
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());
//根据课程审核状态查询 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());
return courseBasePageResult;
}
public CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto dto){
//参数的合法性校验
if (StringUtils.isBlank(dto.getName())) {
// throw new RuntimeException("课程名称为空");
XueChengPlusException.cast("课程名称为空");
}
if (StringUtils.isBlank(dto.getMt())) {
throw new RuntimeException("课程分类为空");
}
if (StringUtils.isBlank(dto.getSt())) {
throw new RuntimeException("课程分类为空");
}
if (StringUtils.isBlank(dto.getGrade())) {
throw new RuntimeException("课程等级为空");
}
if (StringUtils.isBlank(dto.getTeachmode())) {
throw new RuntimeException("教育模式为空");
}
if (StringUtils.isBlank(dto.getUsers())) {
throw new RuntimeException("适应人群为空");
}
if (StringUtils.isBlank(dto.getCharge())) {
throw new RuntimeException("收费规则为空");
}
//向课程基本信息表course_base写入数据
CourseBase courseBaseNew = new CourseBase();
//将传入的页面的参数放到courseBaseNew对象
// courseBaseNew.setName(dto.getName());
// courseBaseNew.setDescription(dto.getDescription());
//上边的从原始对象中get拿数据向新对象set,比较复杂
BeanUtils.copyProperties(dto,courseBaseNew);//只要属性名称一致就可以拷贝
courseBaseNew.setCompanyId(companyId);
courseBaseNew.setCreateDate(LocalDateTime.now());
//审核状态默认为未提交
courseBaseNew.setAuditStatus("202002");
//发布状态为未发布
courseBaseNew.setStatus("203001");
//插入数据库
int insert = courseBaseMapper.insert(courseBaseNew);
if(insert<=0){
throw new RuntimeException("添加课程失败");
}
//向课程营销系courese_market写入数据
CourseMarket courseMarketNew = new CourseMarket();
//将页面输入的数据拷贝到courseMarketNew
BeanUtils.copyProperties(dto,courseMarketNew);
//课程的id
Long courseId = courseBaseNew.getId();
courseMarketNew.setId(courseId);
//保存营销信息
saveCourseMarket(courseMarketNew);
//从数据库查询课程的详细信息,包括两部分
CourseBaseInfoDto courseBaseInfo = getCourseBaseInfo(courseId);
return courseBaseInfo;
}
//查询课程信息
public CourseBaseInfoDto getCourseBaseInfo(long courseId){
//从课程基本信息表查询
CourseBase courseBase = courseBaseMapper.selectById(courseId);
if(courseBase==null){
return null;
}
//从课程营销表查询
CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
//组装在一起
CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto();
BeanUtils.copyProperties(courseBase,courseBaseInfoDto);
if(courseMarket!=null){
BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);
}
//通过courseCategoryMapper查询分类信息,将分类名称放在courseBaseInfoDto对象
//todo:课程分类的名称设置到courseBaseInfoDto
return courseBaseInfoDto;
}
//单独写一个方法保存营销信息,逻辑:存在则更新,不存在则添加
private int saveCourseMarket(CourseMarket courseMarketNew){
//参数的合法性校验
String charge = courseMarketNew.getCharge();
if(StringUtils.isEmpty(charge)){
throw new RuntimeException("收费规则为空");
}
//如果课程收费,价格没有填写也需要抛出异常
if(charge.equals("201001")){
if(courseMarketNew.getPrice() ==null || courseMarketNew.getPrice().floatValue()<=0){
// throw new RuntimeException("课程的价格不能为空并且必须大于0");
XueChengPlusException.cast("课程的价格不能为空并且必须大于0");
}
}
//从数据库查询营销信息,存在则更新,不存在则添加
Long id = courseMarketNew.getId();//主键
CourseMarket courseMarket = courseMarketMapper.selectById(id);
if(courseMarket == null){
//插入数据库
int insert = courseMarketMapper.insert(courseMarketNew);
return insert;
}else{
//将courseMarketNew拷贝到courseMarket
BeanUtils.copyProperties(courseMarketNew,courseMarket);
courseMarket.setId(courseMarketNew.getId());
//更新
int i = courseMarketMapper.updateById(courseMarket);
return i;
}
}
}
5、前后端联调-跨域的三种方案
1、JSONP
通过script标签的src属性进行跨域请求,如果服务端要响应内容则首先读取请求参数callback的值,callback是一个回调函数的名称,服务端读取callback的值后将响应内容通过调用callback函数的方式告诉请求方。如下图:
2、添加响应头
服务端在响应头添加 Access-Control-Allow-Origin:*
3、通过nginx代理跨域
由于服务端之间没有跨域,浏览器通过nginx去访问跨域地址。
6、cors过滤器
package com.xuecheng.system.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
public class GlobalCorsConfig {
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
//允许白名单域名进行跨域调用
config.addAllowedOrigin("*");
//允许跨越发送cookie
config.setAllowCredentials(true);
//放行全部原始头信息
config.addAllowedHeader("*");
//允许所有请求方法跨域调用
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
7、前后端联调
在前端的.env里面定义了一些环境配置
# 前台管理页面-端口
VUE_APP_CLIENT_MANAGE_PORT=8601
# 首页、列表、学习
VUE_APP_CLIENT_PORTAL_URL=http://www.51xuecheng.cn
# 后台服务网关
# VUE_APP_SERVER_API_URL=http://www.51xuecheng.cn/api
#VUE_APP_SERVER_API_URL=http://localhost:63010
#VUE_APP_SERVER_API_URL=http://172.16.63.20:63010
VUE_APP_SERVER_API_URL=http://localhost:63040
# 权限认证
#VUE_APP_SERVER_AUTHORIZATION=ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9
VUE_APP_SERVER_AUTHORIZATION=
#ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9
# Cookie域
VUE_APP_SERVER_DOMAIN=51xuecheng.cn
# 七牛云静态页
VUE_APP_SERVER_QINIU_URL=http://localhost11
# 图片服务器地址
VUE_APP_SERVER_PICSERVER_URL=http://192.168.101.65:9000
8、树形表查询方法
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
9、树形表查询业务实现(难)
代码:
package com.xuecheng.content.service.impl;
import com.xuecheng.content.mapper.CourseCategoryMapper;
import com.xuecheng.content.model.dto.CourseCategoryTreeDto;
import com.xuecheng.content.service.CourseCategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class CourseCategoryServiceImpl implements CourseCategoryService {
CourseCategoryMapper courseCategoryMapper;
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;
}
}
10、新增课程接口定义
需要定义两个数据DTO模型(请求和响应)
### 创建课程
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
}
11、接口开发
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.exception.XueChengPlusException;
import com.xuecheng.base.model.PageParams;
import com.xuecheng.base.model.PageResult;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.mapper.CourseCategoryMapper;
import com.xuecheng.content.mapper.CourseMarketMapper;
import com.xuecheng.content.model.dto.AddCourseDto;
import com.xuecheng.content.model.dto.CourseBaseInfoDto;
import com.xuecheng.content.model.dto.QueryCourseParamsDto;
import com.xuecheng.content.model.po.CourseBase;
import com.xuecheng.content.model.po.CourseCategory;
import com.xuecheng.content.model.po.CourseMarket;
import com.xuecheng.content.service.CourseBaseInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resources;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author Mr.M
* @version 1.0
* @description TODO
* @date 2023/2/12 10:16
*/
public class CourseBaseInfoServiceImpl implements CourseBaseInfoService {
CourseBaseMapper courseBaseMapper;
CourseMarketMapper courseMarketMapper;
CourseCategoryMapper courseCategoryMapper;
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());
//根据课程审核状态查询 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());
return courseBasePageResult;
}
public CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto dto){
//参数的合法性校验
if (StringUtils.isBlank(dto.getName())) {
// throw new RuntimeException("课程名称为空");
XueChengPlusException.cast("课程名称为空");
}
if (StringUtils.isBlank(dto.getMt())) {
throw new RuntimeException("课程分类为空");
}
if (StringUtils.isBlank(dto.getSt())) {
throw new RuntimeException("课程分类为空");
}
if (StringUtils.isBlank(dto.getGrade())) {
throw new RuntimeException("课程等级为空");
}
if (StringUtils.isBlank(dto.getTeachmode())) {
throw new RuntimeException("教育模式为空");
}
if (StringUtils.isBlank(dto.getUsers())) {
throw new RuntimeException("适应人群为空");
}
if (StringUtils.isBlank(dto.getCharge())) {
throw new RuntimeException("收费规则为空");
}
//向课程基本信息表course_base写入数据
CourseBase courseBaseNew = new CourseBase();
//将传入的页面的参数放到courseBaseNew对象
// courseBaseNew.setName(dto.getName());
// courseBaseNew.setDescription(dto.getDescription());
//上边的从原始对象中get拿数据向新对象set,比较复杂
BeanUtils.copyProperties(dto,courseBaseNew);//只要属性名称一致就可以拷贝
courseBaseNew.setCompanyId(companyId);
courseBaseNew.setCreateDate(LocalDateTime.now());
//审核状态默认为未提交
courseBaseNew.setAuditStatus("202002");
//发布状态为未发布
courseBaseNew.setStatus("203001");
//插入数据库
int insert = courseBaseMapper.insert(courseBaseNew);
if(insert<=0){
throw new RuntimeException("添加课程失败");
}
//向课程营销系courese_market写入数据
CourseMarket courseMarketNew = new CourseMarket();
//将页面输入的数据拷贝到courseMarketNew
BeanUtils.copyProperties(dto,courseMarketNew);
//课程的id
Long courseId = courseBaseNew.getId();
courseMarketNew.setId(courseId);
//保存营销信息
saveCourseMarket(courseMarketNew);
//从数据库查询课程的详细信息,包括两部分
CourseBaseInfoDto courseBaseInfo = getCourseBaseInfo(courseId);
return courseBaseInfo;
}
//查询课程信息
public CourseBaseInfoDto getCourseBaseInfo(long courseId){
//从课程基本信息表查询
CourseBase courseBase = courseBaseMapper.selectById(courseId);
if(courseBase==null){
return null;
}
//从课程营销表查询
CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
//组装在一起
CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto();
BeanUtils.copyProperties(courseBase,courseBaseInfoDto);
if(courseMarket!=null){
BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);
}
//通过courseCategoryMapper查询分类信息,将分类名称放在courseBaseInfoDto对象
CourseCategory courseCategory1 = courseCategoryMapper.selectById(courseBase.getMt());
courseBaseInfoDto.setMtName(courseCategory1.getName());
CourseCategory courseCategory2 = courseCategoryMapper.selectById(courseBase.getSt());
courseBaseInfoDto.setStName(courseCategory2.getName());
return courseBaseInfoDto;
}
//单独写一个方法保存营销信息,逻辑:存在则更新,不存在则添加
private int saveCourseMarket(CourseMarket courseMarketNew){
//参数的合法性校验
String charge = courseMarketNew.getCharge();
if(StringUtils.isEmpty(charge)){
throw new RuntimeException("收费规则为空");
}
//如果课程收费,价格没有填写也需要抛出异常
if(charge.equals("201001")){
if(courseMarketNew.getPrice() ==null || courseMarketNew.getPrice().floatValue()<=0){
// throw new RuntimeException("课程的价格不能为空并且必须大于0");
XueChengPlusException.cast("课程的价格不能为空并且必须大于0");
}
}
//从数据库查询营销信息,存在则更新,不存在则添加
Long id = courseMarketNew.getId();//主键
CourseMarket courseMarket = courseMarketMapper.selectById(id);
if(courseMarket == null){
//插入数据库
int insert = courseMarketMapper.insert(courseMarketNew);
return insert;
}else{
//将courseMarketNew拷贝到courseMarket
BeanUtils.copyProperties(courseMarketNew,courseMarket);
// courseMarket.setId(courseMarketNew.getId());感觉重复了
//更新
int i = courseMarketMapper.updateById(courseMarket);
return i;
}
}
}
12、全局异常处理
和前端约定返回的异常信息模型
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;
}
}
通用错误信息
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;
}
}
本项目自定义异常类型
package com.xuecheng.base.exception;
public class XueChengPlusException extends RuntimeException {
private String errMessage;
public XueChengPlusException() {
}
public XueChengPlusException(String message) {
super(message);
this.errMessage = message;
}
public String getErrMessage() {
return errMessage;
}
public void setErrMessage(String errMessage) {
this.errMessage = errMessage;
}
public static void cast(String message){
throw new XueChengPlusException(message);
}
public static void cast(CommonError error){
throw new XueChengPlusException(error.getErrMessage());
}
}
package com.xuecheng.base.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
//@RestControllerAdvice
public class GlobalExceptionHandler {
//对项目的自定义异常类型进行处理
//将信息返回为json格式
(XueChengPlusException.class)
(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;
}
(Exception.class)
(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse exception(Exception e){
//记录异常
log.error("系统异常{}",e.getMessage(),e);
//解析出异常信息
RestErrorResponse restErrorResponse = new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());
return restErrorResponse;
}
}