1 整合阿里云OSS
01-阿里云存儲(chǔ)OSS
一、對(duì)象存儲(chǔ)OSS
為了解決海量數(shù)據(jù)存儲(chǔ)與彈性擴(kuò)容,項(xiàng)目中我們采用云存儲(chǔ)的解決方案- 阿里云OSS。?
1、開(kāi)通“對(duì)象存儲(chǔ)OSS”服務(wù)
(1)申請(qǐng)阿里云賬號(hào)
(2)實(shí)名認(rèn)證
(3)開(kāi)通“對(duì)象存儲(chǔ)OSS”服務(wù)
(4)進(jìn)入管理控制臺(tái)
2、創(chuàng)建Bucket
選擇:標(biāo)準(zhǔn)存儲(chǔ)、公共讀、不開(kāi)通
3、上傳默認(rèn)頭像
創(chuàng)建文件夾avatar,上傳默認(rèn)的用戶頭像


02-后端集成OSS
一、新建云存儲(chǔ)微服務(wù)
1、在service模塊下創(chuàng)建子模塊service-oss
2、配置pom.xml
service-oss上級(jí)模塊service已經(jīng)引入service的公共依賴,所以service-oss模塊只需引入阿里云oss相關(guān)依賴即可,
service父模塊已經(jīng)引入了service-base模塊,所以Swagger相關(guān)默認(rèn)已經(jīng)引入
<dependencies>
? ? ? ? <!-- 阿里云oss依賴 -->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>com.aliyun.oss</groupId>
? ? ? ? ? ? <artifactId>aliyun-sdk-oss</artifactId>
? ? ? ? </dependency>
? ? ? ? <!-- 日期工具欄依賴 -->
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>joda-time</groupId>
? ? ? ? ? ? <artifactId>joda-time</artifactId>
? ? ? ? </dependency>
? ? </dependencies>
3、配置application.properties
#服務(wù)端口
server.port=8002
#服務(wù)名
spring.application.name=service-oss
#環(huán)境設(shè)置:dev、test、prod
spring.profiles.active=dev
#阿里云OSS
#不同的服務(wù)器,地址不同
aliyun.oss.file.endpoint=your endpoint
aliyun.oss.file.keyid=your accessKeyId
aliyun.oss.file.keysecret=your accessKeySecret
#bucket可以在控制臺(tái)創(chuàng)建,也可以使用java代碼創(chuàng)建
aliyun.oss.file.bucketname=guli-file
4、logback-spring.xml
5、創(chuàng)建啟動(dòng)類
創(chuàng)建OssApplication.java
package com.atguigu.oss;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"})
public class OssApplication {
? ? public static void main(String[] args) {
? ? ? ? SpringApplication.run(OssApplication.class,args);
? ? }
}
6、啟動(dòng)項(xiàng)目
報(bào)錯(cuò):

spring boot 會(huì)默認(rèn)加載org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration這個(gè)類,
而DataSourceAutoConfiguration類使用了@Configuration注解向spring注入了dataSource bean,又因?yàn)轫?xiàng)目(oss模塊)中并沒(méi)有關(guān)于dataSource相關(guān)的配置信息,所以當(dāng)spring創(chuàng)建dataSource bean時(shí)因缺少相關(guān)的信息就會(huì)報(bào)錯(cuò)。

@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)
即可成功:

