欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

8.25重点内容

程序员文章站 2024-03-05 18:44:13
...

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.把系统登录功能改造本地
第一步:把地址改为本地
8.25重点内容
第二步:点登录调用的方法8.25重点内容
8.25重点内容
第三步:创建接口写前端需要返回的属性数据

@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
      }

8.25重点内容
8.25重点内容

8.25重点内容
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;
    }
}
相关标签: 重点知识