写在前面

非常有必要说明一下,这个前端学的真的有点小乱小迷糊,因为它的代码太跳跃了,东一个西一个,虽然我知道都是封装好的但是还是得想好一会,也许是我没有系统的学习Vue吧,前端的知识基本上都是在写项目的过程中积累的,但没啥基础,能看懂但是不会写。还有前端的不规范性真的很不习惯,写错单词也不会报错,找错误就得好一会,也许多加字母了,也许漏写了,都不会给你提示报错,真的还挺痛苦的。
这两天有点像是大杂烩,实现了前端的讲师管理代码还有阿里云的云存储以及上传图片的后端代码,知识点还挺多。前端的我就直接挂源码了,看得懂就行。后端的知识得好好说一说。

前端讲师页面

讲师路由

在老师给的前端页面资料中,在src > router > index.js里面添加讲师路由,实现在操作中的页面跳转

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
    //index.js
{
path: '/teacher',
component: Layout,
redirect: '/teacher/table',
name: '讲师管理',
meta: { title: '讲师管理', icon: 'example' },
children: [
{
path: 'table',
name: '讲师列表',
component: () => import('@/views/edu/teacher/list'),
meta: { title: '讲师列表', icon: 'table' }
},
{
path: 'save',
name: '添加讲师',
component: () => import('@/views/edu/teacher/save'),//@/指的是当前文件所在的根目录下
meta: { title: '添加讲师', icon: 'tree' }
},
{
path: 'edit/:id',
name: 'EduTeacherEdit',
component: () => import('@/views/edu/teacher/save'),
meta: { title: '编辑讲师', noCache: true },
hidden: true
}
]
},

axios的封装

老师将axios的代码全都封装了,真的很不习惯,很难看懂它是怎么样封装的
在src > utils > request.js下实现axios封装,节省了很多代码量

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
import axios from 'axios'
import { Message, MessageBox } from 'element-ui'
import store from '../store'
import { getToken } from '@/utils/auth'

// 创建axios实例
const service = axios.create({
baseURL: process.env.BASE_API, // api 的 base_url
timeout: 5000 // 请求超时时间
})

// request拦截器
service.interceptors.request.use(
config => {
if (store.getters.token) {
config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config
},
error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)

// response 拦截器
service.interceptors.response.use(
response => {
/**
* code为非20000是抛错 可结合自己业务进行修改
*/
const res = response.data
if (res.code !== 20000) {
Message({
message: res.message,
type: 'error',
duration: 5 * 1000
})

// 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
MessageBox.confirm(
'你已被登出,可以取消继续留在该页面,或者重新登录',
'确定登出',
{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
})
})
}
return Promise.reject('error')
} else {
return response.data
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)

export default service

讲师的前端请求

老师单独写在了 src > api > edu > teacher.js下,前端请求接口一目了然

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
import request from '@/utils/request'

export default {

//1 讲师列表(条件查询分页)
//current当前页 limit每页显示数 teacherQuery条件对象
getTeacherListPage(current,limit,teacherQuery){
return request({
//url: 'eduservice/'+current+"/"+limit,
url: `/eduservice/teacher/pageTeacherCondition/${current}/${limit}`,
method: 'post',
//teacherQuery条件对象 ,后端使用RequestBody获取数据
//data表示把对象转换json进行传递到接口里面
data: teacherQuery
})
},

//删除讲师
deleteTeacherId(id){
return request({
url: `/eduservice/teacher/${id}`,
method: 'delete',
})
},

//添加讲师
addTeacher(teacher){
return request({
url: `/eduservice/teacher/addTeacher`,
method: 'post',
data: teacher
})
},
//回显讲师
getTeacherInfo(id) {
return request({
url: `/eduservice/teacher/getTeacher/${id}`,
method: 'get'
})
},

//修改讲师
updateTeacher(teacher){
return request({
url: `/eduservice/teacher/updateTeacher`,
method: 'put',
data: teacher
})
}
}

前端页面的展示部分