二、實(shí)現(xiàn)文件上傳
1、從配置文件讀取常量
創(chuàng)建常量讀取工具類:ConstantPropertiesUtil.java
使用@Value讀取application.properties里的配置內(nèi)容
用spring的 InitializingBean 的 afterPropertiesSet 來(lái)初始化配置信息,這個(gè)方法將在所有的屬性被初始化后調(diào)用。
//當(dāng)項(xiàng)目已啟動(dòng),spring接口,spring加載之后,執(zhí)行接口一個(gè)方法
@Component
public class ConstantPropertiesUtil implements InitializingBean {
????//讀取配置文件內(nèi)容
? ? @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;
????//定義公開(kāi)靜態(tài)常量
? ? 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;
????}
}
2、文件上傳
創(chuàng)建Service接口:uploadFileAvatar.java
public interface OssService {
????String uploadFileAvatar(MultipartFile file);
}
實(shí)現(xiàn):OssServiceImpl.java
參考SDK中的:Java->上傳文件->簡(jiǎn)單上傳->流式上傳->上傳文件流
@Service
public class OssServiceImpl implements OssService {
????//上傳頭像到oss
? ? @Override
? ? public String uploadFileAvatar(MultipartFile file) {
????????// Endpoint以杭州為例,其它Region請(qǐng)按實(shí)際情況填寫。
? ? ? ? String endpoint =ConstantPropertiesUtil.END_POINT;
????????String accessKeyId =ConstantPropertiesUtil.ACCESS_KEY_ID;
????????String accessKeySecret =ConstantPropertiesUtil.ACCESS_KEY_SECRET;
????????String bucketName =ConstantPropertiesUtil.BUCKET_NAME;
????????try {
????????????// 創(chuàng)建OSSClient實(shí)例。
? ? ? ? ? ? OSS ossClient =new OSSClientBuilder().build(endpoint,accessKeyId,accessKeySecret);
????????????// 上傳文件流。
? ? ? ? ? ? InputStream inputStream = file.getInputStream();
????????????//獲取文件名稱
? ? ? ? ? ? String fileName = file.getOriginalFilename();
????????????//調(diào)用oss方法實(shí)現(xiàn)上傳
? ? ? ? ? ? //第一個(gè)參數(shù) Bucket名稱
? ? ? ? ? ? //第二個(gè)參數(shù) 上傳到oss文件路徑和文件名稱 如/aa/bb/1.jpg
? ? ? ? ? ? //第三個(gè)參數(shù) 上傳文件輸入流
? ? ? ? ? ? ossClient.putObject(bucketName,fileName,inputStream);
????????????// 關(guān)閉OSSClient。
? ? ? ? ? ? ossClient.shutdown();
?????????? //把上傳之后文件的路徑返回
? ? ? ? ? ? //需要把上傳到阿里云oss路徑手動(dòng)拼接出來(lái)
? ? ? ? ? ? //https://guli-file-1010.oss-cn-beijing.aliyuncs.com/11.jpg
? ? ? ? ? ? String url ="https://"+bucketName+"."+endpoint+"/"+fileName;
????????????return url;
????????}catch (IOException e) {
????????????e.printStackTrace();
????????????return null;
????????}
????}
}
3、控制層
創(chuàng)建controller:FileUploadController.java
@Api(description="阿里云文件管理")
@RestController
@RequestMapping("/eduoss/fileoss")
@CrossOrigin
public class OssController {
????@Autowired
? ? private OssService ossService;
????//上傳頭像的方法
? ? @ApiOperation(value ="文件上傳")
????@PostMapping
? ? public R uploadOssFile(@ApiParam(name ="file", value ="文件", required =true)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? @RequestParam("file")MultipartFile file){
????????//獲取上傳文件
? ? ? ? //返回上傳到oss的路徑
? ? ? ? String url =ossService.uploadFileAvatar(file);
????????return R.ok().data("url",url);
????}
}
4、重啟oss服務(wù)
5、Swagger中測(cè)試文件上傳

解決上傳文件覆蓋問(wèn)題:

//1 在文件名稱里面添加隨機(jī)唯一的值
String uuid =UUID.randomUUID().toString().replaceAll("-","");
//如qwadsfadeasd01.jpg
fileName =uuid+fileName;
//2 把文件按照日期進(jìn)行分類
//2020/11/12/01.jpg
//獲取當(dāng)前日期
String datePath =new DateTime().toString("yyyy/MM/dd");
//拼接
//如2020/11/12/qwadsfadeasd01.jpg
fileName =datePath +"/" + fileName;
測(cè)試:


