EasyExcel3.0.5导出多个sheet,含查询优化

文章目录

  • 前言
  • 创建数据表
  • 代码实现篇
    • POM.xml
    • 配置类
      • 文档配置
      • MyBatis-plus 分页插件配置
      • 自动代码生成配置
    • 准备工作:实体、枚举和转换类
      • 订单实体
      • 订单详情实体
      • 订单图片实体
      • 评价枚举
      • 发票枚举
      • 付款方式枚举
      • 来源枚举
    • Controller 层
    • Service 层
      • Service 实现(重中之重)
    • Mapper
  • 测试
  • 结语

前言

最近做项目时,有一个比较棘手的问题,就是导出多个sheet。不好处理的地方如下:

  1. 分页查询优化;
  2. sheet 拆分;
  3. 还需要下载图片链接的文件。

花了五六个小时,总算是大功告成,保姆式的,我把优化,转换这些细枝末节的都做完了,参考这个例子可解决导出多个 sheet 的疑难杂症。

创建数据表

创建订单表:tb_order

CREATE TABLE `tb_order` (
  `order_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',
  `total_pay` bigint(20) NOT NULL COMMENT '总金额,单位为分',
  `actual_pay` bigint(20) NOT NULL COMMENT '实付金额。单位:分。如:20007,表示:200元7分',
  `payment_type` tinyint(1) unsigned zerofill NOT NULL COMMENT '支付类型,1、在线支付,2、货到付款',
  `post_fee` bigint(20) NOT NULL COMMENT '邮费。单位:分。如:20007,表示:200元7分',
  `create_time` datetime DEFAULT NULL COMMENT '订单创建时间',
  `shipping_name` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流名称',
  `shipping_code` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流单号',
  `user_id` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '用户id',
  `buyer_message` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '买家留言',
  `buyer_nick` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '买家昵称',
  `buyer_rate` tinyint(1) DEFAULT NULL COMMENT '买家是否已经评价,0未评价,1已评价',
  `receiver_state` varchar(100) COLLATE utf8_bin DEFAULT '' COMMENT '收获地址(省)',
  `receiver_city` varchar(255) COLLATE utf8_bin DEFAULT '' COMMENT '收获地址(市)',
  `receiver_district` varchar(255) COLLATE utf8_bin DEFAULT '' COMMENT '收获地址(区/县)',
  `receiver_address` varchar(255) COLLATE utf8_bin DEFAULT '' COMMENT '收获地址(街道、住址等详细地址)',
  `receiver_mobile` varchar(12) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人手机',
  `receiver_zip` varchar(15) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人邮编',
  `receiver` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人',
  `invoice_type` int(1) DEFAULT '0' COMMENT '发票类型(0无发票1普通发票,2电子发票,3增值税发票)',
  `source_type` int(1) DEFAULT '2' COMMENT '订单来源:1:app端,2:pc端,3:M端,4:微信端,5:手机qq端',
  PRIMARY KEY (`order_id`),
  KEY `create_time` (`create_time`),
  KEY `buyer_nick` (`buyer_nick`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

创建订单详情表:

CREATE TABLE `tb_order_detail` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单详情id ',
  `order_id` bigint(20) NOT NULL COMMENT '订单id',
  `sku_id` bigint(20) NOT NULL COMMENT 'sku商品id',
  `num` int(11) NOT NULL COMMENT '购买数量',
  `title` varchar(200) NOT NULL COMMENT '商品标题',
  `own_spec` varchar(1000) DEFAULT '' COMMENT '商品动态属性键值集',
  `price` bigint(20) NOT NULL COMMENT '价格,单位:分',
  `image` varchar(200) DEFAULT '' COMMENT '商品图片',
  PRIMARY KEY (`id`),
  KEY `key_order_id` (`order_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COMMENT='订单详情表';

代码实现篇

为了完整性,决定还是全部贴出来。

POM.xml


<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.5.5version>
        <relativePath/> 
    parent>
    <groupId>com.examplegroupId>
    <artifactId>springBoot-easyExcelartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>springBoot-easyExcelname>
    <description>springBoot-easyExceldescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.5.1version>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-generatorartifactId>
            <version>3.5.1version>
        dependency>

        <dependency>
            <groupId>org.freemarkergroupId>
            <artifactId>freemarkerartifactId>
            <version>2.3.31version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.2.8version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>easyexcelartifactId>
            <version>3.0.5version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.80version>
        dependency>

        
        <dependency>
            <groupId>com.github.xiaoymingroupId>
            <artifactId>knife4j-spring-boot-starterartifactId>
            <version>2.0.9version>
        dependency>

        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.7.22version>
        dependency>

        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-collections4artifactId>
            <version>4.4version>
        dependency>

    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <version>2.5.5version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombokgroupId>
                            <artifactId>lombokartifactId>
                        exclude>
                    excludes>
                configuration>
            plugin>
        plugins>
    build>
project>

配置类

文档配置

文档配置类:Knife4jConfiguration.java

package cn.com.easyExcel.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {

    @Bean(value = "createRestFulApi")
    public Docket createRestFulApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        .description("
swagger-bootstrap-ui RestFul APIs
"
) .version("1.0") .build()) //分组名称 .groupName("2.X版本") .select() //这里指定Controller扫描包路径 .apis(RequestHandlerSelectors.basePackage("cn.com.easyExcel.controller")) .paths(PathSelectors.any()) .build(); } }

MyBatis-plus 分页插件配置

package cn.com.easyExcel.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("cn.com.ztn.excel.mapper")
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

自动代码生成配置

package cn.com.easyExcel.pojo;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Collections;

public class AutoGenerator {

    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/test", "root", "root")
                .globalConfig(builder -> {
                    builder.author("") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("D://autoGenerator"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("cn.com") // 设置父包名
                            .moduleName("easyExcel") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://autoGenerator/xml")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("tb_order", "tb_order_detail") // 设置需要生成的表名
                            .addTablePrefix("tb_", "t_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
}

准备工作:实体、枚举和转换类

订单实体

Order.java,包含 easyExcel 注解

package cn.com.easyExcel.pojo;

import cn.com.easyExcel.excel.converter.BuyerRateConverter;
import cn.com.easyExcel.excel.converter.InvoiceTypeConverter;
import cn.com.easyExcel.excel.converter.PaymentTypeConverter;
import cn.com.easyExcel.excel.converter.SourceTypeConverter;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@TableName("tb_order")
@ApiModel(value = "Order", description = "Order对象")
public class Order implements Serializable {

    private static final long serialVersionUID = -3009679794631979306L;

    @ExcelIgnore
    @ApiModelProperty("订单id")
    @TableId(value = "order_id", type = IdType.AUTO)
    private Long orderId;

    @ApiModelProperty("总金额,单位为分")
    @ColumnWidth(18)
    @ExcelProperty(value = "总金额", index = 0)
    private Long totalPay;

    @ApiModelProperty("实付金额。单位:分。如:20007,表示:200元7分")
    @ColumnWidth(18)
    @ExcelProperty(value = "实付金额", index = 1)
    private Long actualPay;

    @ApiModelProperty("支付类型,1、在线支付,2、货到付款")
    @ColumnWidth(22)
    @ExcelProperty(value = "支付类型", index = 2, converter = PaymentTypeConverter.class)
    private Boolean paymentType;

    @ApiModelProperty("邮费。单位:分。如:20007,表示:200元7分")
    @ColumnWidth(18)
    @ExcelProperty(value = "邮费", index = 3)
    private Long postFee;

    @ApiModelProperty("订单创建时间")
    @ColumnWidth(22)
    @ExcelProperty(value = "订单创建时间", index = 4)
    private LocalDateTime createTime;

    @ApiModelProperty("物流名称")
    @ColumnWidth(24)
    @ExcelProperty(value = "物流名称", index = 5)
    private String shippingName;

    @ApiModelProperty("物流单号")
    @ColumnWidth(24)
    @ExcelProperty(value = "物流单号", index = 6)
    private String shippingCode;

    @ApiModelProperty("用户id")
    @ExcelIgnore
    private String userId;

    @ApiModelProperty("买家留言")
    @ColumnWidth(30)
    @ExcelProperty(value = "买家留言", index = 7)
    private String buyerMessage;

    @ApiModelProperty("买家昵称")
    @ColumnWidth(20)
    @ExcelProperty(value = "买家昵称", index = 8)
    private String buyerNick;

    @ApiModelProperty("买家是否已经评价,0未评价,1已评价")
    @ColumnWidth(22)
    @ExcelProperty(value = "买家是否已经评价", index = 9, converter = BuyerRateConverter.class)
    private Boolean buyerRate;

    @ApiModelProperty("收获地址(省)")
    @ColumnWidth(16)
    @ExcelProperty(value = "省", index = 10)
    private String receiverState;

    @ApiModelProperty("收获地址(市)")
    @ColumnWidth(16)
    @ExcelProperty(value = "市", index = 11)
    private String receiverCity;

    @ApiModelProperty("收获地址(区/县)")
    @ColumnWidth(16)
    @ExcelProperty(value = "县", index = 12)
    private String receiverDistrict;

    @ApiModelProperty("收获地址(街道、住址等详细地址)")
    @ColumnWidth(32)
    @ExcelProperty(value = "街道、住址等详细地址", index = 13)
    private String receiverAddress;

    @ApiModelProperty("收货人手机")
    @ColumnWidth(22)
    @ExcelProperty(value = "手机号码", index = 14)
    private String receiverMobile;

    @ApiModelProperty("收货人邮编")
    @ColumnWidth(20)
    @ExcelProperty(value = "邮编", index = 15)
    private String receiverZip;

    @ApiModelProperty("收货人")
    @ColumnWidth(20)
    @ExcelProperty(value = "收货人", index = 16)
    private String receiver;

    @ApiModelProperty("发票类型(0无发票1普通发票,2电子发票,3增值税发票)")
    @ColumnWidth(22)
    @ExcelProperty(value = "发票类型", index = 17, converter = InvoiceTypeConverter.class)
    private Integer invoiceType;

    @ApiModelProperty("订单来源:1:app端,2:pc端,3:M端,4:微信端,5:手机qq端")
    @ColumnWidth(20)
    @ExcelProperty(value = "订单来源", index = 18, converter = SourceTypeConverter.class)
    private Integer sourceType;
}

订单详情实体

OrderDetail.java

package cn.com.easyExcel.pojo;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@TableName("tb_order_detail")
@ApiModel(value = "OrderDetail", description = "订单详情表")
public class OrderDetail implements Serializable {

    private static final long serialVersionUID = 7472956543906342272L;

    @ExcelIgnore
    @ApiModelProperty("订单详情id ")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @ApiModelProperty("订单id")
    @ColumnWidth(22)
    @ExcelProperty(value = "订单id", index = 0)
    private Long orderId;

    @ApiModelProperty("sku商品id")
    @ColumnWidth(20)
    @ExcelProperty(value = "sku商品id", index = 1)
    private Long skuId;

    @ApiModelProperty("购买数量")
    @ColumnWidth(18)
    @ExcelProperty(value = "购买数量", index = 2)
    private Integer num;

    @ApiModelProperty("商品标题")
    @ColumnWidth(24)
    @ExcelProperty(value = "商品标题", index = 3)
    private String title;

    @ApiModelProperty("商品动态属性键值集")
    @ExcelIgnore
    private String ownSpec;

    @ApiModelProperty("价格,单位:分")
    @ColumnWidth(20)
    @ExcelProperty(value = "价格", index = 4)
    private Long price;

    @ApiModelProperty("商品图片")
    @ExcelIgnore
    private String image;

}

订单图片实体

OrderImage.java

package cn.com.easyExcel.pojo;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ApiModel(value = "OrderImage", description = "订单图片")
public class OrderImage {

    @ColumnWidth(56)
    @ExcelProperty(value = "图片", index = 0)
    @ApiModelProperty("图片")
    private String image;
}

评价枚举

BuyerRateEnum.java

package cn.com.easyExcel.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

public enum BuyerRateEnum {
    ONLINE(Boolean.TRUE, "未评价"),
    OFFLINE(Boolean.FALSE, "已评价");

    @EnumValue
    private Boolean key;

    @JsonValue
    private String value;

    BuyerRateEnum(Boolean key, String value) {
        this.key = key;
        this.value = value;
    }

    public Boolean getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }
}

发票枚举

InvoiceTypeEnum.java

package cn.com.easyExcel.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public enum InvoiceTypeEnum {

    NO_INVOICE(0, "无发票"),
    NORMAL_INVOICE(1, "普通发票"),
    ELECTRON_INVOICE(2, "电子发票"),
    VAT_INVOICE(3, "增值税发票");

    @EnumValue
    private Integer key;

    @JsonValue
    private String value;

    InvoiceTypeEnum(int key, String value) {
        this.key = key;
        this.value = value;
    }

    public Integer getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }

    public static Integer getRandomInvoiceType() {
        List<Integer> invoiceKeys = Arrays.stream(InvoiceTypeEnum.values())//
                .map(InvoiceTypeEnum::getKey)//
                .collect(Collectors.toList());

        int index = (int) (Math.random() * invoiceKeys.size());
        return invoiceKeys.get(index);
    }
}

付款方式枚举

PaymentTypeEnum.java

package cn.com.easyExcel.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

public enum PaymentTypeEnum {

    ONLINE(Boolean.TRUE, "在线支付"),
    OFFLINE(Boolean.FALSE, "货到付款");

    @EnumValue
    private Boolean key;

    @JsonValue
    private String value;

    PaymentTypeEnum(Boolean key, String value) {
        this.key = key;
        this.value = value;
    }

    public Boolean getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }
}

来源枚举

SourceTypeEnum.java

package cn.com.easyExcel.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public enum SourceTypeEnum {

    APP(0, "app端"),
    PC(1, "pc端"),
    M(2, "M端"),
    WX(3, "微信端"),
    QQ(4, "手机QQ端");

    @EnumValue
    private Integer key;

    @JsonValue
    private String value;

    SourceTypeEnum(Integer key, String value) {
        this.key = key;
        this.value = value;
    }

    public Integer getKey() {
        return key;
    }

    public String getValue() {
        return value;
    }

    public static Integer getRandomSourceType() {
        List<Integer> sourceKeys = Arrays.stream(SourceTypeEnum.values())//
                .map(SourceTypeEnum::getKey)//
                .collect(Collectors.toList());

        int index = (int) (Math.random() * sourceKeys.size());
        return sourceKeys.get(index);
    }
}

Controller 层

OrderController.java

package cn.com.easyExcel.controller;

import cn.com.easyExcel.param.OrderExportParam;
import cn.com.easyExcel.service.OrderService;
import cn.com.easyExcel.vo.ResultVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;

@Api(tags = "订单表")
@RestController
@RequestMapping(value = "/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @ApiOperation(value = "初始化订单数据")
    @GetMapping("/initOrderData")
    public ResultVo initOrderData(){
        orderService.initOrderData();
        return ResultVo.success();
    }

    @ApiOperation(value = "多sheet导出")
    @PostMapping("/excel/moreSheetExport")
    public void moreSheetExport(HttpServletResponse response, OrderExportParam param){
        orderService.moreSheetExport(response, param);
    }
}

ResultVo.java

package cn.com.easyExcel.vo;

import lombok.Data;

@Data
public class ResultVo<T> {
    private static final String SUCCESS_CODE = "S10000";
    private static final String UNKNOWN_FAIL_CODE = "E10001";

    private T data;
    private String code;
    private String message;
    private boolean success;

    public ResultVo() {
    }

    public ResultVo(boolean success, String code) {
        this.success = success;
        this.code = code;
    }

    public ResultVo(T data, boolean success, String code, String message) {
        this.data = data;
        this.success = success;
        this.code = code;
        this.message = message;
    }

    public static ResultVo<Void> success() {
        return success(null, (String)null);
    }

    public static <R> ResultVo<R> success(R data) {
        return success(data, (String)null);
    }

    public static <R> ResultVo<R> success(R data, String message) {
        return new ResultVo(data, true, SUCCESS_CODE, message);
    }

    public static ResultVo<Void> successMsg(String message) {
        return success(null, message);
    }

    public static <R> ResultVo<R> failure(String code) {
        return failure(code, (String)null);
    }

    public static <R> ResultVo<R> failure(String code, String message) {
        return failure(null, code, message);
    }

    public static <R> ResultVo<R> failure(R data, String code, String message) {
        return new ResultVo(data, false, code, message);
    }

    public static <R> ResultVo<R> failureMsg(String message) {
        return failure(UNKNOWN_FAIL_CODE, message);
    }

}

Service 层

OrderService.java

package cn.com.easyExcel.service;

import cn.com.easyExcel.param.OrderExportParam;

import javax.servlet.http.HttpServletResponse;

public interface OrderService {
    /**
     * 初始化订单数据
     */
    void initOrderData();

    /**
     * 多 sheet 导出
     */
    void moreSheetExport(HttpServletResponse response, OrderExportParam param);
}

Service 实现(重中之重)

package cn.com.easyExcel.service.impl;

import cn.com.easyExcel.enums.InvoiceTypeEnum;
import cn.com.easyExcel.enums.SourceTypeEnum;
import cn.com.easyExcel.excel.listener.ExportListener;
import cn.com.easyExcel.mapper.OrderDetailMapper;
import cn.com.easyExcel.mapper.OrderMapper;
import cn.com.easyExcel.param.OrderExportParam;
import cn.com.easyExcel.pojo.Order;
import cn.com.easyExcel.pojo.OrderDetail;
import cn.com.easyExcel.pojo.OrderImage;
import cn.com.easyExcel.service.OrderService;
import cn.com.easyExcel.util.InitDataUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    private static final int PAGE_SIZE = 100;

    private static final String PATTERN = "yyyy-MM-dd-HH-mm-ss";

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern(PATTERN);

    private static final String CHARACTER_UTF_8 = "UTF-8";

    private static final String ORDER_INFO =  "订单信息";

    @Override
    public void initOrderData() {
        for (int i = 1; i <= 280; i++) {
            Order order = Order.builder()
//                    .orderId((long) i)
                    .totalPay(2000L)    //单位:分
                    .actualPay(2000L)
                    .paymentType(InitDataUtil.getBoolean())
                    .postFee(1000L)
                    .createTime(LocalDateTime.now())
                    .shippingName(InitDataUtil.getShippingName())
                    .shippingCode(InitDataUtil.getShippingCode())
                    .userId(InitDataUtil.getShippingCode())
                    .buyerMessage("质量不错,买的很划算")
                    .buyerNick(InitDataUtil.getReceiver())
                    .buyerRate(InitDataUtil.getBoolean())
                    .receiverState(InitDataUtil.getReceiverState())
                    .receiverCity("华夏国泰民安市")
                    .receiverAddress("华夏国泰民安街道")
                    .receiverMobile(InitDataUtil.getMobile())
                    .receiver(InitDataUtil.getReceiver())
                    .invoiceType(InvoiceTypeEnum.getRandomInvoiceType())
                    .sourceType(SourceTypeEnum.getRandomSourceType())
                    .build();
            orderMapper.insert(order);

            OrderDetail orderDetail = OrderDetail.builder()
                    .orderId(order.getOrderId())
                    .skuId(33L)
                    .num(10)
                    .title(InitDataUtil.getTitle())
                    .price(3000L)
                    .image(InitDataUtil.getImage())
                    .build();
            orderDetailMapper.insert(orderDetail);
        }
    }

    @SneakyThrows
    @Override
    public void moreSheetExport(HttpServletResponse response, OrderExportParam param) {
        LambdaQueryWrapper<Order> queryWrapper = Wrappers.lambdaQuery();
        //TODO:用queryWrapper组装前端传递的参数省略
        moreSheetExport(response, ORDER_INFO, queryWrapper);
    }

    /**
     * 多sheet导出
     */
    public void moreSheetExport(HttpServletResponse response, String sheetName,
                                LambdaQueryWrapper<Order> queryWrapper) throws IOException {
        ServletOutputStream out = ExportListener.getServletOutputStream(response, sheetName);
        ExcelWriter excelWriter = EasyExcel.write(out).build();

        String nowTime = dtf.format(LocalDateTime.now());

        int startIndex = 1;
        while (true){

            int startParam =(startIndex - 1) * PAGE_SIZE;
            int pageIndex = (int) Math.ceil((double) startParam / (double) PAGE_SIZE+1);
            Page<Order> pageQuery = new Page<>(pageIndex, PAGE_SIZE, false);
            Page<Order> orderListByPage = orderMapper.selectPage(pageQuery, queryWrapper);

            List<Order> orderList = orderListByPage.getRecords();
            if (CollectionUtils.isEmpty(orderList)) {
                break;
            }
            WriteSheet orderSheet = EasyExcel.writerSheet(0, "订单信息").head(Order.class).build();
            excelWriter.write(orderList, orderSheet);

            LambdaQueryWrapper<OrderDetail> detailWrapper = Wrappers.lambdaQuery();
            List<Long> orderIds = orderList.stream().map(Order::getOrderId).collect(Collectors.toList());
            detailWrapper.in(OrderDetail::getOrderId, orderIds);
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(detailWrapper);    //查询详情
            WriteSheet detailSheet = EasyExcel.writerSheet(1, "订单详情").head(OrderDetail.class).build();
            excelWriter.write(orderDetailList, detailSheet);

            List<String> imageList = orderDetailList.stream().map(OrderDetail::getImage).collect(Collectors.toList());
            List<OrderImage> orderImageList = new ArrayList<>();
            for (String image : imageList) {
                OrderImage orderImage = OrderImage.builder().image(image).build();
                orderImageList.add(orderImage);
            }
            WriteSheet imageSheet = EasyExcel.writerSheet(2, "订单图片").head(OrderImage.class).build();
            excelWriter.write(orderImageList, imageSheet);

            if(CollectionUtils.isNotEmpty(imageList)){
                for(String image : imageList){
                    downloadFile(image, "D:\\ORDER-" + nowTime);
                }
            }
            startIndex++;
        }
        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
    }

    public static void downloadFile(String urlStr, String savePath) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 设置超时间为3秒
        conn.setConnectTimeout(3 * 1000);
        // 防止屏蔽程序抓取而返回:403
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
        // 得到输入流
        InputStream inputStream = conn.getInputStream();

        String headerField = conn.getHeaderField("Content-Disposition");
        String realFileName = headerField.split(";")[2].split("=")[1];
        String fileName = URLDecoder.decode(realFileName, CHARACTER_UTF_8);

        // 获取字节数组
        byte[] getData = readInputStream(inputStream);
        // 文件保存位置
        File saveDir = new File(savePath);
        if (!saveDir.exists()) {
            saveDir.mkdir();
        }
        File file = new File(saveDir + File.separator + fileName);
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(getData);
        fos.close();
        inputStream.close();
    }

    public static byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }
}