没事就多看看这两个vue代码,基本上这两个看会了vue前端也就差不多了,记录一下。

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
<!--回显讲师数据的页面-->
<template>
<div class="app-container">
<!--查询表单-->
<el-form :inline="true" class="demo-form-inline">
<el-form-item>
<el-input v-model="teacherQuery.name" placeholder="讲师名"/>
</el-form-item>
<el-form-item>
<el-select v-model="teacherQuery.level" clearable placeholder="讲师头衔">
<el-option :value="1" label="高级讲师"/>
<el-option :value="2" label="首席讲师"/>
</el-select>
</el-form-item>
<el-form-item label="添加时间">
<el-date-picker
v-model="teacherQuery.begin"
type="datetime"
placeholder="选择开始时间"
value-format="yyyy-MM-dd HH:mm:ss"
default-time="00:00:00"
/>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="teacherQuery.end"
type="datetime"
placeholder="选择截止时间"
value-format="yyyy-MM-dd HH:mm:ss"
default-time="00:00:00"
/>
</el-form-item>
<el-button type="primary" icon="el-icon-search" @click="getList()">查询</el-button>
<el-button type="default" @click="resetData()">清空</el-button>
</el-form>
<!-- 表格 -->
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="数据加载中"
border
fit
highlight-current-row>
<el-table-column
label="序号"
width="70"
align="center">
<template slot-scope="scope">
{{ (page - 1) * limit + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="name" label="名称" width="80" />
<el-table-column label="头衔" width="80">
<template slot-scope="scope">
{{ scope.row.level===1?'高级讲师':'首席讲师' }}
</template>
</el-table-column>
<el-table-column prop="intro" label="资历" />
<el-table-column prop="gmtCreate" label="添加时间" width="160"/>
<el-table-column prop="sort" label="排序" width="60" />
<el-table-column label="操作" width="200" align="center">
<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>
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>


<!-- 分页 --><!-- : 相当于v-bind实现单向绑定 -->
<el-pagination
:current-page="page"
:page-size="limit"
:total="total"
style="padding: 30px 0; text-align: center;"
layout="total, prev, pager, next, jumper"
@current-change="getList"
/>
</div>
</template>

<script>

//引入调用teacher.js文件
import teacher from '@/api/edu/teacher.js'

export default {
//写核心代码位置
data() {
return { //定义变量和初始值
list: null, //查询之后接口返回集合
page: 1, //当前页
limit: 10, //每页记录数
total: 0, //总记录数
teacherQuery: {} //条件封装对象
}
},
created() { //页面渲染之前执行,一般调用methods定义的方法
//调用methods里面的方法
this.getList()
},
methods: { //创建具体的方法,调用teacher.js定义的方法
//讲师列表的方法
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)
})//请求失败
},

resetData() {
//表单输入项数据清空
this.teacherQuery = {}

//查询所有讲师数据
this.getList()

},
//删除讲师的方法
removeDataById(id) {
// debugger
// console.log(memberId)
this.$confirm('此操作将永久删除讲师记录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => { //点击确定,执行then方法
//调用删除的方法
teacher.deleteTeacherId(id)
.then(response => { //删除成功
// 提示信息
this.$message({
type: 'success',
message: '删除成功!'
});
//回到列表页面
this.getList()
})
})//点击取消方法省略
}
}

}
</script>

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
<!--添加讲师和修改讲师页面,包括了第六天的图片上传前端代码-->
<template>
<div class="app-container">
<el-form label-width="120px">
<el-form-item label="讲师名称">
<el-input v-model="teacher.name"/>
</el-form-item>
<el-form-item label="讲师排序">
<el-input-number v-model="teacher.sort" controls-position="right" min="0"/>
</el-form-item>
<el-form-item label="讲师头衔">
<el-select v-model="teacher.level" clearable placeholder="请选择">
<!--
数据类型一定要和取出的json中的一致,否则没法回填
因此,这里value使用动态绑定的值,保证其数据类型是number
-->
<el-option :value="1" label="高级讲师"/>
<el-option :value="2" label="首席讲师"/>
</el-select>
</el-form-item>
<el-form-item label="讲师资历">
<el-input v-model="teacher.career"/>
</el-form-item>
<el-form-item label="讲师简介">
<el-input v-model="teacher.intro" :rows="10" type="textarea"/>
</el-form-item>

