在线教育业务笔记02

在线教育业务笔记02- 课程分类管理模块

一、EasyExcel基本使用

1、EasyExcel写操作

在这里插入图片描述

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 = "F:\\write.xlsx";
        // 2 调用easyexcel里面的方法实现写操作
        // write方法两个参数:第一个参数文件路径名称,第二个参数实体类class
        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;
    }
}

2、EasyExcel读操作

在这里插入图片描述

2.1、创建读取操作的监听器
package com.atguigu.demo.excel;

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 data, AnalysisContext analysisContext) {
        System.out.println("****"+data);
    }
    // 读取表头内容
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表头:"+headMap);
    }
    // 读取完成之后
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) { }
}

2.2、调用实现最终读取
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) throws Exception {

        // 写法1:
        String filename = "F:\\write.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(filename, DemoData.class, new ExcelListener()).sheet().doRead();
       
        // 写法2:
        InputStream in = new BufferedInputStream(new FileInputStream("F:\\01.xlsx"));
        ExcelReader excelReader = EasyExcel.read(in, ReadData.class, new ExcelListener()).build();
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        excelReader.finish();
    }
}

二、课程分类管理添加课程和课程分类树形显示后端业务

1、添加依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>service</artifactId>
        <groupId>com.atguigu</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>service_edu</artifactId>


    <dependencies>
        <!--电子表格上传-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>
</project>

2、api接口

2.1、Controller

在这里插入图片描述

package com.atguigu.eduservice.controller;


import com.atguigu.commonutils.R;
import com.atguigu.eduservice.entity.subject.OneSubject;
import com.atguigu.eduservice.service.EduSubjectService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import org.springframework.web.multipart.MultipartFile;

import java.util.List;

/**
 * 课程科目 前端控制器
 * @author Tzc
 */
@Api(description="课程分类管理")
@CrossOrigin //跨域
@RestController
@RequestMapping("/eduservice/subject")
public class EduSubjectController {

    @Autowired
    private EduSubjectService subjectService;


    /**
     * //添加课程分类 使用导入Excel表格的方式
     * //获取上传过来文件,把文件内容读取出来
     * @param file
     * @return
     */
    @ApiOperation(value = "Excel批量导入")
    @PostMapping("addSubject")
    public R addSubject(MultipartFile file) {

        //1 获取上传的excel文件 MultipartFile
        // 上传过来excel文件
        subjectService.saveSubject(file, subjectService);

        //判断返回集合是否为空
        return R.ok();
    }


    /**
     * 查询所有课程分类列表以树形显示(树形)
     * @return
     */
    @GetMapping("getAllSubject")
    public R getAllSubject() {

        // list集合泛型是一级分类 因为一级分类中包含二级分类的集合
        List<OneSubject> list = subjectService.getAllOneTwoSubject();
        return R.ok().data("list",list);
    }

}


2.2、创建和Excel对应的实体类(添加课程分类使用)

上传Excel模板阿里云下载地址
https://cor2022314.oss-cn-beijing.aliyuncs.com/mysql.xlsx

在这里插入图片描述

package com.atguigu.eduservice.entity.excel;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;


/**
 * 对应上传的Excel表格格式模板 用于数据保存
 * @author Tzc
 */
@Data
public class SubjectData {

    // 一级分类名称 index = 0 代表表格第一列
    @ExcelProperty(index = 0)
    private String oneSubjectName;

    // 二级分类名称 index = 1 代表表格第二列
    @ExcelProperty(index = 1)
    private String twoSubjectName;
}

2.3、创建用于封装课程分类的vo对象(页面显示树形结构使用)在这里插入图片描述
2.3.1、第一种实现实体类
package com.atguigu.eduservice.entity.subject;

        import io.swagger.annotations.ApiModelProperty;
        import lombok.Data;

        import java.util.ArrayList;
        import java.util.List;

/**
 * @author Tzc
 * 课程列表一级分类 和一级分类的集合 用于数据前端显示
 */
@Data
public class OneSubject {

    // 主键
    private String id;

    // 节点名称
    private String title;

    // 存储子节点的集合,初始化是为了避免空指针异常
    private List<TwoSubject> children = new ArrayList<>();

}