导出监听器:ExportListener.java,在前面的好几篇关于 easyExcel 的文章已经分享过,详情请移步 EasyExcel3.0.5 加快大数据查询速度,查询性能优化。

初始化数据:InitDataUtil.java

package cn.com.easyExcel.util;

import java.util.UUID;

public class InitDataUtil {
    public static Boolean getBoolean(){
        Boolean[] doc = {Boolean.TRUE, Boolean.FALSE};
        int index = (int) (Math.random() * doc.length);
        return doc[index];
    }

    public static String getShippingName(){
        String[] doc = {"顺丰物流", "中通物流", "韵达物流", "百世通", "云途物流"};
        int index = (int) (Math.random() * doc.length);
        return doc[index];
    }

    public static String getReceiverState(){
        String[] doc = {"北京", "上海", "广东省", "河北省", "河南省", "湖南省", "湖北省", "江西省", "福建省"};
        int index = (int) (Math.random() * doc.length);
        return doc[index];
    }

    public static String getShippingCode(){
        //随机生成一位整数
        int random = (int) (Math.random()*9+1);
        String valueOf = String.valueOf(random);
        //生成uuid的hashCode值
        int hashCode = UUID.randomUUID().toString().hashCode();
        //可能为负数
        if(hashCode<0){
            hashCode = -hashCode;
        }
        return valueOf + String.format("%015d", hashCode);
    }

