项目5:实现数据字典的上传下载

项目5:实现数据字典的上传下载

1.什么是数据字典?如何设计?

2.业务流程逻辑

3.数据库表的设计

4.实现上传下载逻辑(前端)

5.实现上传逻辑(后端)

6.实现下载依赖(后端)

项目5:实现数据字典的上传下载

1.什么是数据字典?如何设计?

①什么是数据字典?

数据字典是用来管理系统常用的分类数据和固定设计

②数据字典的设计?

  • id
  • parent_id
  • 即parent_id即为本标题的父标题,没有则为0

③数据字典中数据库对应后端pojo

  • 数据库中的数据字典(Dict对象)
    需要存储creat_time,update_time,is_deleted
  • 后端pojo
    对应具体的excel即传输对象,不需要上述三个字段的存储,故对象为(ExcelDictDTO)

2.业务流程逻辑

①上传excel表

  • 前端单击上传
  • 后端接收到excel表解析为ExcelDictDTO对象
  • 然后通过mapper保存到数据库中

②下载excel表

  • 前端单击下载
  • 后端接受到请求后通过mapper查询数据库并且封装为ExcelDictDTO对象
  • 然后通过http响应返回文件

③注意

  • ajax请求只能解析字符串
  • http请求可以解析字符串,解析文件
  • 故在上传时可以用ajax请求
  • 故在下载时只能用普通的请求

3.数据库表的设计

①数据库表

项目5:实现数据字典的上传下载_第1张图片

②数据库表对应的entity

package com.atguigu.srb.core.pojo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 

* 数据字典 *

* * @author Likejin * @since 2023-04-09 */
@Data @EqualsAndHashCode(callSuper = false) @ApiModel(value="Dict对象", description="数据字典") public class Dict implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "id") @TableId(value = "id", type = IdType.AUTO) private Long id; @ApiModelProperty(value = "上级id") private Long parentId; @ApiModelProperty(value = "名称") private String name; @ApiModelProperty(value = "值") private Integer value; @ApiModelProperty(value = "编码") private String dictCode; @ApiModelProperty(value = "创建时间") private LocalDateTime createTime; @ApiModelProperty(value = "更新时间") private LocalDateTime updateTime; @ApiModelProperty(value = "删除标记(0:不可用 1:可用)") @TableField("is_deleted") @TableLogic private Boolean deleted; }

③数据传输对象dto(与excel对应)

package com.atguigu.srb.core.pojo.dto;

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

@Data
public class ExcelDictDTO {

    @ExcelProperty("id")
    private Long id;

    @ExcelProperty("上级id")
    private Long parentId;

    @ExcelProperty("名称")
    private String name;

    @ExcelProperty("值")
    private Integer value;

    @ExcelProperty("编码")
    private String dictCode;
}

4.实现上传逻辑(前端)

①在路由中加入系统设置/数据字典路由

项目5:实现数据字典的上传下载_第2张图片
srb-admin\src\router\index.js

{
    path: '/core',
    component: Layout,
    redirect: '/core/dict/list',
    name: 'coreDict',
    meta: { title: '系统设置', icon: 'el-icon-setting' },
    alwaysShow: true,
    children: [
      {
        path: 'dict/list',
        name: '数据字典',
        component: () => import('@/views/core/dict/list'),
        meta: { title: '数据字典' },
      },
    ],
  },

②新建页面组件(配合路由渲染主内容区)

srb-admin\src\views\core\dict\list.vue