<!-- 讲师头像:TODO -->
<!-- 讲师头像 -->
<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>

<el-form-item>
<el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>

<script>

//引入调用teacher.js文件
import teacherApi from '@/api/edu/teacher.js'
import ImageCropper from '@/components/ImageCropper'//引入组件
import PanThumb from '@/components/PanThumb'

export default {
components: { ImageCropper, PanThumb },//声明组件
data() {
return {
teacher: {
name: '',
sort: 0,
level: 1,
career: '',
intro: '',
avatar: ''
},
//上传弹框组件是否显示
imagecropperShow:false,
imagecropperKey:0,//上传组件唯一标识
BASE_API:process.env.BASE_API,//获取dev.env.js里面地址
saveBtnDisabled:false //保存按钮是否禁用
}
},
created() { //页面渲染之前执行
this.init()
},

watch: { //监听
$route(to,from) { //路由变化的方式,路由发生变化,方法就会被执行
this.init()
}
},

methods: {

close() { //关闭上传弹框的方法
this.imagecropperShow=false
//上传组件初始化
this.imagecropperKey = this.imagecropperKey+1
},
//图片上传成功的方法
cropSuccess(data) {
this.imagecropperShow=false
//上传之后接口返回图片地址
this.teacher.avatar=data.url
this.imagecropperKey = this.imagecropperKey+1
},
init() {
//判断路径有id值,做修改
console.log('created')
if (this.$route.params && this.$route.params.id) {
//从路径获取id值
const id = this.$route.params.id
//调用根据id查询方法
this.getInfo(id)
}else { //路径没有id值,做添加
//清空表单
this.teacher = {}

}
},

//根据讲师id查询方法
getInfo(id){
teacherApi.getTeacherInfo(id)
.then(response => {
this.teacher = response.data.teacher
})
},

//修改讲师
updateTeacher(teacher) {
teacherApi.updateTeacher(this.teacher)
.then(response => {
//提示信息
this.$message({
type: 'success',
message: '修改成功!'
});
//回到列表页面 路由跳转
this.$router.push({path:'/teacher/table'})
})
},

saveOrUpdate() {
//判断修改还是添加
//根据teacher是否有id
if(!this.teacher.id){
//添加
this.saveTeacher()
}else {
//修改
this.updateTeacher()
}

},

//添加讲师方法
saveTeacher() {
teacherApi.addTeacher(this.teacher)
.then(response => { //添加成功
//提示信息
this.$message({
type: 'success',
message: '添加成功!'
});
//回到列表页面 路由跳转
this.$router.push({path:'/teacher/table'})

})
}
}
}
</script>

搭建阿里云oss操作项目环境

基础配置

基本上就和第一天的一致,就改成8002端口然后不需要使用数据库所以就controller和service以及utils包就足够。
重点讲一下这个报错问题:
当这个oss模块不会使用到数据库操作时而且配置文件也没有配置数据库,那么就会报错
Description: Failed to configure a DataSource: url' attribute is not specif Reason: Failed to determine a suitable driver class
原因就是启动的时候,找数据库配置,但是现在模块因为不需要数据库,只是做上传到oss功能。没有配置数据库,spring找不到数据库配置就会报错

解决方式:
1、添加上数据库配置
2、在启动类添加属性,默认不去加载数据库配置

1
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

将图片上传到阿里oss

首先写了一个工具类,将阿里密钥和一些服务器ip用静态方式得到
很有意思的是看起来是私有的变量实际上用静态公有来得到,原因就是要想用springboot配置文件的参数,就必须用@value来得到,但是私有成员变量用静态就取不到value里面的值,所以只能多加公有静态变量直接后面可以直接.出来

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
package com.atguigu.oss.utils;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//当项目一启动, spring接口, spring加载之后,执行接口的一个方法
@Component
public class ConstAndPropertiesUtils implements InitializingBean {

//读取配置文件内容
@Value("${aliyun.oss.file.endpoint}")
private String endpoint;//不能直接用static静态,不然Value不能赋值给成员变量

@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 KEY_ID;
public static String KEY_SECRET;
public static String BUCKET_NAME;
@Override
public void afterPropertiesSet() throws Exception {
END_POINT = endpoint;
KEY_ID = keyId;
KEY_SECRET = keySecret;
BUCKET_NAME = bucketName;
}
}

