8.25重点内容
1.自动填充
1.1实体类加注解 @TableField(fill = FieldFill.INSERT)
1.2在common创建自动填充类,实现MetaObjectHandler添加组件
//自动填充组件
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//添加时候用
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
//修改时候用
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
}
2.修改功能
2.1先根据id查询后修改:updateById(eduTeacher),eduTeacher对象中包含id,可以这样写
3.全局异常处理方式
3.1在common中创建统一异常处理类
3.2需要R对象,需要在common中导入service的相关依赖
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public R error(Exception e){
e.printStackTrace();
return R.error();
}
}
4.自定义异常:需要手动抛出
4.1在common中创建自定义类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GuliException extends RuntimeException {
private Integer code;
private String msg;
}
4.2在全局异常中添加方法
//自定义异常处理
@ExceptionHandler(GuliException.class)
@ResponseBody
public R error(GuliException e){
e.printStackTrace();
return R.error().code(e.getCode()).message(e.getMsg());
4.3在控制层测试,需要手动抛出异常
try {
int i=10/0;
}catch (Exception e){
//执行自定义异常处理
throw new GuliException(20001, "执行了自定义异常");
}
5.统一日志管理
5.1设置日志级别:配置类中 logging.level.root=WARN
5.2日志不仅输出到控制台,也可以输出到文件中,用日志工具Logback,配置xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="D:/guli_log/edu" />
<!-- 彩色日志 -->
<!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<!-- 例如:如果此处配置了INFO级别,则后面其他位置即使配置了DEBUG级别的日志,也不会被输出 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 WARN 日志 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
如果未设置此属性,那么当前logger将会继承上级的级别。
-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别:
-->
<!--开发环境:打印控制台-->
<springProfile name="dev">
<!--可以输出项目中的debug日志,包括mybatis的sql日志-->
<logger name="com.guli" level="INFO" />
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG
可以包含零个或多个appender元素。
-->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
<!--生产环境:输出到文件-->
<springProfile name="pro">
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</springProfile>
</configuration>
5.3把异常信息输出到文件中
加注解:@Slf4j
加在catch中加log.error(e.getMessage());
6.axios,经常和vue一起使用,实现ajax请求
7.node.js 相当于java中的jdk, 是js的运行环境,不需要浏览器,直接在node.js中运行
8.npm是包管理工具 相当于java中的maven, 管理js依赖
9.babel是转换器,把es6代码转换成es5代码
10.webpack是打包工具,可以把多个静态资源文件打包成一个文件
11.b2c模式,分为前台用户系统和后台管理系统,后台管理系统中有前端页面和后端接口
12.把系统登录功能改造本地
第一步:把地址改为本地
第二步:点登录调用的方法
第三步:创建接口写前端需要返回的属性数据
@RestController
@RequestMapping("/eduservice/user")
@CrossOrigin
public class EduLoginController {
//登录login
@PostMapping("login")
public R login(){
return R.ok().data("token","admin");
}
//获取用户数据info
@GetMapping("info")
public R info(){
return R.ok().data("roles", "[admin]").data("name", "admin").data("avatar", "https://n.sinaimg.cn/sinacn20118/600/w1080h1920/20190402/b757-hvcmeux6975565.jpg");
}
}
13.如何在框架中加功能:看别人的功能是怎么开发的,仿照着做
开发流程:
0.添加路由:src-router-index
1.在路由中引入list-vue页面component: () => import(’@/views/edu/teacher/list’)
2.在list-vue页面中引入js,import teacher from ‘@/api/edu/teacher’
3.js文件时定义接口的地址,js是在vue新建方法时调用使用
4.在list-vue页面中调用js方法
5.利用element-ui组件把请求接口返回的数据呈现在页面上
14.条件查询+分页
输入框样式上:v-model="teacherQuery.level"aaa@qq.com="getList()
分页样式上:@current-change="getList"
methods:中写具体方法
created中在页面渲染之前调用methods中的方法
<script>
import teacher from '@/api/edu/teacher'
export default {//表示可以被别人调用,不用写vue对象了,在main.js中封装好了
//写核心代码位置
data(){//定义变量和初始值
return{
list:null,//查询之后接口返回的集合
page:1,//当前页
limit:5,//每页显示记录数
total:0,//总记录数
teacherQuery:{}//条件封装对象
}
},
created(){//页面渲染之前执行,调用methods定义的方法
this.getList()
},
methods:{//创建具体的方法,调用teacher.js定义的方法
//1.查询讲师列表
getList(page=1){
this.page=page
teacher.getTeacherListPage(this.page,this.limit,this.teacherQuery)
.then(response =>{//请求成功
//response接口返回的数据
this.list=response.data.records
this.total=response.data.total
console.log(this.list)
console.log(this.total)
})
.catch(error =>{//请求失败
console.log(error)
})
},
//2.清空查询条件并返回页面
resetData(){
//1.表单输入项数据为空
this.teacherQuery={}
//2.查询所有讲师数据
this.getList()
},
//3.逻辑删除并返回列表
removeDataById(id){
this.$confirm('此操作将永久删除讲师记录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {//点击确定执行then方法
teacher.deleteTeacherId(id).then(response =>{//删除成功
//提示信息
this.$message({
type: 'success',
message: '删除成功!'
});
//回到列表页面
this.getList()
})
})
}
}
}
15.利用阿里云OSS上传图片
添加完成后实现不同路由之间的跳转写法: this.$router.push({path:’/teacher/table’})
从页面到后台关系 页面index-element ui-vue-js-vo-controller-service-mapper-mysql js中携带从服务器返回的数据
16.修改操作
1.在vue中 scope.row.id
<template slot-scope="scope">
<router-link :to="'/teacher/edit/'+scope.row.id">
<el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button>
</router-link>
</template>
17.如何判断点击保存的时候是添加还是修改?
在index中用路由用隐藏id的方式判断
{
path: 'save',
name: '添加讲师',
component: () => import('@/views/edu/teacher/save'),
meta: { title: '添加讲师', icon: 'tree' }
},
{
//设置隐藏路由--:id的意思相当于占位符,通过id识别是修改---隐藏id的用法
path: 'edit/:id',
name: '编辑讲师',
component: () => import('@/views/edu/teacher/save'),
meta: { title: '编辑讲师', noCache: true },
hidden: true
}
18.路由切换问题:点修改后直接点添加发现数据还在,怎么去掉数据回显?
created(){
this.init()
},
watch: {
$route(to, from) {
this.init()
}
},
//注意一点是初始化只有一次,点添加并没有再一次初始化,不会执行else中方法,需要加一个监听器
init(){
//判断路径是否有id
if (this.$route.params && this.$route.params.id) {
//从路径中获取id值
const id = this.$route.params.id
//调用根据id查询的回显方法
this.getInfo(id)
}else{//路径没有id做添加
this.teacher={}
}
},
19.利用阿里云OSS上传图片步骤
第一步:创建common子模块,修改配置文件,引入相关依赖
第二步:启动类因为不需要访问数据库只是上传图片,所以添加一个属性
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
第三步:创建一个组件,用来读取配置文件内容
@Component
public class ConstantProperties implements InitializingBean {
//用@Value读取配置文件内容赋值给属性
@Value("${aliyun.oss.file.endpoint}")
private String endpoint;
@Value("${aliyun.oss.file.keyid}")
private String keyId;
@Value("${aliyun.oss.file.keysecret}")
private String keySecret;
@Value("${aliyun.oss.file.bucketname}")
private String bucketName;
//定义公开静态常量
public static String END_POINT;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;
@Override
public void afterPropertiesSet() throws Exception {
END_POINT=endpoint;
ACCESS_KEY_ID = keyId;
ACCESS_KEY_SECRET = keySecret;
BUCKET_NAME = bucketName;
}
}
第四步:创建controller和service
@CrossOrigin
@RestController
@RequestMapping("/eduoss/fileoss")
public class OssController {
@Autowired
private OssService ossService;
//上传头像方法
@PostMapping //获取上传文件
public R uploadOssFile(MultipartFile file){
//返回上传到oss的路径
String url=ossService.uplaodFileAvatar(file);
return R.ok().data("url",url);
}
}
实现类
@Service
public class OssServiceImpl implements OssService {
//上传头像到oss
@Override
public String uplaodFileAvatar(MultipartFile file) {
// 工具类获取值
String endpoint = ConstantProperties.END_POINT;
String accessKeyId = ConstantProperties.ACCESS_KEY_ID;
String accessKeySecret = ConstantProperties.ACCESS_KEY_SECRET;
String bucketName = ConstantProperties.BUCKET_NAME;
try {
// 创建OSS实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//获取上传文件输入流
InputStream inputStream = file.getInputStream();
//获取文件名称
String fileName = file.getOriginalFilename();
//1 在文件名称里面添加随机唯一的值
String uuid = UUID.randomUUID().toString().replaceAll("-","");
//ewtqr313401.jpg,没有-了,全局唯一
fileName = uuid+fileName;
//2 把文件按照日期进行分类
//获取当前日期
String datePath = new DateTime().toString("yyyy/MM/dd");
//拼接
// 2019/11/12/ewtqr313401.jpg
fileName = datePath+"/"+fileName;
//调用oss方法实现上传
//第一个参数 Bucket名称
//第二个参数 上传到oss文件路径和文件名称 aa/bb/1.jpg
//第三个参数 上传文件输入流
ossClient.putObject(bucketName,fileName , inputStream);
// 关闭OSSClient。
ossClient.shutdown();
//把上传之后文件路径返回
//需要把上传到阿里云oss路径手动拼接出来
// https://edu-guli-1010.oss-cn-beijing.aliyuncs.com/01.jpg
String url = "https://"+bucketName+"."+endpoint+"/"+fileName;
return url;
}catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
第五步:前端
<!-- 讲师头像 -->
<el-form-item label="讲师头像">
<!-- 头衔缩略图, -->
<pan-thumb :image="teacher.avatar"/>
<!-- 文件上传按钮 -->
<el-button type="primary" icon="el-icon-upload" @click="imagecropperShow=true">更换头像
</el-button>
<!--
v-show:是否显示上传组件
:key:类似于id,如果一个页面多个图片上传控件,可以做区分
:url:后台上传的url地址
@close:关闭上传组件
@crop-upload-success:上传成功后的回调
-->
<image-cropper
v-show="imagecropperShow"
:width="300"
:height="300"
:key="imagecropperKey"
:url="BASE_API+'/eduoss/fileoss'"
field="file"
@close="close"
@crop-upload-success="cropSuccess"/>
</el-form-item>
data(){
imagecropperShow:false,//是否显示上传组件
imagecropperKey:0,//上传组件key
BASE_API:process.env.BASE_API,//获取端口号
methods:{
close(){//关闭上传弹框的方法
this.imagecropperShow=false
//上传组件初始化:每次让key值有一个变化,修复了点击保存然后再点更换头像时出现的bug
this.imagecropperKey=this.imagecropperKey+1
},
//上传成功方法
cropSuccess(data){
this.imagecropperShow=false
//上传之后接口返回图片地址
this.teacher.avatar=data.url
//每次让key值有一个变化,修复了点击保存然后再点更换头像时出现的bug
this.imagecropperKey=this.imagecropperKey+1
},
20.EasyExcel
第一步:引入依赖
第二步:创建实体类,和excel数据对应
@Data
public class DemoData {
//设置excel表头名称
@ExcelProperty(value = "学生编号",index = 0)
private Integer sno;
@ExcelProperty(value = "学生姓名",index = 1)
private String sname;
}
第三步:实现excel写操作
public static void main(String[] args) {
//1.设置写入文件夹地址和excel文件名称
String filename="F:\\write.xlsx";
//2.调用easyexcel里面的方法实现操作
EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData());
}
//创建方法返回list集合
private static List<DemoData> getData(){
List<DemoData> list=new ArrayList<>();
for(int i=0;i<10;i++){
DemoData data=new DemoData();
data.setSno(i);
data.setSname("lucy"+i);
list.add(data);
}
return list;
}
第四步:实现exce读操作
1.前提是创建监听器
//创建监听器(固定)
public class ExcelListener extends AnalysisEventListener<DemoData> {
//一行一行读取excel内容
@Override
public void invoke(DemoData data, AnalysisContext analysisContext) {
System.out.println("***"+data);
}
//读取表头内容
@Override
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
System.out.println("表头"+headMap);
}
//读取完成之后
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
2.实现excel读操作
public static void main(String[] args) {
//1.设置写入文件夹地址和excel文件名称
String filename = "F:\\write.xlsx";
//2.调用easyexcel里面的方法实现操作
EasyExcel.read(filename, DemoData.class, new ExcelListener()).sheet().doRead();
}
}
21.EasyExcel应用到项目中
第一步:创建一级分类和二级分类的实体类
//一级分类:对应数据库的实体类
@Data
public class OneSubject {
private String id;
private String title;
//一个一级分类中包含多个二级分类
private List<TwoSubject> children=new ArrayList<>();
}
//二级分类,一级分类有多个二级分类
@Data
public class TwoSubject {
private String id;
private String title;
}
第二步:创建监听器,不交给spring管理,因为在实现类中需要new对象,在SubjectExcelListener 中需要手动用构造方法形式注入service,同时在实现类中传入service接口到SubjectExcelListener 中,这样SubjectExcelListener 就能调用service方法,间接实现@Autowied,可以调方法做添加!
//监听器
//SubjectExcelListener不能交给spring管理,因为实现类中要new SubjectExcelListener,不交给spring创建对象,不能实现数据库操作
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
//改造:手动注入业务层,手动创建有参和无参构造,为了后面做添加操作
private EduSubjectService eduSubjectService;
public SubjectExcelListener(EduSubjectService eduSubjectService) {
this.eduSubjectService = eduSubjectService;
}
public SubjectExcelListener() {}
//读取excel内容,一行一行读
@Override
public void invoke(SubjectData data, AnalysisContext context) {
//1.判断
if(data==null){
throw new GuliException(20001,"文件数据为空");
}
//3.一行一行读,每次读取两个值,第一个值是一级分类,第二个值是二级分类
//添加一级分类
//3.1先判断一级分类是否重复 一级分类名称
EduSubject existOneSubject = this.existOneSubject(eduSubjectService, data.getOneSubjectName());
if(existOneSubject==null){
//没有相同的一级分类,允许添加,先创建表属性的实例对象
existOneSubject=new EduSubject();
existOneSubject.setParentId("0");
existOneSubject.setTitle(data.getOneSubjectName());//一级分类名称
eduSubjectService.save(existOneSubject);
}
//添加二级分类
//3.2判断二级分类是否重复
//获取二级分类id值
String pid=existOneSubject.getId();
EduSubject existTwoSubject = this.existTwoSubject(eduSubjectService, data.getTwoSubjectName(), pid);
if(existTwoSubject==null){
//没有相同的二级分类,允许添加,先创建表属性的实例对象
existTwoSubject=new EduSubject();
existTwoSubject.setParentId(pid);
existTwoSubject.setTitle(data.getTwoSubjectName());//二级分类名称
eduSubjectService.save(existTwoSubject);
}
}
//2.1判断一级分类不能重复添加 一级分类名称
private EduSubject existOneSubject(EduSubjectService eduSubjectService,String name){
QueryWrapper<EduSubject> wrapper=new QueryWrapper<>();
wrapper.eq("title", name);
wrapper.eq("parent_id", "0");
EduSubject oneSubject = eduSubjectService.getOne(wrapper);
return oneSubject;
}
//2.2判断二级分类不能重复添加
private EduSubject existTwoSubject(EduSubjectService eduSubjectService,String name,String pid){
QueryWrapper<EduSubject> wrapper=new QueryWrapper<>();
wrapper.eq("title", name);
wrapper.eq("parent_id", pid);
EduSubject twoSubject = eduSubjectService.getOne(wrapper);
return twoSubject;
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}
第三步:控制层
//添加课程分类
//获取上传过来的文件,把文件内容读取
@PostMapping("addSubject")
public R addSubject(MultipartFile multipartFile){
eduSubjectService.saveSubject(multipartFile,eduSubjectService);
return R.ok();
}
第四步:实现类
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
//添加课程分类
@Override
public void saveSubject(MultipartFile multipartFile,EduSubjectService eduSubjectService) {
try {
//1.获取上传文件输入流
InputStream in=multipartFile.getInputStream();
//2.执行读操作
EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(eduSubjectService)).sheet().doRead();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public List<OneSubject> getAllOneTwoSubject() {
//1.查询一级分类 parentId=0,EduSubject是数据库表格的字段
QueryWrapper<EduSubject> WrapperOne=new QueryWrapper();
WrapperOne.eq("parent_id", "0");
List<EduSubject> oneSubjectList=baseMapper.selectList(WrapperOne);
//2.查询二级分类 parentId!=0
QueryWrapper<EduSubject> WrapperTwo=new QueryWrapper();
WrapperTwo.ne("parent_id", "0");
List<EduSubject> twoSubjectList=baseMapper.selectList(WrapperTwo);
//****先创建一个list集合,返回封装好的数据
List<OneSubject> finalSubjectList=new ArrayList<>();
//3.封装一级分类
//遍历oneSubjectList集合,得到每一个EduSubject对象
for (int i = 0; i < oneSubjectList.size(); i++) {
EduSubject oneEduSubject = oneSubjectList.get(i);
//把eduSubject中的值放到OneSubject对象中,多个OneSubject放到finalSubjectList中1
OneSubject oneSubject=new OneSubject();
// oneSubject.setId(eduSubject.getId());
// oneSubject.setTitle(eduSubject.getTitle());
//简单方法:
BeanUtils.copyProperties(oneEduSubject, oneSubject);
finalSubjectList.add(oneSubject);
//4.封装二级分类:在一级分类遍历中查询所有二级分类---嵌套for循环
//遍历二级分类list集合
List<TwoSubject> twoFinalSubject=new ArrayList<>();
for (int m = 0; m < twoSubjectList.size(); m++) {
//获取每一个二级分类
EduSubject twoEduSubject = twoSubjectList.get(m);
//判断:循环遍历获取到的二级分类twoEduSubject的parentId和一级分类的id是否一致,一致的话就封装
if (twoEduSubject.getParentId().equals(oneEduSubject.getId())){
//先创建一个封装二级分类的对象------------检查无误后打包走人
TwoSubject twoSubject=new TwoSubject();
BeanUtils.copyProperties(twoEduSubject, twoSubject);
//将封装好的二级分类对象twoSubject,二次封装到集合中
twoFinalSubject.add(twoSubject);
}
}
//5.把二次封装好的二级分类twoFinalSubject放到一级分类中,存到一级分类实例对象OneSubject中
oneSubject.setChildren(twoFinalSubject);
}
return finalSubjectList;
}
}
上一篇: JS知识点合集