    public static String getMobile() {
        //给予真实的初始号段,号段是在百度上面查找的真实号段
        String[] start = {"133", "149", "153", "173", "177",
                "180", "181", "189", "199", "130", "131", "132",
                "145", "155", "156", "166", "171", "175", "176", "185", "186", "166", "134", "135",
                "136", "137", "138", "139", "147", "150", "151", "152", "157", "158", "159", "172",
                "178", "182", "183", "184", "187", "188", "198", "170", "171"};

        String phoneFirstNum = start[(int) (Math.random() * start.length)];
        String phoneLastNum = "";
        //定义尾号,尾号是8位
        final int LENPHONE = 8;
        //循环剩下的位数
        for (int i = 0; i < LENPHONE; i++) {
            //每次循环都从0~9挑选一个随机数
            phoneLastNum += (int) (Math.random() * 10);
        }
        //最终将号段和尾数连接起来
        return phoneFirstNum + phoneLastNum;
    }

    public static String getReceiver(){
        String[] doc = {"朝歌晚酒", "都怪时光太动听", "笑我孤陋", "水墨青花","时光清浅", "草帽撸夫", "江山如画",
                "热度不够", "盏茶浅抿", "把酒临风", "且听风吟", "梦忆笙歌", "倾城月下", "清风墨竹", "自愈心暖", "几许轻唱",
                "平凡之路", "半夏倾城", "南栀倾寒", "孤君独战", "温酒杯暖", "眉目亦如画", "旧雪烹茶", "律断华章", "清酒暖风",
                "清羽墨安", "一夕夙愿", "南顾春衫", "和云相伴", "夕颜若雪", "时城旧巷", "梦屿千寻", "故港笑别", "水袖萦香",
                "秋水墨凉", "海棠花瘦", "千城暮雪", "华灯初上", "一纸枕书", "剑断青丝", "风烟影月", "日月星辰", "浅喜深爱"};
        int index = (int) (Math.random() * doc.length);
        return doc[index];
    }