6、配置nginx反向代理

配置nginx實(shí)現(xiàn)請(qǐng)求轉(zhuǎn)發(fā)的功能:



驗(yàn)證:

03-前端整合上傳組件
一、前端整合圖片上傳組件
1、復(fù)制頭像上傳組件
從vue-element-admin復(fù)制組件:
vue-element-admin/src/components/ImageCropper
vue-element-admin/src/components/PanThumb

2、前端參考實(shí)現(xiàn)
src/views/components-demo/avatarUpload.vue
3、前端添加文件上傳組件
src/views/edu/teacher/save.vue
template:
引入組件模塊:
import?ImageCropper?from?'@/components/ImageCropper'
import?PanThumb?from?'@/components/PanThumb'
4、設(shè)置默認(rèn)頭像(也可不設(shè)置)
onfig/dev.env.js中添加阿里云oss bucket地址
OSS_PATH:'"https://guli-file.oss-cn-beijing.aliyuncs.com"'
組件中初始化頭像默認(rèn)地址
const defaultForm = {
? ......,
? avatar: process.env.OSS_PATH + '/avatar/default.jpg'
}
5、js腳本實(shí)現(xiàn)上傳和圖片回顯
export?default?{
????components:?{?ImageCropper,?PanThumb?},
????data(){
????????return?{
????????????teacher:{
.........
????????????},
????????????//上傳彈框組件是否顯示
????????????imagecropperShow:false,
????????????imagecropperKey:0,//上傳組件key值
????????????BASE_API:?process.env.BASE_API,?//?接口API地址
????????????saveBtnDisabled:?false?//?保存按鈕是否禁用,
????????}
????},
?? ......,
????methods: {
????????//其他函數(shù)
????????......,
??????? close(){//關(guān)閉上傳彈框方法
????????????this.imagecropperShow?=?false;
????????????//?上傳失敗后,重新打開(kāi)上傳組件時(shí)初始化組件,否則顯示上一次的上傳結(jié)果
????????????this.imagecropperKey?=?this.imagecropperKey?+?1
????????},
????????cropSuccess(data){//上傳成功方法
????????????this.imagecropperShow?=?false;
????????????//上傳之后接口返回圖片地址
????????????this.teacher.avatar?=?data.url
????????????//?上傳成功后,重新打開(kāi)上傳組件時(shí)初始化組件,否則顯示上一次的上傳結(jié)果
????????????this.imagecropperKey?=?this.imagecropperKey?+?1
????????},
二、測(cè)試文件上傳
前后端聯(lián)調(diào)
2 EasyExcel導(dǎo)入課程分類
01-EasyExcel讀寫Excel的基本使用
一、Excel導(dǎo)入導(dǎo)出的應(yīng)用場(chǎng)景
1、數(shù)據(jù)導(dǎo)入:減輕錄入工作量
2、數(shù)據(jù)導(dǎo)出:統(tǒng)計(jì)信息歸檔
3、數(shù)據(jù)傳輸:異構(gòu)系統(tǒng)之間數(shù)據(jù)傳輸
二、EasyExcel簡(jiǎn)介
1、EasyExcel特點(diǎn)
????Java領(lǐng)域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個(gè)嚴(yán)重的問(wèn)題就是非常的耗內(nèi)存。如果你的系統(tǒng)并發(fā)量不大的話可能還行,但是一旦并發(fā)上來(lái)后一定會(huì)OOM或者JVM頻繁的full gc。
????EasyExcel是阿里巴巴開(kāi)源的一個(gè)excel處理框架,以使用簡(jiǎn)單、節(jié)省內(nèi)存著稱。EasyExcel能大大減少占用內(nèi)存的主要原因是在解析Excel時(shí)沒(méi)有將文件數(shù)據(jù)一次性全部加載到內(nèi)存中,而是從磁盤上一行行讀取數(shù)據(jù),逐個(gè)解析。
????EasyExcel采用一行一行的解析模式,并將一行的解析結(jié)果以觀察者的模式通知處理(AnalysisEventListener)。
02-Excel寫
一、創(chuàng)建項(xiàng)目,實(shí)現(xiàn)EasyExcel對(duì)Excel寫操作
1、創(chuàng)建一個(gè)普通的maven項(xiàng)目
項(xiàng)目名:excel-easydemo
2、pom中引入xml相關(guān)依賴
<dependencies>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>com.alibaba</groupId>
? ? ? ? ? ? <artifactId>easyexcel</artifactId>
? ? ? ? ? ? <version>2.1.1</version>
? ? ? ? </dependency>
? ? </dependencies>
3、創(chuàng)建實(shí)體類
設(shè)置表頭和添加的數(shù)據(jù)字段
@Data
public class DemoData {
????//設(shè)置excel表頭名稱
? ? @ExcelProperty("學(xué)生編號(hào)")
????private Integer sno;
????@ExcelProperty("學(xué)生姓名")
????private String sname;
}
4 、實(shí)現(xiàn)寫操作
TestEasyExcel.java
(1)創(chuàng)建方法循環(huán)設(shè)置要添加到Excel的數(shù)據(jù)
//循環(huán)設(shè)置要添加的數(shù)據(jù),最終封裝到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;
? ? }
(2)實(shí)現(xiàn)最終的添加操作(寫法一)
public static void main(String[] args) {
? ? ? ? //實(shí)現(xiàn)excel寫的操作
? ? ? ? //1 設(shè)置寫入文件夾地址和excel文件名稱
? ? ? ? String filename = "F:\\write.xlsx";
? ? ? ? //2 調(diào)用easyexcel里面的方法實(shí)現(xiàn)寫操作
? ? ? ? //write方法兩個(gè)參數(shù):第一個(gè)參數(shù)文件路徑名稱,第二個(gè)參數(shù)實(shí)體類class
? ? ? ? EasyExcel.write(filename,DemoData.class).sheet("學(xué)生列表").doWrite(getData());
? ? }


(3)實(shí)現(xiàn)最終的添加操作(寫法二)
public static void main(String[] args) throws Exception {
????????// 寫法2,方法二需要手動(dòng)關(guān)閉流
????????//實(shí)現(xiàn)excel寫的操作
? ? ? ? //1 設(shè)置寫入文件夾地址和excel文件名稱
? ? ? ? String filename = "F:\\write.xlsx";
? ? ? ??ExcelWriter excelWriter=EasyExcel.write(fileName,DemoData.class).build();
????????WriteSheet writeSheet=EasyExcel.writerSheet("寫入方法二").build();
????????excelWriter.write(data(),writeSheet);
????????/// 千萬(wàn)別忘記finish 會(huì)幫忙關(guān)閉流
????????excelWriter.finish();
? ? }
03-Excel讀
一、實(shí)現(xiàn)EasyExcel對(duì)Excel讀操作
1、創(chuàng)建實(shí)體類
@Data
public class DemoData {
? ? //設(shè)置excel表頭名稱
? ? @ExcelProperty(value = "學(xué)生編號(hào)",index = 0)
? ? private Integer sno;
? ? @ExcelProperty(value = "學(xué)生姓名",index = 1)
? ? private String sname;
}
2、創(chuàng)建讀取操作的監(jiān)聽(tīng)器
public class ExcelListener extends AnalysisEventListener<DemoData> {
? ? //一行一行讀取excel內(nèi)容
? ? @Override
? ? public void invoke(DemoData demoData, AnalysisContext analysisContext) {
? ? ? ? System.out.println("******"+demoData);
? ? }
? ? //讀取表頭內(nèi)容
? ? @Override
? ? public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
? ? ? ? System.out.println("表頭:"+headMap);
? ? }
? ? //讀取完成之后
? ? @Override
? ? public void doAfterAllAnalysed(AnalysisContext analysisContext) {
? ? }
}
3、調(diào)用實(shí)現(xiàn)最終的讀取
public class TestEasyExcel {
? ? public static void main(String[] args) {
? ? ? ? //實(shí)現(xiàn)excel讀操作
????????// 寫法1:
? ? ? ? String filename = "F:\\write.xlsx";
????????// 這里 需要指定讀用哪個(gè)class去讀,然后讀取第一個(gè)sheet 文件流會(huì)自動(dòng)關(guān)閉
? ? ? ? EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead();
????????// 寫法2:
????????InputStream in = new BufferedInputStream(new FileInputStream("F:\\01.xlsx"));
? ? ? ? ExcelReader excelReader = EasyExcel.read(in, DemoData.class, new ExcelListener()).build();
? ? ? ? ReadSheet readSheet = EasyExcel.readSheet(0).build();
? ? ? ? excelReader.read(readSheet);
? ? ? ? // 這里千萬(wàn)別忘記關(guān)閉,讀的時(shí)候會(huì)創(chuàng)建臨時(shí)文件,到時(shí)磁盤會(huì)崩的
? ? ? ? excelReader.finish();
? ? }

04-前端頁(yè)面的實(shí)現(xiàn)
一、Excel模板(本案例中使用靜態(tài)模板)
1、編輯Excel模板
2、將文件上傳至阿里云OSS

二、配置路由
1、添加路由
{
????path:?'/subject',
????component:?Layout,
????redirect:?'/subject/table',
????name:?'課程分類管理',
????meta:?{?title:?'課程分類管理',?icon:?'example'?},
????children:?[
??????{
????????path:?'table',
????????name:?'課程分類列表',
????????component:?()?=>?import('@/views/edu/subject/list'),
????????meta:?{?title:?'課程分類列表',?icon:?'table'?}
??????},
??????{
????????path:?'tree',
????????name:?'添加課程分類',
????????component:?()?=>?import('@/views/edu/subject/save'),
????????meta:?{?title:?'添加課程分類',?icon:?'tree'?}
??????}
????]
??},
2、添加vue組件

三、表單組件save.vue
1、js定義數(shù)據(jù)
<script>
export?default?{
????data(){
????????return?{
????????????BASE_API:?process.env.BASE_API,?//?接口API地址
????????????importBtnDisabled:?false,?//?按鈕是否禁用,
????????????loading:?false
????????}
????},
2、template
<template>
??<div?class="app-container">
????<el-form?label-width="120px">
??????<el-form-item?label="信息描述">
????????<el-tag?type="info">excel模版說(shuō)明</el-tag>
????????<el-tag>
??????????<i?class="el-icon-download"/>
??????????<a?:href="'/static/01.xlsx'">點(diǎn)擊下載模版</a>
????????</el-tag>
??????</el-form-item>
??????<el-form-item?label="選擇Excel">
????????<el-upload
??????????ref="upload" ?? //相當(dāng)于id='upload'
??????????:auto-upload="false"
??????????:on-success="fileUploadSuccess"
??????????:on-error="fileUploadError"
??????????:disabled="importBtnDisabled"
??????????:limit="1"? //限制每次只能上傳一個(gè)文件
??????????:action="BASE_API+'/eduservice/edu-subject/addSubject'"
??????????name="file"
??????????accept="application/vnd.ms-excel">
??????????<el-button?slot="trigger"?size="small"?type="primary">選取文件</el-button>
??????????<el-button
????????????:loading="loading"
????????????style="margin-left:?10px;"
????????????size="small"
????????????type="success"
????????????@click="submitUpload">上傳到服務(wù)器</el-button>
????????</el-upload>
??????</el-form-item>
????</el-form>
??</div>
</template>
3、js上傳方法
methods:{
????????//點(diǎn)擊按鈕上傳文件到接口里面
????????submitUpload(){
????????????this.importBtnDisabled?=?true
????????????this.loading?=?true
????????????//js:document.getElementById("upload").submit()
????????????this.$refs.upload.submit()
????????},
????????........
????}
4、回調(diào)函數(shù)
????????//上傳成功
????????fileUploadSuccess(){
????????????//提示信息
????????????this.loading?=?false
????????????this.$message({
????????????????type:?'success',
????????????????message:'添加課程分類成功'
????????????})
????????????//跳轉(zhuǎn)課程分類列表
????????},
????????//上傳失敗
????????fileUploadError(){
????????????this.loading?=?false
????????????this.$message({
????????????????type:'error',
????????????????message:'添加課程失敗'
????????????})
????????}

05-課程分類管理接口
一、添加依賴
1、service-edu模塊配置依賴
<dependencies>
? ? ? ? <dependency>
? ? ? ? ? ? <groupId>com.alibaba</groupId>
? ? ? ? ? ? <artifactId>easyexcel</artifactId>
? ? ? ? ? ? <version>2.1.1</version>
? ? ? ? </dependency>
? ? </dependencies>
二、業(yè)務(wù)處理
1、EduSubjectController
@Api(description="課程分類管理")
@RestController
@RequestMapping("/eduservice/edu-subject")
@CrossOrigin //跨域
public class EduSubjectController {
? ? @Autowired
? ? private EduSubjectService subjectService;
? ? //添加課程分類
? ? //獲取上傳過(guò)來(lái)的文件把文件內(nèi)容讀取出來(lái)
? ? @PostMapping("addSubject")
? ? public R addSubject(MultipartFile file){
? ? ? ? //上傳過(guò)來(lái)的excel文件
? ? ? ? subjectService.saveSubject(file,subjectService);
? ? ? ? return R.ok();
? ? }
}
2、創(chuàng)建和Excel對(duì)應(yīng)的實(shí)體類
@Data
public class SubjectData {
? ? @ExcelProperty(index = 0)
? ? private String oneSubjectName;
? ? @ExcelProperty(index = 1)
? ? private String twoSubjectName;
}
3、EduSubjectService
(1)接口
public interface EduSubjectService extends IService<EduSubject> {
? ? //添加課程分類
? ? void saveSubject(MultipartFile file,EduSubjectService subjectService);
}
(2)實(shí)現(xiàn)類
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
? ? //添加課程分類
? ? @Override
? ? public void saveSubject(MultipartFile file,EduSubjectService subjectService) {
? ? ? ? try {
? ? ? ? ? ? //文件輸入流
? ? ? ? ? ? InputStream in = file.getInputStream();
? ? ? ? ? ? //調(diào)用方法進(jìn)行讀取
? ? ? ? ? ? EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(subjectService)).sheet().doRead();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}
4、創(chuàng)建讀取Excel監(jiān)聽(tīng)器
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
? ? //因?yàn)镾ubjectExcelListener不能交給spring進(jìn)行管理,需要自己new,不能注入其他對(duì)象
? ? //不能實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作
? ? public EduSubjectService subjectService;
? ? public SubjectExcelListener() {}
? ? public SubjectExcelListener(EduSubjectService subjectService) {
? ? ? ? this.subjectService = subjectService;
? ? }
? ? //讀取excel內(nèi)容,一行一行進(jìn)行讀取
? ? @Override
? ? public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
? ? ? ? if (subjectData == null){
? ? ? ? ? ? throw new GuliException(20001,"文件數(shù)據(jù)為空");
? ? ? ? }
? ? ? ? //一行一行讀取,每次讀取有兩個(gè)值,第一個(gè)值一級(jí)分類,第二個(gè)值二級(jí)分類
? ? ? ? //判斷一級(jí)分類是否重復(fù)
? ? ? ? EduSubject exitOneSubject = this.exitOneSubject(subjectService, subjectData.getOneSubjectName());
? ? ? ? if (exitOneSubject == null){//沒(méi)有相同一級(jí)分類,進(jìn)行添加
? ? ? ? ? ? exitOneSubject = new EduSubject();
? ? ? ? ? ? exitOneSubject.setParentId("0");
? ? ? ? ? ? exitOneSubject.setTitle(subjectData.getOneSubjectName()); //一級(jí)分類名稱
? ? ? ? ? ? subjectService.save(exitOneSubject);
? ? ? ? }
? ? ? ? String pid = exitOneSubject.getId();
? ? ? ? //添加二級(jí)分類
? ? ? ? //判斷二級(jí)分類是否重復(fù)
? ? ? ? EduSubject exitTwoSubject = this.exitTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);
? ? ? ? if (exitTwoSubject == null){
? ? ? ? ? ? exitTwoSubject = new EduSubject();
? ? ? ? ? ? exitTwoSubject.setParentId(pid);
? ? ? ? ? ? exitTwoSubject.setTitle(subjectData.getTwoSubjectName()); //一級(jí)分類名稱
? ? ? ? ? ? subjectService.save(exitTwoSubject);
? ? ? ? }
? ? }
? ? //判斷一級(jí)分類不能重復(fù)添加
? ? private EduSubject exitOneSubject(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;
? ? }
? ? //判斷二級(jí)分類不能重復(fù)添加
? ? private EduSubject exitTwoSubject(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) {
? ? }
}