package com.atguigu.eduservice.entity.subject;

import lombok.Data;




/**
 * @author Tzc
 * 课程列表二级分类
 */
@Data
public class TwoSubject {
    private String id;
    private String title;
}

2.3.2、第二种实现实体类
package com.atguigu.eduservice.entity.subject;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Tzc
 * 课程列表一级分类 和一级分类的集合 用于数据前端显示
 */
@Data
public class OneSubject {

     // 主键
    private String id;

    // 父节点的 id
    private String parentId;

    // 节点名称
    private String title;

    // 存储子节点的集合,初始化是为了避免空指针异常
    private List<OneSubject> children = new ArrayList<>();
    
}

3、Service

3.1、接口

在这里插入图片描述

package com.atguigu.eduservice.service;

import com.atguigu.eduservice.entity.EduSubject;
import com.atguigu.eduservice.entity.subject.OneSubject;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

/**
 * 课程科目 服务类
 * @author Tzc
 */
public interface EduSubjectService extends IService<EduSubject> {


    /**
     * 添加课程分类
     * @param file 本地文件处理加载类
     * @param subjectService 本类提供调用方法
     */
    void saveSubject(MultipartFile file, EduSubjectService subjectService);


    /**
     * 查询所有课程分类列表以树形显示(树形)
     * @return
     */
    List<OneSubject> getAllOneTwoSubject();
}

3.2、实现类

在这里插入图片描述

package com.atguigu.eduservice.service.impl;