<template>
  <div class="app-container">
    <!--Excel导入按钮-->
    <div style="margin-bottom: 10px">
      <el-button
        @click="dialogVisible = true"
        type="primary"
        size="mini"
        icon="el-icon-download"
      >
        导入Excel
      </el-button>
      <el-button
        @click="exportData"
        type="primary"
        size="mini"
        icon="el-icon-upload2"
      >
        导出Excel
      </el-button>
    </div>

    <!--显示一个dialog对话框-->
    <el-dialog title="数据字典导入" :visible.sync="dialogVisible" width="30%">
      <el-form>
        <el-form-item label="请选择Excel文件">
          <!--accept为只接受xls,xlsx的文件后缀,
          name名字必须要和后端的名字保持一致,
          avtion为提交地址发起ajax远程调用(和自己写的request请求无关),自己处理成功失败-->
          <el-upload
            :auto-upload="true"
            :multiple="false"
            :limit="1"
            :on-exceed="fileUploadExceed"
            :on-success="fileUploadSuccess"
            :on-error="fileUploadError"
            :action="BASE_API + '/admin/core/dict/import'"
            name="file"
            accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
          >
            <el-button size="small" type="primary">点击上传</el-button>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dialogVisible: false, //对话框是否显式
      BASE_API: process.env.VUE_APP_BASE_API, //获取后端接口地址
    }
  },
  methods: {
    // 上传多于一个文件时
    fileUploadExceed() {
      this.$message.warning('只能选取一个文件')
    },

    //上传成功回调(http通信成功,有可能业务失败或业务失败)
    fileUploadSuccess(response) {
      if (response.code === 0) {
        //业务成功
        this.$message.success('数据导入成功')
        this.dialogVisible = false
      } else {
        //业务事变
        this.$message.error(response.message)
      }
    },

    //上传失败回调(http通信不成功)
    fileUploadError(error) {
      this.$message.error('数据导入失败')
    },

    //Excel数据导出
    exportData() {
      //导出excel,此时不能用ajax请求,无法响应文件
      window.location.href = this.BASE_API + '/admin/core/dict/export'
    },
  },
}
</script>

5.实现上传逻辑(后端)

①逻辑流程

  • 前端单击上传(发起ajax请求)
  • 后端controller接收到文件类型数据,调用service层保存
  • service层调用easyexcel接口读取excel数据(利用监听器)
  • 监听器调用mapper接口存储excel数据到数据库

②创建dto数据传输对象(封装excel表格数据)

package com.atguigu.srb.core.pojo.dto;

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

@Data
public class ExcelDictDTO {

    @ExcelProperty("id")
    private Long id;

    @ExcelProperty("上级id")
    private Long parentId;

    @ExcelProperty("名称")
    private String name;

    @ExcelProperty("值")
    private Integer value;

    @ExcelProperty("编码")
    private String dictCode;
}

③引入easyexcel依赖

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>easyexcelartifactId>
        dependency>
        
        <dependency>
            <groupId>org.apache.xmlbeansgroupId>
            <artifactId>xmlbeansartifactId>
        dependency>

④编写controller接口(接受文件调用sevice存储数据库)

package com.atguigu.srb.core.controller.admin;


import com.alibaba.excel.EasyExcel;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.common.result.R;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.service.DictService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