核心代码就在OssServiceImpl,我就直接贴上来了

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
package com.atguigu.oss.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.atguigu.oss.service.OssService;
import com.atguigu.oss.utils.ConstAndPropertiesUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.UUID;

@Service
public class OssServiceImpl implements OssService {
@Override
public String uploadFileAvatar(MultipartFile file) {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
//调用我们自己写的ConstAndPropertiesUtils工具类,底层实际上是在.properties文件用value取值
String endpoint = ConstAndPropertiesUtils.END_POINT;
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = ConstAndPropertiesUtils.KEY_ID;
String accessKeySecret = ConstAndPropertiesUtils.KEY_SECRET;
// 填写Bucket名称。
String bucketName = ConstAndPropertiesUtils.BUCKET_NAME;

// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

try {
//获取上传文件输入流
InputStream inputStream = file.getInputStream();

//获取文件名称
String fileName = file.getOriginalFilename();

//1 在文件名称里面添加随机唯一的值
String uuid = UUID.randomUUID().toString().replace("-","");
fileName = uuid+fileName;

//2 把文件按照日期进行分类
//2019//11/12/01.jpg
//获取当前的日期
String nowTime = new DateTime().toString("yyyy/MM/dd");
//继续拼接
fileName = nowTime+"/"+fileName;

//调用oss方法实现上传
//第一个参数 Bucket名称
//第二个参数 上传到oss文件路径的名称和文件名称
//第三个参数 输入流对象
ossClient.putObject(bucketName, fileName, inputStream);

//关闭ossClient
ossClient.shutdown();

//把上传之后文件路径返回
//需要把上传到阿里云oss路径手动拼接出来
String url = "https://" + bucketName + "." + endpoint + "/" + fileName;

return url;
} catch (Exception oe) {
oe.printStackTrace();
return null;
}
}
}

课程分类模块

创建edu_subject数据库

终于到了新的功能区,知识点还是很多的,干货满满