import com.alibaba.excel.EasyExcel;
import com.atguigu.eduservice.entity.EduSubject;
import com.atguigu.eduservice.entity.excel.SubjectData;
import com.atguigu.eduservice.entity.subject.OneSubject;
import com.atguigu.eduservice.entity.subject.TwoSubject;
import com.atguigu.eduservice.listener.SubjectExcelListener;
import com.atguigu.eduservice.mapper.EduSubjectMapper;
import com.atguigu.eduservice.service.EduSubjectService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 课程科目 服务实现类
 * @author Tzc
 */
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {


    /**
     * 添加课程分类
     * read读取excel内容
     *
     * @param file
     * @param subjectService
     */
    @Override
    public void saveSubject(MultipartFile file, EduSubjectService subjectService) {

        try {
            // 文件输入流
            InputStream in = file.getInputStream();

            // 调用方法进行读取
            EasyExcel.read(in, SubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 查询所有课程分类列表以树形显示(树形)
     * @return
     */
    @Override
    public List<OneSubject> getAllOneTwoSubject() {

/*       // 1 查询所有一级分类  parentid = 0
        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<EduSubject> eduSubjects = baseMapper.selectList(null);


        // 创建list集合,用于存储最终封装数据
        List<OneSubject> finalSubjectList = new ArrayList<>();

        // 3 封装一级分类
        // 查询出来所有的一级分类list集合遍历,得到每个一级分类对象,获取每个一级分类对象值,
        // 封装到要求的list集合里面 List<OneSubject> finalSubjectList
        // 遍历oneSubjectList集合
        for (int i = 0; i < oneSubjectList.size(); i++) {
            // 得到oneSubjectList每个eduSubject对象
            EduSubject eduSubject = oneSubjectList.get(i);
            // 把eduSubject里面值获取出来,放到OneSubject对象里面
            OneSubject oneSubject = new OneSubject();
            // oneSubject.setId(eduSubject.getId());
            // oneSubject.setTitle(eduSubject.getTitle());
            // eduSubject值复制到对应oneSubject对象里面
            BeanUtils.copyProperties(eduSubject, oneSubject);
            //多个OneSubject放到finalSubjectList里面
            finalSubjectList.add(oneSubject);

            // 在一级分类循环遍历查询所有的二级分类
            // 创建list集合封装每个一级分类的二级分类
            List<TwoSubject> twoFinalSubjectList = new ArrayList<>();
            // 遍历二级分类list集合
            for (int m = 0; m < twoSubjectList.size(); m++) {
                // 获取每个二级分类
                EduSubject tSubject = twoSubjectList.get(m);
                // 判断二级分类parentid和一级分类id是否一样
                if (tSubject.getParentId().equals(eduSubject.getId())) {
                    // 把tSubject值复制到TwoSubject里面,放到twoFinalSubjectList里面
                    TwoSubject twoSubject = new TwoSubject();
                    BeanUtils.copyProperties(tSubject, twoSubject);
                    twoFinalSubjectList.add(twoSubject);
                }
            }
            // 4.把一级下面所有二级分类放到一级分类里面
            oneSubject.setChildren(twoFinalSubjectList);
        }*/


        // 查询全部的 OneSubject 对象
        List<EduSubject> eduSubjects = baseMapper.selectList(null);
        ArrayList<OneSubject> oneSubjectsList = new ArrayList<>();
        for (EduSubject eduSubject : eduSubjects) {
            OneSubject oneSubject = new OneSubject();
            BeanUtils.copyProperties(eduSubject,oneSubject);
            oneSubjectsList.add(oneSubject);
        }
        System.out.println("============>>>>>>>"+oneSubjectsList.toString());


        // 2.声明一个变量用来存储找到的根节点
        // OneSubject root01=null;
        ArrayList<OneSubject> root = new ArrayList<>();


        // 3.创建 Map 对象用来存储 id 和 OneSubject 对象的对应关系便于查找父节点
        Map<String, OneSubject> subjectMap = new HashMap<>();

        // 4.遍历 oneSubjectsList 填充 subjectMap
        for (OneSubject oneSubject : oneSubjectsList) {
            String id = oneSubject.getId();

            //将id(键)和对应的对象(值)存入集合
            subjectMap.put(id, oneSubject);
        }

        // 5.再次遍历 oneSubjectsList 查找根节点、组装父子节点
        for (OneSubject oneSubject : oneSubjectsList) {
            // 6.获取当前 oneSubject 对象的 pid 属性值
            String parentId = oneSubject.getParentId();

            // 7.如果 parentId 为 0,判定为根节点
            if (Objects.equals(parentId, "0")) {
                //将根对象赋值个成员变量
                //root01 = oneSubject;
                root.add(oneSubject);
                // 8.如果当前节点是根节点,那么肯定没有父节点,结束本次循环
                continue;
            }

            // 9.如果pid不为 null,说明当前节点有父节点,那么可以根据pid到menuMap中查找对应的Menu对象
            OneSubject father = subjectMap.get(parentId);

            // 10.将当前节点存入父节点的 children 集合
             father.getChildren().add(oneSubject);
        }
        return root;
    }
}


4、创建监听器读取Excel

在这里插入图片描述

package com.atguigu.eduservice.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.atguigu.eduservice.entity.EduSubject;
import com.atguigu.eduservice.entity.excel.SubjectData;
import com.atguigu.eduservice.mapper.EduSubjectMapper;
import com.atguigu.eduservice.service.EduSubjectService;
import com.atguigu.servicebase.exceptionhandler.GuliException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * 业务 用于读取excel表格内容 并保存到数据库
 * @author Tzc
 */
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {

    // 因为SubjectExcelListener不交给spring进行管理,需要自己new,不能注入其他对象
    // 不能实现数据库操作
    public EduSubjectService subjectService;


    public SubjectExcelListener() {}

    // 创建有参数构造,传递subjectService用于操作数据库
    public SubjectExcelListener(EduSubjectService subjectService) {
        this.subjectService = subjectService;
    }


    /**
     * 读取excel表格内容,一行一行进行读取 并保存到数据库中
     * @param subjectData
     * @param analysisContext
     */
    @Override
    public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
        if(subjectData == null) {
            throw new GuliException(20001,"文件数据为空");
        }

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

        // 获取一级分类id值
        String pid = existOneSubject.getId();

        // 判断二级分类是否重复
        EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);
        // 添加二级分类
        if(existTwoSubject == null) {
            existTwoSubject = new EduSubject();
            existTwoSubject.setParentId(pid);
            // 二级分类名称
            existTwoSubject.setTitle(subjectData.getTwoSubjectName());
            subjectService.save(existTwoSubject);
        }
    }


    /**
     * 根据parent_id=0 可以判断是一级课程级分类 如果查询结果唯一返回查询的对象 用于于保存
     * @param subjectService
     * @param name 调用者传入的Excel表格一级分类名称
     * @return
     */
    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;
    }


    /**
     * 根据二级课程分类的parent_id=一级课程分类的id值 可以判断是二级课程分类 如果查询结果唯一返回查询的对象 用于余保存
     * @param subjectService
     * @param name 调用者传入的Excel表格二级分类名称
     * @param pid 一级分类id值
     * @return
     */
    private EduSubject existTwoSubject(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) {

    }
}