    public static String getTitle(){
        String[] doc = {"手机", "小家电", "白色家电", "饮料食品","办公用品", "户外运动", "美妆护肤", "母婴"};
        int index = (int) (Math.random() * doc.length);
        return doc[index];
    }

	//图片上传到 OSS 返回的路径
    public static String getImage(){
        String[] doc = {"https://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/day1/file/19236f66ba0b4a08988961ec15c6047f",
                "https://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/day1/file/4e2a64bb97f8439b8d5cde57b3337aea",
                "https://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/day1/file/54737626a07d4147ab1fcb8beac7eaa8",
                "https://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/day1/file/1f1b064497914a16b847801c13890a28",
                "https://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/day1/file/af7f868044634ddabc52da7e6778eb36",
                "https://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/day1/file/15eb6eaa6241402eb79ee9b938cdea31",
                "https://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/day1/file/56ef58f283f34c5e986101c9ca091cef"};
        int index = (int) (Math.random() * doc.length);
        return doc[index];
    }
}

Mapper

OrderMapper.java

package cn.com.easyExcel.mapper;

import cn.com.easyExcel.pojo.Order;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface OrderMapper extends BaseMapper<Order> {

}

OrderDetailMapper.java

package cn.com.easyExcel.mapper;

import cn.com.easyExcel.pojo.OrderDetail;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface OrderDetailMapper extends BaseMapper<OrderDetail> {

}

mapper.xml 没有写任何代码,mybatis-plus 的方便强大之处。

测试

EasyExcel3.0.5导出多个sheet,含查询优化_第1张图片
EasyExcel3.0.5导出多个sheet,含查询优化_第2张图片
EasyExcel3.0.5导出多个sheet,含查询优化_第3张图片
EasyExcel3.0.5导出多个sheet,含查询优化_第4张图片
图片下载在指定目录,只有七个图片。
EasyExcel3.0.5导出多个sheet,含查询优化_第5张图片

结语

好了,费了很大功夫才实现,觉得有用,欢迎留言点赞。

你可能感兴趣的:(easyexcel,微服务,spring,boot)