1
2
3
4
5
6
7
8
9
10
CREATE TABLE `edu_subject` (
`id` char(19) NOT NULL COMMENT '课程类别ID',
`title` varchar(10) NOT NULL COMMENT '类别名称',
`parent_id` char(19) NOT NULL DEFAULT '0' COMMENT '父ID',
`sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序字段',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='课程科目';

基础框架搭建

和第一天的创建方法一致,代码生成器一键生成,还是在service_edu模块下,非常香!!所以不多说了嘻嘻

EasyExcel的使用(技术点)

之前我就有了解过,现在正式学,收获很多很多

创建表头类

1
2
3
4
5
6
7
8
9
10
11
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

@Data
public class DemoData {
//设置excel表头的名称,index表示excel对应的列关系
@ExcelProperty(value = "学生编号",index = 0)
private Integer sno;
@ExcelProperty(value = "学生姓名",index = 1)
private String name;
}

读操作必备监听器

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
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

import java.util.Map;

public class ExcelListener extends AnalysisEventListener<DemoData> {

//一行一行读取excel内容
@Override
public void invoke(DemoData demoData, AnalysisContext analysisContext) {
System.out.println("***" + demoData);
}

@Override
public void invokeHeadMap(Map<Integer,String > headMap,AnalysisContext context){
System.out.println("表头:" +headMap);
}

//读取完之后的操作
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {

}
}

EasyExcel方法实现

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
package com.atguigu.demo.excel;
import com.alibaba.excel.EasyExcel;
import java.util.ArrayList;
import java.util.List;

public class TestEasyExcel {

public static void main(String[] args) {
//实现excel写操作
//1 设置写入文件夹的地址和excel文件名称
String filename = "D:\\student.xlsx";

//调用easyExcel方法实现写操作
//write方法两个参数:第一个参数文件地址和名称,第二个参数实体类.class
//sheet方法指的是excel下面那个表栏
//doWrite()表示你要写的东西,传的是集合
EasyExcel.write(filename, DemoData.class).sheet("学生列表").doWrite(getData());

//实现excel读操作
String filename1 = "D:\\student.xlsx";
EasyExcel.read(filename1, DemoData.class,new ExcelListener()).sheet().doRead();
}

//创建方法返回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.setName("herry" + i);
list.add(data);
}
return list;
}
}

EasyExcel实战

需求:实现使用excel表格的形式添加一级课程和二级课程到数据库中
基本思路:
实际上是一个读的操作,那么就需要表头类和监听器,主要的功能在监听器中实现,在这过程中有一个细节就是监听器类不能够交给spring管理,只能够手动的调用service类,然后用有参构造方法赋值来得到。详细见代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Autowired
private EduSubjectService subjectService;

//添加课程分类
//获取上传过来的文件,把文件内容读取出来
//这个参数file指的就是客户上传的文件,可以读到
@PostMapping("addSubject")
public R addSubject(MultipartFile file){
//上传过来的excel文件

subjectService.saveSubject(file,subjectService);//传入subjectService以便后面在监听器中使用

return R.ok();
}

下面是服务层接口

1
2
3
4
public interface EduSubjectService extends IService<EduSubject> {
//添加课程分类
void saveSubject(MultipartFile file,EduSubjectService subjectService);
}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
//添加课程分类
@Override
public void saveSubject(MultipartFile file, EduSubjectService subjectService) {
try{
//文件输入流
InputStream inputStream = file.getInputStream();
//调用方法进行读取
EasyExcel.read(inputStream, SubjectDemo.class,new SubjectExcelListener(subjectService)).sheet().doRead();
}catch (Exception e){
e.printStackTrace();
}
}
}

监听器中实现核心代码

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
public class SubjectExcelListener extends AnalysisEventListener<SubjectDemo> {

//因为SubjectExcelListener不能交给spring进行管理,需要自己new,也不能注入其他对象
//不能实现数据库操作
//所以用有参构造自己拿
public EduSubjectService subjectService;
public SubjectExcelListener(){}
public SubjectExcelListener(EduSubjectService subjectService){
this.subjectService = subjectService;
}

//读取excel内容,一行一行的读取数据
@Override
public void invoke(SubjectDemo subjectDemo, AnalysisContext analysisContext) {
if(subjectDemo == null){
throw new GuliException(20001,"文件数据为空");
}

//一行一行读取,每行读取有两个值,第一个值是一级分类,第二个值是二级分类
//判断一级分类是否重复
EduSubject existOneSubject = this.existOneSubject(subjectService, subjectDemo.getOneSubjectName());
if(existOneSubject == null){ //没有相同一级分类,进行添加
existOneSubject = new EduSubject();
existOneSubject.setParentId("0");
existOneSubject.setTitle(subjectDemo.getOneSubjectName());//一级分类名称
subjectService.save(existOneSubject);
}

//判断二级分类是否重复
String pid = existOneSubject.getId();//取一级分类的id值

EduSubject existTowSubject = this.existTowSubject(subjectService, subjectDemo.getTwoSubjectName(), pid);
if(existTowSubject == null){
existTowSubject = new EduSubject();
existTowSubject.setParentId(pid);
existTowSubject.setTitle(subjectDemo.getTwoSubjectName());//一级分类名称
subjectService.save(existTowSubject);
}
}

//判断一级分类不能重复添加,比如前端开发的一级分类不可能出现多次
private EduSubject existOneSubject(EduSubjectService subjectService,String name){
QueryWrapper<EduSubject> wrapper =new QueryWrapper<>();
wrapper.eq("title",name);
wrapper.eq("parent_id","0");
EduSubject oneSubject = subjectService.getOne(wrapper);
return oneSubject;
}
//判断二级分类不能重复添加
private EduSubject existTowSubject(EduSubjectService subjectService,String name,String pid){
QueryWrapper<EduSubject> wrapper =new QueryWrapper<>();
wrapper.eq("title",name);
wrapper.eq("parent_id",pid);
EduSubject twoSubject = subjectService.getOne(wrapper);
return twoSubject;
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {

}
}

最后

要记录一下吧,今天是我的生日啦,写完这篇我就要去许愿啦,真的希望能够实现叭
嘉楠,祝你生日快乐
一切的沉淀终将会有收获