三、课程分类前端页面显示(目标)

1、课程分类页面显示过程分析(思路)

在这里插入图片描述

2、过程分析实现(代码)

2.1、添加路由进行页面跳转

在这里插入图片描述

// 课程分类管理模块
  {
    path: '/subject',
    component: Layout,
    redirect: '/subject/list',
    name: '课程分类管理',
    meta: { title: '课程分类管理', icon: 'example' },
    children: [
      {
        path: 'list',
        name: '课程分类列表',
        component: () => import('@/views/edu/subject/list'),
        meta: { title: '课程分类列表', icon: 'table' }
      },
      {
        path: 'save',
        name: '添加课程分类',  
        component: () => import('@/views/edu/subject/save'),
        meta: { title: '添加课程分类', icon: 'tree' }
      }
    ]
  }
2.2、创建api调用后端接口

在这里插入图片描述

import request from '@/utils/request'
export default {

    // 查询所有课程分类列表以树形显示(树形)
    getSubjectList() {
        return request({
            url: '/eduservice/subject/getAllSubject',
            method: 'get'
          })
    }
}

2.3、前端页面

在这里插入图片描述

<template>
  <div class="app-container">

    <!-- 搜索框 -->
    <el-input v-model="filterText" placeholder="Filter keyword" style="margin-bottom:30px;" />

    <!-- 课程分类列表以树形显示 ref="tree2" 相当于id-->
    <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: [],  //返回所有分类数据
      defaultProps: {
        children: 'children',
        label: 'title' // 分类名称
      }
    }
  },

  // 执行方法 页面加载之前
  created() {
      this.getAllSubjectList()
  },
  watch: {


    filterText(val) {
     // 代码中的this.$refs就是搜集所有的ref的一个对象。通过this.$refs 可以访问到此vue实例中的所有设置了ref属性的DOM元素,并对其进行操作
     // 类似于原生js的document.getElementById("#id")
      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
    }
  }
}
</script>

四、添加课程分类前端页面分析

在这里插入图片描述
在这里插入图片描述

<template>
  <div class="app-container">
    <el-form label-width="120px">
      <el-form-item label="信息描述">
        <el-tag type="info">excel模版说明</el-tag>
        <el-tag>
          <i class="el-icon-download"/>
          <a :href="'https://cor2022314.oss-cn-beijing.aliyuncs.com/mysql.xlsx'">点击下载模版</a>
        </el-tag>

      </el-form-item>

      <el-form-item label="选择Excel">
        <el-upload
          ref="upload"
          :auto-upload="false"
          :on-success="fileUploadSuccess"
          :on-error="fileUploadError"
          :disabled="importBtnDisabled"
          :limit="1"
          :action="BASE_API+'/eduservice/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">上传到服务器</el-button>
        </el-upload>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
export default {
    data() {
        return {
            BASE_API: process.env.BASE_API, // 接口API地址
            importBtnDisabled: false, // 按钮是否禁用,
            loading: false
        }
    },
    created() {

    },
    methods:{
        //点击按钮上传文件到接口里面
        submitUpload() {
            this.importBtnDisabled = true
            this.loading = true
            // js: document.getElementById("upload").submit()
            this.$refs.upload.submit()
        },
        //上传成功
        fileUploadSuccess(response) {
            //提示信息
            this.loading = false
            this.$message({
                type: 'success',
                message: '添加课程分类成功'
            })
            //跳转课程分类列表
            //路由跳转
            this.$router.push({path:'/subject/list'})
        },
        //上传失败
        fileUploadError() {
            this.loading = false
            this.$message({
                type: 'error',
                message: '添加课程分类失败'
            })
        }
    }
}
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值