06-分類列表展示
一、前端實(shí)現(xiàn)
1、參考 views/tree/index.vue
2、創(chuàng)建api
api/edu/subject.js
import?request?from?'@/utils/request'
export?default{
????//1?課程分類
????getSubjectList(){
????????return?request({
????????????url:`/eduservice/edu-subject/getAllSubject`,
????????????method:'get'
????????})
????}
}
3、list.vue
<template>
??<div?class="app-container">
????<el-input?v-model="filterText"?placeholder="Filter?keyword"?style="margin-bottom:30px;"?/>
????<el-tree
??????ref="tree2"
??????:data="data2"
??????:props="defaultProps"
??????:filter-node-method="filterNode"
??????class="filter-tree"
??????default-expand-all
????/>
??</div>
</template>
<script>
import?subject?from?'@/api/edu/subject'
export?default?{
??data()?{
????return?{
??????filterText:?'',
??????data2:?[],?//返回所有分類數(shù)據(jù)
??????defaultProps:?{
????????children:?'children',
????????label:?'title'
??????}
????}
??},
??created(){
????this.getAllSubjectList()
??},
??watch:?{
????filterText(val)?{
??????this.$refs.tree2.filter(val)
????}
??},
??methods:?{
????getAllSubjectList(){
??????subject.getSubjectList()
????????.then(response=>{
????????????this.data2?=?response.data.list
????????})
????},
????filterNode(value,?data)?{
??????if?(!value)?return?true
??????return?data.title.toLowerCase().indexOf(value.toLowerCase())?!==?-1? //優(yōu)化前端過(guò)濾功能
????}
??}
}
</script>
二、后端實(shí)現(xiàn)
1、創(chuàng)建vo
//一級(jí)分類
@Data
public class OneSubject {
????private String id;
????private String title;
????//一個(gè)一級(jí)分類含有多個(gè)二級(jí)分類
? ? private Listchildren =new ArrayList<>();
}
//二級(jí)分類
@Data
public class TwoSubject {
????private String id;
????private String title;
}
2、創(chuàng)建controller
????//課程分類列表(樹形)
????@ApiOperation(value = "嵌套數(shù)據(jù)列表")? ? @GetMapping("getAllSubject")
? ? public R getAllSubject(){
? ? ? ? //list集合泛型是一級(jí)分類
? ? ? ? List<OneSubject> list = subjectService.getAllOneTwoSubject();
? ? ? ? return R.ok().data("list",list);
? ? }
3、創(chuàng)建service
接口
????//課程分類列表(樹形)
? ? List<OneSubject> getAllOneTwoSubject();
實(shí)現(xiàn)Final
????@Override
? ? public List<OneSubject> getAllOneTwoSubject() {
? ? ? ? //1 獲取一級(jí)分類 parent_id=0
? ? ? ? QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();
? ? ? ? wrapperOne.eq("parent_id","0");
? ? ? ? List<EduSubject> oneSubjectList = baseMapper.selectList(wrapperOne);
? ? ? ? //2 獲取二級(jí)分類
? ? ? ? QueryWrapper<EduSubject> wrapperTwo = new QueryWrapper<>();
? ? ? ? wrapperTwo.ne("parent_id","0");
? ? ? ? List<EduSubject> twoSubjectList = baseMapper.selectList(wrapperTwo);
? ? ? ? //創(chuàng)建list集合用于存儲(chǔ)最終封裝數(shù)據(jù)
? ? ? ? List<OneSubject> finalSubjectList = new ArrayList<>();
? ? ? ? //3 封裝一級(jí)分類
? ? ? ? //查詢出來(lái)的所有一級(jí)分類list集合遍歷,得到每一個(gè)分類對(duì)象,獲取每一個(gè)一級(jí)分類對(duì)象值
? ? ? ? //封裝到要求的list集合里面 List<OneSubject> finalSubjectList
? ? ? ? for (int i = 0; i < oneSubjectList.size(); i++) {
? ? ? ? ? ? //得到oneSubjectList每個(gè)eduSubject對(duì)象
? ? ? ? ? ? EduSubject eduSubject = oneSubjectList.get(i);
? ? ? ? ? ? //把eduSubject里面獲取出來(lái)的值,放到oneSubject對(duì)象里面
? ? ? ? ? ? OneSubject oneSubject = new OneSubject();
//? ? ? ? ? ? oneSubject.setId(eduSubject.getId());
//? ? ? ? ? ? oneSubject.setTitle(eduSubject.getTitle());
? ? ? ? ? ? BeanUtils.copyProperties(eduSubject,oneSubject);
? ? ? ? ? ? //多個(gè)oneSubject放到finalSubjectList里面
? ? ? ? ? ? finalSubjectList.add(oneSubject);
? ? ? ? ? ? //4 封裝二級(jí)分類
? ? ? ? ? ? //在一級(jí)分類循環(huán)遍歷查詢所有二級(jí)分類
? ? ? ? ? ? //創(chuàng)建list集合封裝每一個(gè)一級(jí)分類的二級(jí)分類
? ? ? ? ? ? List<TwoSubject> twoFinalSubjectList = new ArrayList<>();
? ? ? ? ? ? for (int m = 0; m < twoSubjectList.size(); m++) {
? ? ? ? ? ? ? ? //獲取每個(gè)二級(jí)分類
? ? ? ? ? ? ? ? EduSubject tSubject = twoSubjectList.get(m);
? ? ? ? ? ? ? ? //判斷二級(jí)分類parentid和一級(jí)分類id是否一樣
? ? ? ? ? ? ? ? if (tSubject.getParentId().equals(eduSubject.getId())){
? ? ? ? ? ? ? ? ? ? //把tSubject值復(fù)制到twoSubject里面,放到twoFinalSubjectList里面
? ? ? ? ? ? ? ? ? ? TwoSubject twoSubject = new TwoSubject();
? ? ? ? ? ? ? ? ? ? BeanUtils.copyProperties(tSubject,twoSubject);
? ? ? ? ? ? ? ? ? ? twoFinalSubjectList.add(twoSubject);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? //把一級(jí)下面所有的二級(jí)分類放到一級(jí)分類里面
? ? ? ? ? ? oneSubject.setChildren(twoFinalSubjectList);
? ? ? ? }
? ? ? ? return finalSubjectList;
? ? }