/**
 * 

* 数据字典 前端控制器 *

* * @author Likejin * @since 2023-04-09 */
@Api(tags = "数据字典管理") @RestController @RequestMapping("/admin/core/dict") @Slf4j @CrossOrigin public class AdminDictController { @Resource DictService dictService; @ApiOperation("Excel数据的批量导入(导入到数据库)") @PostMapping("/import") public R batchImport( @ApiParam(value="Excel数据字典文件") @RequestParam("file") MultipartFile file){ try { InputStream inputStream = file.getInputStream(); dictService.importData(inputStream); return R.ok().message("数据字典批量导入成功"); } catch (Exception e) { //返回R对象,并且打印异常跟踪栈 throw new BusinessException(ResponseEnum.UPLOAD_ERROR,e); } } @ApiOperation("Excel数据的导出") //访问页面 @GetMapping("/export") public void export(HttpServletResponse response){ try { // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman //设置类型为excel response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 String fileName = URLEncoder.encode("mydict", "UTF-8").replaceAll("\\+", "%20"); //设置附件的形式下载到浏览器端 response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); EasyExcel.write(response.getOutputStream(), ExcelDictDTO.class).sheet("数据字典").doWrite(dictService.listDictData()); } catch (IOException e) { //EXPORT_DATA_ERROR(104, "数据导出失败"), throw new BusinessException(ResponseEnum.EXPORT_DATA_ERROR, e); } } }

⑤编写service接口和serviceimpl

package com.atguigu.srb.core.service;

import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.pojo.entity.Dict;
import com.baomidou.mybatisplus.extension.service.IService;

import java.io.InputStream;
import java.util.List;

/**
 * 

* 数据字典 服务类 *

* * @author Likejin * @since 2023-04-09 */
public interface DictService extends IService<Dict> { void importData(InputStream inputStream); List<ExcelDictDTO> listDictData(); }
package com.atguigu.srb.core.service.impl;

import com.alibaba.excel.EasyExcel;
import com.atguigu.srb.core.listener.ExcelDictDTOListener;
import com.atguigu.srb.core.mapper.DictMapper;
import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.pojo.entity.Dict;
import com.atguigu.srb.core.service.DictService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 

* 数据字典 服务实现类 *

* * @author Likejin * @since 2023-04-09 */
@Slf4j @Service public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService { @Resource private DictMapper dictMapper; //读取数据 //写入数据库,利用事务的操作成功全成功,失败全失败(只要异常出现则回滚) @Transactional(rollbackFor = Exception.class) @Override public void importData(InputStream inputStream) { //传入输入流, EasyExcel.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener(dictMapper)).sheet().doRead(); log.info("Excel导入成功"); } @Override public List<ExcelDictDTO> listDictData() { List<Dict> dictList = dictMapper.selectList(null); //创建ExcelDictDTO列表,将Dict列表转化为ExcelDictDTO列表 ArrayList<ExcelDictDTO> excelDictDTOList = new ArrayList<>(dictList.size()); dictList.forEach(dict -> { ExcelDictDTO excelDictDTO = new ExcelDictDTO(); //对象拷贝,dict拷贝到excelDictDTO(相同列拷贝) BeanUtils.copyProperties(dict, excelDictDTO); excelDictDTOList.add(excelDictDTO); }); return excelDictDTOList; } }

⑥编写excel读取监听器并调用mapper接口

package com.atguigu.srb.core.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.atguigu.srb.core.mapper.DictMapper;
import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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

@Slf4j
@NoArgsConstructor
public class ExcelDictDTOListener extends AnalysisEventListener<ExcelDictDTO> {

    //@Resource注入mapper不行,因为本类并没有被spring管理,需要用参数构造的方式加入mapper
    private DictMapper dictMapper;

    public ExcelDictDTOListener(DictMapper dictMapper){
        this.dictMapper = dictMapper;
    }

    //数据列表
    List<ExcelDictDTO> list = new ArrayList<ExcelDictDTO>();

    //每隔五条记录存储一次数据
    static final  int BATCH_SIZE=5;
    @Override
    public void invoke(ExcelDictDTO data, AnalysisContext analysisContext) {
        log.info("解析到一条数据{}",data);
        //调用mapper层的save数据
        //批量解析,列表达到一定数量,然后一次性存储数据

        //数据存入数据列表
        list.add(data);
        if(list.size()>=BATCH_SIZE){
            saveData();
            list.clear();
        }
    }

    private void saveData() {
        log.info("{}条数据被存储到数据库",list.size());
        //调用mapper层的批量save方法
        //TODO
        dictMapper.insertBatch(list);
        log.info("{}条数据被存储到数据库成功",list.size());
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //最后剩余的数据不足BATCH_SIZE,最终一次性存储剩余数据
        saveData();
        log.info("解析完成");
    }
}

⑦编写mapper接口和具体的sql语句

package com.atguigu.srb.core.mapper;

import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.pojo.entity.Dict;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import java.util.List;

/**
 * 

* 数据字典 Mapper 接口 *

* * @author Likejin * @since 2023-04-09 */
public interface DictMapper extends BaseMapper<Dict> { void insertBatch(List<ExcelDictDTO> list); }

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.srb.core.mapper.DictMapper">

    <insert id="insertBatch">
        insert into dict (
        id ,
        parent_id ,
        name ,
        value ,
        dict_code
        ) values
        <foreach collection="list" item="item" index="index" separator=",">
            (
            #{item.id} ,
            #{item.parentId} ,
            #{item.name} ,
            #{item.value} ,
            #{item.dictCode}
            )
        foreach>
    insert>

mapper>

⑧配置打包时打包JAVA文件内的xml

  • JAVA目录下的xml不会被打包,只会打包.java
  • 代码生成器生成的xml文件都放在java包下
  • 两种解决方法:
    将xml文件都放入resources下
    打包方式改变
  • 使用打包方式
    <build>
        
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>falsefiltering>
            resource>
        resources>
    build>

6.实现下载依赖(后端)

①业务流程

  • 前端单击下载(向后端发起非ajax请求,可以不用设置跨域)
  • 后端controller接受请求,controller调用easyexcel接口返回excel文件
  • easyexcel接口利用service的获取到的数据封装为excel
  • 注意:
    调用代码生成器的service接口获得的是Dict类型数据,需要封装为ExcelDictDTO数据返回excel文件

②controller

package com.atguigu.srb.core.controller.admin;


import com.alibaba.excel.EasyExcel;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.common.result.R;
import com.atguigu.common.result.ResponseEnum;
import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.service.DictService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

/**
 * 

* 数据字典 前端控制器 *

* * @author Likejin * @since 2023-04-09 */
@Api(tags = "数据字典管理") @RestController @RequestMapping("/admin/core/dict") @Slf4j @CrossOrigin public class AdminDictController { @Resource DictService dictService; @ApiOperation("Excel数据的批量导入(导入到数据库)") @PostMapping("/import") public R batchImport( @ApiParam(value="Excel数据字典文件") @RequestParam("file") MultipartFile file){ try { InputStream inputStream = file.getInputStream(); dictService.importData(inputStream); return R.ok().message("数据字典批量导入成功"); } catch (Exception e) { //返回R对象,并且打印异常跟踪栈 throw new BusinessException(ResponseEnum.UPLOAD_ERROR,e); } } @ApiOperation("Excel数据的导出") //访问页面 @GetMapping("/export") public void export(HttpServletResponse response){ try { // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman //设置类型为excel response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 String fileName = URLEncoder.encode("mydict", "UTF-8").replaceAll("\\+", "%20"); //设置附件的形式下载到浏览器端 response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); EasyExcel.write(response.getOutputStream(), ExcelDictDTO.class).sheet("数据字典").doWrite(dictService.listDictData()); } catch (IOException e) { //EXPORT_DATA_ERROR(104, "数据导出失败"), throw new BusinessException(ResponseEnum.EXPORT_DATA_ERROR, e); } } }

③service层

  • 封装代码生成器返回Dict的list为DTO的list
package com.atguigu.srb.core.service;

import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.pojo.entity.Dict;
import com.baomidou.mybatisplus.extension.service.IService;

import java.io.InputStream;
import java.util.List;

/**
 * 

* 数据字典 服务类 *

* * @author Likejin * @since 2023-04-09 */
public interface DictService extends IService<Dict> { void importData(InputStream inputStream); List<ExcelDictDTO> listDictData(); }
package com.atguigu.srb.core.service.impl;

import com.alibaba.excel.EasyExcel;
import com.atguigu.srb.core.listener.ExcelDictDTOListener;
import com.atguigu.srb.core.mapper.DictMapper;
import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.pojo.entity.Dict;
import com.atguigu.srb.core.service.DictService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 

* 数据字典 服务实现类 *

* * @author Likejin * @since 2023-04-09 */
@Slf4j @Service public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService { @Resource private DictMapper dictMapper; //读取数据 //写入数据库,利用事务的操作成功全成功,失败全失败(只要异常出现则回滚) @Transactional(rollbackFor = Exception.class) @Override public void importData(InputStream inputStream) { //传入输入流, EasyExcel.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener(dictMapper)).sheet().doRead(); log.info("Excel导入成功"); } @Override public List<ExcelDictDTO> listDictData() { List<Dict> dictList = dictMapper.selectList(null); //创建ExcelDictDTO列表,将Dict列表转化为ExcelDictDTO列表 ArrayList<ExcelDictDTO> excelDictDTOList = new ArrayList<>(dictList.size()); dictList.forEach(dict -> { ExcelDictDTO excelDictDTO = new ExcelDictDTO(); //对象拷贝,dict拷贝到excelDictDTO(相同列拷贝) BeanUtils.copyProperties(dict, excelDictDTO); excelDictDTOList.add(excelDictDTO); }); return excelDictDTOList; } }

未更新

未更新

未更新

未更新

未更新

未更新

未更新

未更新

你可能感兴趣的:(项目1:金融借钱还钱,java)