• 首页
  • 产品中心
    • 数式Oinone四大产品

      低代码开发平台无代码开发平台集成开发平台AI大模型开发
    • 数式Oinone体系能力

      用户与组织权限管理文件管理消息中心国际化业务审计
    • 数式Oinone核心产品特性

      低无一体面向软件公司场景无限制应用级扩容可分可合
  • 服务中心
    • 客户服务

      预约演示方案咨询私有部署找人定制
    • 开发者

      问答下载
    • Oinone学院

      社区学习

    《精讲面向软件公司的低代码平台——以Oinone为例》

  • 合作伙伴
    渠道申请伙伴名录专家库
  • 关于数式
0571-88757863

文件导入导出(Import And Export)


一、概述

在 Oinone 中,导入/导出功能是通过 Excel 文件作为媒介进行处理的,这也是大多数管理信息系统常用的一种方式。

在功能设计时,我们发现任何一个工作表都可以拆分为一个一个不重复的区块进行单独设计,这也是 Oinone 设计导入/导出模板的设计亮点之一。

Excel 导入/导出模板通过多区块设计简化了单个工作表在业务系统中灵活定义的复杂度,在使用 Oinone 导入/导出功能时,灵活的拆解工作表是一项必备技能。

下面将从 Excel 模板设计概念出发,一步一步帮助读者学会使用导入/导出功能以满足业务需求。

(一)Excel 模板设计概念

1、名词解释

  • 工作簿(Workbook):一个 Excel 文件称为一个工作簿。
  • 工作表(Sheet):一个工作簿中存在多个工作表。
  • 区块(Block):一个工作表中包含多个区块,区块的顺序在多个工作表中是连续的。
  • 行(Row):在工作表中水平方向的所有单元格的集合。
  • 列(Col):在工作表中垂直方向的所有单元格的集合。
  • 单元格(Cell):由坐标 A1 定位可以得到一个数据存放的最小单元。
  • 解析类型(analysisType)
    • 固定表头:与 “表格” 类似,多条数据按 “表头” 定义的格式 “向下” 填充。
    • 固定格式:与 “表单” 类似,单条数据按 “单元格” 定义的格式进行填充。
  • 排列方向(direction)
    • 水平排列:子元素水平排列,垂直填充。
    • 垂直排列:子元素垂直排列,水平填充。

2、固定表头

如图所示,左侧为设计区域,右侧为填充后的结果,总共由四个区块组成。

  • 第一个区块:允许定义多级表头。
  • 第二个区块:上下排列的两个区块,第二个区块将根据第一个区块填充内容的数量向下平移。
  • 第三个区块:当 “末级行” 具备样式时,在填充时将保留原有样式。如图所示:合并单元格样式将在填充时进行保留。
  • 第四个区块:当 “排列方向” 设置为垂直排列时,表头会自动 “行列转置” 并进行水平填充。

3、固定格式

如图所示,左侧为设计区域,右侧为填充后的结果,只有一个区块。显而易见的是,固定格式的填充方式相比固定表头来说要简单的多,仅仅将数据按设计好的位置进行填充即可。

4、设计范围

对于不同的 “解析类型” ,设计范围有些许区别:

  • 固定表头:设计范围需要向填充方向扩展一行。
  • 固定格式:设计范围与 Excel 定义范围完全一致。

(二)模型拓扑图

提示:

更多关于 “文件模块” 相关 API 的内容请参考:Reference List - 模型

二、准备工作

在使用文件导入导出功能时,需要在 pamirs-demo-boot 引入 pamirs-file2-core 包依赖,并在启动模块中增加 file 模块。

<dependency>
  <groupId>pro.shushi.pamirs.core</groupId>
  <artifactId>pamirs-file2-core</artifactId>
</dependency>
pamirs:
	boot:
    modules:
      - file

如果需要在模块中定义导入/导出模板或自定义导入/导出逻辑的,需要在 pamirs-demo-api 引入 pamirs-file2-api 包依赖:

<dependency>
  <groupId>pro.shushi.pamirs.core</groupId>
  <artifactId>pamirs-file2-api</artifactId>
</dependency>

在 Oinone 中,除了对应依赖的引入外,还需要在 当前模块 定义中声明对应的模块依赖:

……
@Module(
    name = DemoModule.MODULE_NAME,
    displayName = "演示模块",
    version = "1.0.0",
    priority = 1,
    dependencies = {
        ……
        FileModule.MODULE_MODULE,
        ……
    }
)
……
public class DemoModule implements PamirsModule {
……
}

三、Yaml 配置

pamirs:
  boot:
    modules:
      - file
  file:
    auto-upload-logo: false # 启动时自动上传 logo
    auto-create-template: true # 启动时自动生成 Excel 模板
    import-property:
      default-each-import: false # 默认逐行导入
      max-error-length: 100 # 默认最大收集错误行数
    export-property:
      default-clear-export-style: false # 默认使用csv导出
      excel-max-support-length: 100000 # excel导出最大支持100000行
      csv-max-support-length: 1000000 # csv导出最大支持1000000行

四、创建模板

(一)使用 ExcelHelper 创建模板

在之前的 “教程 - 文件导入导出” 中我们已经初步使用过 ExcelHelper 工具类来创建一个简单的固定表头的 Excel 模板,先让我们简单回顾一下:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(ExcelHelper.fixedHeader(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createBlock("测试模型", TestModel.MODEL_MODEL)
                .addColumn("code", "编码")
                .addColumn("name", "名称")
                .addColumn("user.login", "用户账号")
                .build());
    }
}

ExcelHelper#fixedHeader 是创建 固定表头 类型模板的一种简化方式,其本质还是通过 WorkbookDefinitionBuilder 创建模板的。其简化了一些常用功能:

  • createBlock:是 createSheet 和 createBlock 的组合调用,工具类中也提供了单独的调用方法。
  • 自动计算设计区域,当 addColumn 被调用时,将增加一列,设计区域将进行水平扩展。
  • 简化了配置行和表头一一对应,这一点可以在下一节内容中得到体现。

对于这样创建的 Excel 模板内容,在下一节内容会详细解释。

提示:

在 ExcelHelper#fixedHeader 调用之后,会创建一个 ExcelFixedHeadHelper 对象提供一些简单的方法创建工作簿,如果出现无法设置或无法定义的场景,请使用 WorkbookDefinitionBuilder 创建模板。

提示:

在这里我们选择使用 “导入模板” 进行演示,目的是为了通过 “下载导入模板” 功能快速看到 Excel 文件对应的效果,读者可以在每一步骤之后自行通过这一功能下载对应的 Excel 文件查看其内容,导入模板的内容与设计内容是完全一致的。

读者还可自行创建 “导出模板” 对示例中用到的一些功能进行尝试,在本章内容中只会有 “导入模板” 的示例代码。

(二)使用 WorkbookDefinitionBuilder 创建模板

让我们看一下和上一小节中使用 ExcelHelper 创建相同的模板时,使用 WorkbookDefinitionBuilder 对应的写法:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "$A$1:$C$2")
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(Boolean.TRUE)
                .createCell().setField("code").and()
                .createCell().setField("name").and()
                .createCell().setField("user.login").and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("编码").and()
                .createCell().setValue("名称").and()
                .createCell().setValue("用户账号").and()
                .and()
                .and()
                .and()
                .build());
    }
}

我们可以看到,创建 Excel 工作簿的过程是按步骤一步一步处理的:

  1. 创建工作簿并设置属性。
  2. 创建工作表。
  3. 创建区块并设置属性。
  4. 创建配置行,用于字段的声明,不在 Excel 工作簿中显示。
  5. 创建表头行,在 Excel 工作簿中显示的静态内容。

PS:and 方法 用于 “返回上一层” 构建。

针对每个方法的简单介绍如下所示:

  • createSheet 和 and 组合:创建工作表并指定工作表名称。
  • createBlock 和 and 组合:创建区块,并指定区块对应的解析类型、排列方向、模型以及设计区域。
  • createHeader 和 setIsConfig(true) 组合:创建配置行,仅指定字段即可。
  • createHeader 和 and 组合:创建表头行,仅指定单元格内容即可。
  • ExcelHelper.createDefaultStyle:创建默认样式:全边框单元格;水平常规对其;垂直居中;11号字体;无加粗。
  • setBold(true):设置字体加粗。

用这样的方式创建出来的 Excel 模板文件是这样的:

提示:

  • 设计范围可以通过 Excel 单元格定位的语法格式进行表示,固定表头类型的区块需要向填充方向扩展一行。
  • 在表头行设置的行样式在单元格未设置单元格样式的情况下会使用行样式作为单元格样式,可以理解为行样式是这一行所有单元格的默认样式。
  • 在配置行设置的行样式将在数据填充时会被每一个被填充单元格使用。

警告:

“链式调用” 是 “文件模块” 在设计 API 时根据数据结构特点提供的较为清晰的一种使用方式,但在 Java 中,链式调用的栈长度有一定的限制,如果编译时出现 “java: Compilation failed: internal java compiler error” 异常,可以通过给变量赋值的方式 “打断” 链式调用使程序可以正常运行。

(三)创建固定格式模板

下面让我们来看一下 “固定格式” 模板的创建方法,它与固定表头模板类似,唯一不同的是,标题与字段是间隔定义的,完全按照 Excel 单元格顺序一行一行进行创建,正如下面的代码所示:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_FORMAT, ExcelDirectionEnum.HORIZONTAL, "$A$1:$D$2")
                .createMergeRange("B2:D2")
                .createHeader().setIsConfig(true)
                .createCell().and()
                .createCell().and()
                .createCell().and()
                .createCell().and()
                .and()
                .createRow().setStyleBuilder(ExcelHelper.createDefaultStyle())
                .createCell().setValue("编码").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("code").and()
                .createCell().setValue("名称").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("name").and()
                .and()
                .createRow().setStyleBuilder(ExcelHelper.createDefaultStyle())
                .createCell().setValue("用户账号").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("user.login").and()
                .createCell().and()
                .createCell().and()
                .and()
                .and()
                .and()
                .build());
    }
}

在我们创建好这个模板后,通过 “下载导入模板” 功能就可以看到如下所示的 Excel 文件内容:

提示:

在这个示例中,我们用到了 createMergeRange 来创建合并单元格,其坐标定位方式与 Excel 文件支持的方式完全一样。

这里需要注意的是,如果在 createSheet 之后创建合并单元格将不会根据区块填充的变化而变化,读者可以将合并单元格功能尝试用在 “固定表头” 的导出模板中就看到合并效果会有差异。

(四)创建带有预置行的导入模板

1、使用 setPresetNumber 方法创建预置空行

在第二小节示例代码的基础上,通过 setPresetNumber(10) 设置 10 行预置空行。示例代码如下所示:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "$A$1:$C$2")
                .setPresetNumber(10)
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(true)
                .createCell().setField("code").and()
                .createCell().setField("name").and()
                .createCell().setField("user.login").and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("编码").and()
                .createCell().setValue("名称").and()
                .createCell().setValue("用户账号").and()
                .and()
                .and()
                .and()
                .build());
    }
}

下载的导入模板 Excel 文件内容如下:

2、使用 createRow 方法创建自定义内容的预置行

除了预置空行之外,我们还可以通过 createRow 方法创建行并且设置对应值自定义预置行,这在具有示例填写的导入模板中是非常有意义的。示例代码如下所示:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "$A$1:$C$2")
                .setPresetNumber(10)
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(true)
                .createCell().setField("code").and()
                .createCell().setField("name").and()
                .createCell().setField("user.login").and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("编码").and()
                .createCell().setValue("名称").and()
                .createCell().setValue("用户账号").and()
                .and()
                .createRow()
                .createCell().setValue("这是编码").and()
                .createCell().setValue("这是名称").and()
                .createCell().setValue("这是用户账号").and()
                .and()
                .and()
                .and()
                .build());
    }
}

下载的导入模板 Excel 文件内容如下:

提示:

setPresetNumber 方法设置预置空数量是预置行的总数,如果手动创建了一行预置行,则在补充预置行时会自动补充不足的部分。比如:示例中预置行总数是 10 行,因为手动创建了一行预置行,则最后只补充了 9 行预置空行。

(五)开启自动列宽

在上面通过 createRow 创建自定义预置行的示例中,我们发现 “这是用户账号” 这一单元格的数据超出所在单元格了,那么,怎么解决这个问题呢?

我们可以通过 “自动列宽” 功能根据内容长度自动计算列宽来处理这个问题。让我们在对应的配置行字段通过 setAutoSizeColumn 方法开启这一列的 “自动列宽” 功能。示例代码如下所示:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "$A$1:$C$2")
                .setPresetNumber(9)
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(true)
                .createCell().setField("code").and()
                .createCell().setField("name").and()
                .createCell().setField("user.login").setAutoSizeColumn(true).and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("编码").and()
                .createCell().setValue("名称").and()
                .createCell().setValue("用户账号").and()
                .and()
                .createRow()
                .createCell().setValue("这是编码").and()
                .createCell().setValue("这是名称").and()
                .createCell().setValue("这是用户账号").and()
                .and()
                .and()
                .and()
                .build());
    }
}

下载的导入模板 Excel 文件内容如下:

提示:

在导出时,如果遇到数据特别长的情况下,自动列宽功能并不是特别万能的解决方案。这时候我们需要使用固定列宽来解决数据过长时展示异常的问题。

我们可以将 setAutoSizeColumn 改为下面这样:

setStyleBuilder(ExcelHelper.createDefaultStyle().setWidth(3000))

单位问题:POI 提供的宽度单位与 Excel 中通常使用的单位不同,在实际使用时可能会出现误差,需要自行调试。

(六)多级表头

示例代码如下所示:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "$A$1:$C$2")
                .createMergeRange("$A1:$B1")
                .setPresetNumber(10)
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(true)
                .createCell().setField("code").and()
                .createCell().setField("name").and()
                .createCell().setField("user.login").setAutoSizeColumn(true).and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("基础信息").and()
                .createCell().and()
                .createCell().setValue("用户信息").and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("编码").and()
                .createCell().setValue("名称").and()
                .createCell().setValue("用户账号").and()
                .and()
                .createRow()
                .createCell().setValue("这是编码").and()
                .createCell().setValue("这是名称").and()
                .createCell().setValue("这是用户账号").and()
                .and()
                .and()
                .and()
                .build());
    }
}

下载的导入模板 Excel 文件内容如下:

(七)混合格式

示例代码如下所示:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_FORMAT, ExcelDirectionEnum.HORIZONTAL, "$A$1:$D$2")
                .createMergeRange("$B$2:$D$2")
                .createHeader().setIsConfig(true)
                .createCell().setAutoSizeColumn(true).and()
                .createCell().setAutoSizeColumn(true).and()
                .createCell().setAutoSizeColumn(true).and()
                .createCell().setAutoSizeColumn(true).and()
                .and()
                .createRow().setStyleBuilder(ExcelHelper.createDefaultStyle())
                .createCell().setValue("编码").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("code").and()
                .createCell().setValue("名称").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("name").and()
                .and()
                .createRow().setStyleBuilder(ExcelHelper.createDefaultStyle())
                .createCell().setValue("用户账号").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("user.login").and()
                .createCell().and()
                .createCell().and()
                .and()
                .and()
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "$A$3:$D$4")
                .setPresetNumber(10)
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(true)
                .createCell().setField("partners[*].name").and()
                .createCell().setField("partners[*].partnerType").and()
                .createCell().setField("partners[*].phone").and()
                .createCell().setField("partners[*].email").and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("合作伙伴名称").and()
                .createCell().setValue("合作伙伴类型").and()
                .createCell().setValue("合作伙伴手机号").and()
                .createCell().setValue("合作伙伴邮箱").and()
                .and()
                .and()
                .and()
                .build());
    }
}

下载的导入模板 Excel 文件内容如下:

提示:

对于 Excel 行/列的设置属性,如果出现冲突的,则只会在最上方或最左边的配置行中生效。比如:示例中自动列宽属性配置在第一个区块的配置行的单元格中。

(八)创建多工作表模板

示例代码如下所示:

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(WorkbookDefinitionBuilder.newInstance(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createSheet("测试模型")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_FORMAT, ExcelDirectionEnum.HORIZONTAL, "$A$1:$D$2")
                .createMergeRange("$B$2:$D$2")
                .createHeader().setIsConfig(true)
                .createCell().and()
                .createCell().and()
                .createCell().and()
                .createCell().and()
                .and()
                .createRow().setStyleBuilder(ExcelHelper.createDefaultStyle())
                .createCell().setValue("编码").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("code").and()
                .createCell().setValue("名称").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("name").and()
                .and()
                .createRow().setStyleBuilder(ExcelHelper.createDefaultStyle())
                .createCell().setValue("用户账号").setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true))).and()
                .createCell().setField("user.login").and()
                .createCell().and()
                .createCell().and()
                .and()
                .and()
                .and()
                .createSheet("合作伙伴列表")
                .createBlock(TestModel.MODEL_MODEL, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, "$A$1:$D$2")
                .setPresetNumber(10)
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(true)
                .createCell().setField("partners[*].name").setAutoSizeColumn(true).and()
                .createCell().setField("partners[*].partnerType").setAutoSizeColumn(true).and()
                .createCell().setField("partners[*].phone").setAutoSizeColumn(true).and()
                .createCell().setField("partners[*].email").setAutoSizeColumn(true).and()
                .and()
                .createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)))
                .createCell().setValue("合作伙伴名称").and()
                .createCell().setValue("合作伙伴类型").and()
                .createCell().setValue("合作伙伴手机号").and()
                .createCell().setValue("合作伙伴邮箱").and()
                .and()
                .and()
                .and()
                .build());
    }
}

下载的导入模板 Excel 文件内容如下:

  • 测试模型工作表
  • 合作伙伴列表

五、自定义导入/导出逻辑

不论是导入逻辑还是导出逻辑,都是使用 “扩展点” 对其逻辑进行处理的,下面分别介绍导入扩展点和导出扩展点的一些基本用法和常见场景的处理方式。

扩展点使用 expression 属性配置表达式来决定在什么样的条件下执行对应的扩展点,表达式的用法可参考:函数 API - 表达式

(一)模型解释

  • 工作簿模型:在页面上选择模板时使用的模型编码。
  • 区块模型:实际导入/导出时使用的模型编码。

以 “使用 ExcelHelper 创建模板” 小节的示例代码为例:(为了区分不同模型,下面的代码稍做修改)

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(ExcelHelper.fixedHeader(TestModel1.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .createBlock("测试模型", TestModel2.MODEL_MODEL)
                .addColumn("code", "编码")
                .addColumn("name", "名称")
                .addColumn("user.login", "用户账号")
                .build());
    }
}

可以看到:

  • ExcelHelper#fixedHeader 方法的第一个入参为:TestModel1.MODEL_MODEL ,该模型编码为 “工作簿模型”,被记录在 ExcelWorkbookDefinition#model 字段中。
  • createBlock 方法的第二个入参为:TestModel2.MODEL_MODEL ,该模型编码为 “区块模型”,被记录在 ExcelBlockDefinition#bindingModel 字段中。

(二)自定义导入扩展点

按模板定义的名称使用导入扩展点是最基本的用法之一:

@Component
@Ext(ExcelImportTask.class)
public class TestModelImportExtPoint implements ExcelImportDataExtPoint<TestModel> {

    @ExtPoint.Implement(expression = "importContext.definitionContext.name==\"" + TestModelImportTemplate.TEMPLATE_NAME + "\"")
    @Override
    public Boolean importData(ExcelImportContext importContext, TestModel data) {
        // 自定义导入逻辑
        return true;
    }
}
  • @Ext(ExcelImportTask.class):导入扩展点固定参数注解。
  • ExcelImportDataExtPoint:导入扩展点接口定义。
  • ExcelImportContext:Excel 导入上下文,包含 Excel 模板定义等导入所需信息。
  • TestModel:同 “区块模型” 对应的 JAVA 类型。

提示:

表达式中,importContext 是方法参数名,通过 “.” 分隔的写法获取对象中的值。这是表达式的取值用法,扩展点的 expression 属性要求计算结果必须是 布尔(Boolean) 类型。示例中表达式可以表述为:当模板名称为 testModelImportTemplate 时执行当前扩展点。

1、数据验证

在数据验证过程中,有两种中断方式:

  • 通过 “抛出异常” 或 “return false” 进行中断,此时将不再继续读取数据。
  • 通过 return true 进行中断,此时将继续读取数据。

异常中断,不再进行数据导入:(推荐)

public Boolean importData(ExcelImportContext importContext, TestModel data) {
    String code = data.getCode();
    if (StringUtils.isBlank(code)) {
        throw new IllegalArgumentException("编码不允许为空");
    }
    // 其他处理逻辑
    return true;
}

提示:

这里的异常并不是直接与前端进行交互,可以使用任何 JAVA 内置异常。

“return false” 中断,添加错误提示信息,不再继续读取数据:

public Boolean importData(ExcelImportContext importContext, TestModel data) {
    String code = data.getCode();
    if (StringUtils.isBlank(code)) {
        importContext.getImportTask().addTaskMessage(TaskMessageLevelEnum.ERROR, "编码不允许为空");
        return false;
    }
    // 其他处理逻辑
    return true;
}

仅添加错误提示信息,继续读取数据:

public Boolean importData(ExcelImportContext importContext, TestModel data) {
    String code = data.getCode();
    if (StringUtils.isBlank(code)) {
        importContext.getImportTask().addTaskMessage(TaskMessageLevelEnum.ERROR, "编码不允许为空");
        return true;
    }
    // 其他处理逻辑
    return true;
}

2、逐行导入

若需要收集错误信息并生成对应的 Excel 错误文件,需要在模板中配置 eachImport = true 开启逐行导入功能。

@Component
public class TestModelImportTemplate implements ExcelTemplateInit {

    public static final String TEMPLATE_NAME = "testModelImportTemplate";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        return Collections.singletonList(ExcelHelper.fixedHeader(TestModel.MODEL_MODEL, TEMPLATE_NAME)
                .setDisplayName("测试模型导入")
                .setType(ExcelTemplateTypeEnum.IMPORT)
                .setEachImport(true)
                .createBlock("测试模型", TestModel.MODEL_MODEL)
                .addColumn("code", "编码")
                .addColumn("name", "名称")
                .addColumn("user.login", "用户账号")
                .build());
    }
}

提示:

启用逐行导入功能后,只有通过 “异常中断” 才会进行错误信息的收集,其他中断方式都不会生成 “导入错误文件”。

3、批量处理导入数据

对于导入性能有要求的业务场景,可以自行控制导入扩展点与数据库操作之间的访问频率。例如我们可以在每一行数据进入之后,先将其保存在 “读取上下文” 提供的 “数据缓冲区”,直到没有最后一行的时候对所有数据进行批量处理。示例代码如下:

public Boolean importData(ExcelImportContext importContext, TestModel data) {
    // 数据验证
    String code = data.getCode();
    if (StringUtils.isBlank(code)) {
        throw new IllegalArgumentException("编码不允许为空");
    }
    // 获取/创建缓存数据集
    List<TestModel> dataList = importContext.getDataBuffer(0, ArrayList::new);
    // 判断是否读取完成
    if (importContext.getCurrentListener().hasNext()) {
        dataList.add(data);
    } else {
        new TestModel().createOrUpdateBatch(dataList);
    }
    return true;
}

4、多区块导入数据处理

不论是一个工作表有多个区块,还是多个工作表每个工作表分别有一个区块,总的来说都是多区块处理。

针对每个区块,我们都有对应的区块模型以及模型编码对应的 JAVA 类型,我们可以根据 区块模型 或 区块索引 来区分这些具体的 JAVA 类型,以便于我们在代码中操作数据。示例代码如下:

public Boolean importData(ExcelImportContext importContext, Object data) {
    if (importContext.getCurrentBlockNumber() == 0) {
        TestModel1 testModel1 = (TestModel1) data;
        // 测试模型1处理
    } else if (importContext.getCurrentBlockNumber() == 1) {
        TestModel2 testModel2 = (TestModel2) data;
        // 测试模型2处理
    }
    // 其他处理逻辑
    return true;
}

(三)自定义导出扩展点

按模板定义的名称使用导出扩展点是最基本的用法之一:

@Component
@Ext(ExcelExportTask.class)
public class TestModelExportExtPoint extends ExcelExportSameQueryPageTemplate implements ExcelExportFetchDataExtPoint {

    @ExtPoint.Implement(expression = "context.name==\"" + TestModelExportTemplate.TEMPLATE_NAME + "\"")
    @Override
    public List<`Object`> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) {
        List<`Object`> dataList = super.fetchExportData(exportTask, context);
        // 自定义导出逻辑
        return dataList;
    }
}
  • @Ext(ExcelExportTask.class):导出扩展点固定参数注解。
  • ExcelExportFetchDataExtPoint:导出扩展点接口定义。
  • ExcelExportSameQueryPageTemplate:导出扩展点默认获取数据的默认实现,包含Hook 调用,与前端发起 queryPage 请求完全相同。
  • List<Object>:区块数据,按区块索引进行定义。关于返回值的问题,在下文中可以找到具体解释。

1、导出扩展点返回值

为了让导出扩展点的数据获取功能较为通用,其返回值表示区块数据。

假设我们有这样两个区块的模板:

  • 第一个区块为 “固定格式” 类型,其数据结构为 “对象(Object)”。
  • 第二个区块为 “固定表头” 类型,其数据结构为 “列表(List)”。

那么,其返回值的伪代码可以表示为:

public List<`Object`> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) {
    // 第一个区块数据
    TestModel data1 = new TestModel();
    // 第二个区块数据
    List<TestModel> data2 = new ArrayList<>();
    // 按区块定义顺序组合为列表
    return Lists.newArrayList(data1, data2);
}

2、导出时使用权限过滤

由于导出功能未经过前端请求,因此 Hook 功能没有被使用,在我们进行自定义数据获取时,需要使用元位指令 API 让 Hook 生效,更多关于 元位指令 API 的内容请参考:元位指令 API

下面这段代码示例展示了如何通过自定义数据获取达到与默认扩展点查询逻辑完全一致的情况:

@Component
@Ext(ExcelExportTask.class)
public class TestModelExportExtPoint implements ExcelExportFetchDataExtPoint {

    @ExtPoint.Implement(expression = "context.name==\"" + TestModelExportTemplate.TEMPLATE_NAME + "\"")
    @Override
    public List<`Object`> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) {
        // 查询逻辑需要通过元位指令包裹运行,否则 Hook 不生效
        List<TestModel> dataList = Models.directive().run(() -> {
            // 拼接传入的 RSQL 表达式,通常仅作用于第一个区块
            IWrapper<TestModel> wrapper = exportTask.temporaryRsql(exportTask.getWorkbookDefinition().getDomain(), () -> Optional.ofNullable(exportTask.getConditionWrapper())
                    .map(ConditionWrapper::<TestModel>generatorQueryWrapper)
                    .orElseGet(() -> Pops.<TestModel>query().ge(SqlConstants.ID, 0)));
            wrapper.setModel(TestModel.MODEL_MODEL);
            // 使用带拦截的查询方式
            return Models.data().queryListByWrapper(wrapper);
        }, SystemDirectiveEnum.BUILT_ACTION, SystemDirectiveEnum.HOOK);
        // 其他处理逻辑
        return Lists.newArrayList(dataList);
    }
}

警告:

和其他重写函数类似,一些内置的导出逻辑也会因为重写扩展点而不再生效。例如:数据集大小的校验、自动查询关联关系字段数据等内置功能。对于关联关系字段的查询操作,可以根据模板定义的内容自行决定是否需要查询。

提示:

如果查询逻辑需要自定义,仅实现 ExcelExportFetchDataExtPoint 接口即可。

如果在查询后需要进行计算,可通过 ExcelExportSameQueryPageTemplate 类辅助查询。辅助查询仅能用于第一个区块,

六、Reference List

(一)模型

1、Excel 工作簿(ExcelWorkbookDefinition)

字段名类型是否必选默认值描述
nameString是Excel 工作簿的定义名称
displayNameString是Excel 工作簿显示名称
filenameString否导出时使用的文件名,不指定则默认使用名称作为文件名
modelString是模型编码用于决定导入导出展示在哪个模型的 table 页,并不用于模型化操作
bindingViewNameString否当指定视图时,该模板仅在指定视图中展示
typeExcelTemplateTypeEnum是IMPORT_EXPORT模板类型
versionOfficeVersionEnum是AUTOOffice 版本,导入时根据文件名后缀自动识别,无后缀时需手动指定;导出时默认使用新版本
sheetListList<ExcelSheetDefinition>否工作表列表,当指定工作表索引时,有且仅有一个对象;否则根据 Excel 文件中的 sheet 个数按顺序排列
redirectUriString否下载导入模板重定向地址
sheetDefinitionsString是工作表定义 JSON 字符串
definitionContextString否缓存工作簿定义的解析内容
dataStatusDataStatusEnum是ENABLED数据状态
importStrategyExcelImportStrategyEnum否STANDARD导入策略
exportStrategyExcelExportStrategyEnum否STANDARD导出策略
hasErrorRollbackBoolean是false出现错误进行回滚
maxErrorLengthInteger是100最大错误数
clearExportStyleBoolean是false清除导出样式,使用 CSV 格式进行导出
excelMaxSupportLengthInteger否excel 格式导出最大支持行数
csvMaxSupportLengthInteger否csv 格式导出最大支持行数
templateSourceExcelTemplateSourceEnum否CUSTOM模板来源
locationsList<ExcelLocation>否国际化配置
defaultShowBoolean否默认是否显示
showBoolean否是否显示
eachImportBoolean否false逐行导入
domainString否默认过滤规则,rsql 表达式
excelImportModeExcelImportModeEnum否MULTI_MODEL导入模式
langString否模板所属语言

2、Excel 工作表(ExcelSheetDefinition)

字段名类型是否必选默认值描述
nameString否指定工作表名称,当未指定工作表名称时,默认使用模型的显示名称,未绑定模型生成时,默认使用「Sheet + ${index}」作为工作表名称
autoSizeColumnBoolean否true自动列宽
onceFetchDataBoolean否该属性仅在包含【固定格式】块时生效,并且所有块的绑定模型必须一致
blockDefinitionListList<ExcelBlockDefinition>是区块定义
mergeRangeListList<ExcelCellRangeDefinition>否单元格合并范围
uniqueDefinitionsList<ExcelUniqueDefinition>否唯一定义

3、Excel 区块(ExcelBlockDefinition)

字段名类型是否必选默认值描述
bindingModelString否在绑定模型后,可使用默认模板及模型解析功能
fetchNamespaceString否获取函数命名空间
fetchFunString否获取函数名称
domainString否默认过滤规则,rsql 表达式
analysisTypeExcelAnalysisTypeEnum是指定工作表所使用的解析类型,不同的解析类型所需的定义方式存在差异
directionExcelDirectionEnum是指定当前表头行的排列方向
designRangeExcelCellRangeDefinition是设计范围
usingCascadingStyleBoolean否将样式覆盖变为样式层叠;单个区块有效;优先级顺序为:表头行样式 < 数据行样式 < 单元格样式
presetNumberInteger否使用空的字符串填充数据行的单元格
headerListList<ExcelHeaderDefinition>否表头定义
rowListList<ExcelRowDefinition>否行定义
mergeRangeListList<ExcelCellRangeDefinition>否单元格合并范围
uniqueDefinitionsList<ExcelUniqueDefinition>否唯一定义

4、Excel 行(ExcelRowDefinition)

字段名类型是否必选默认值描述
cellListList<ExcelCellDefinition>否单元格列表
styleExcelStyleDefinition否在一行中默认使用全局样式,若设置单元格样式,则覆盖全局样式;在表头行中,水平表头行设置整列样式;垂直表头行设置整行样式;若表头行和数据行同时设置样式,则已数据行的样式为全局样式

5、Excel 表头行(ExcelHeaderDefinition)

继承:ExcelRowDefinition

字段名类型是否必选默认值描述
isConfigBoolean否false配置行不参与计算,且导出时自动忽略;配置行指定的应用范围必须是需要配置的表头范围
isFrozenBoolean否false冻结功能仅在非配置行且非隐藏行中生效

6、Excel 单元格(ExcelCellDefinition)

字段名类型是否必选默认值描述
fieldString否单元格属性定义 固定表头:仅在配置行中生效 固定格式:在任意单元格中生效
valueString否单元格的值
typeExcelValueTypeEnum否值类型
formatString否格式化方式(参考用户手册)
translateBoolean否是否翻译 默认情况下,静态值、枚举和布尔字段会自动翻译,其他情况根据指定值处理
isStaticBoolean否false是否是静态值 静态值在数据解析时将使用配置值,不读取单元格的值
isFieldValueBoolean否标记该单元格的内容为属性值
autoSizeColumnBoolean否true是否自动列宽
styleExcelStyleDefinition否单元格样式
styleCacheCellStyle( transient )否样式缓存(仅运行时使用,序列化时忽略)

7、Excel 唯一定义(ExcelUniqueDefinition)

字段名类型是否必选默认值描述
modelString是关联的模型编码
uniquesList<String>是唯一属性列表 多个属性组合构成唯一标识

8、Excel 样式(ExcelStyleDefinition)

字段名类型是否必选默认值描述
horizontalAlignmentExcelHorizontalAlignmentEnum否GENERAL水平对齐方式
verticalAlignmentExcelVerticalAlignmentEnum否垂直对齐方式
fillBorderStyleExcelBorderStyleEnum否全边框样式(同时设置上下左右边框)
topBorderStyleExcelBorderStyleEnum否上边框样式
rightBorderStyleExcelBorderStyleEnum否右边框样式
bottomBorderStyleExcelBorderStyleEnum否下边框样式
leftBorderStyleExcelBorderStyleEnum否左边框样式
fillBorderColorInteger(RGB 色值)否全边框颜色(同时设置上下左右边框颜色)
topBorderColorInteger(RGB 色值)否上边框颜色
rightBorderColorInteger(RGB 色值)否右边框颜色
bottomBorderColorInteger(RGB 色值)否下边框颜色
leftBorderColorInteger(RGB 色值)否左边框颜色
fillPatternTypeExcelFillPatternTypeEnum否背景填充类型(纯色 / 网点等)
backgroundColorInteger(RGB 色值)否背景色
foregroundColorInteger(RGB 色值)否前景色(图案颜色)
wrapTextBoolean否false是否自动换行
shrinkToFitBoolean否false是否自动收缩文本以适应单元格宽度
widthInteger否单元格宽度(仅首行有效,单位:列宽单位)
heightInteger否单元格高度(仅首列有效,单位:行高单位)
typefaceDefinitionExcelTypefaceDefinition否字体定义(包含字体名称、大小、加粗、斜体等属性)
styleCacheCellStyle(transient)否样式缓存(仅运行时使用,序列化时忽略,自动管理 Workbook 样式对象)

9、Excel 字体(ExcelTypefaceDefinition)

字段名类型是否必选默认值描述
typefaceExcelTypefaceEnum否SONG字体类型(如宋体、黑体)
sizeInteger否11字体大小(单位:磅)
italicBoolean否false是否斜体
strikeoutBoolean否false是否添加删除线
colorInteger(IndexedColors索引值)否0xfff字体颜色(如IndexedColors.RED.getIndex()为红色)
typeOffsetExcelTypeOffsetEnum否NORMAL字符偏移类型(正常 / 上标 / 下标)
underlineExcelUnderlineEnum否NONE下划线类型(无 / 单下划线 / 双下划线等)
boldBoolean否false是否加粗

10、Excel 翻译(ExcelLocation)

字段名类型是否必选默认值描述
modelString是关联的模型编码(用于定位模板所属模型)
nameString是模板名称(对应 ExcelWorkbookDefinition 的 name 字段)
langString是语言标识(如zh-CN
、en-US
)
locationItemsList<ExcelLocationItem>否国际化配置项列表 存储各语言下的文本映射,支持 JSON 格式序列化

11、Excel 翻译项(ExcelLocationItem)

字段名类型是否必选默认值描述
originString是原始文本(未翻译的值)
targetString是翻译后的目标文本(对应语言的值)

12、抽象 Excel 任务(AbstractExcelTask)

字段名类型是否必选默认值描述
nameString是任务名称
workbookDefinitionExcelWorkbookDefinition是Excel 工作簿定义对象(关联实体)
workbookDefinitionIdLong是Excel 工作簿定义 ID(数据库存储字段)
workbookNameString否Excel 工作簿名称(冗余字段,方便快速查询)
stateExcelTaskStateEnum是任务状态(待处理 / 运行中 / 已完成 / 失败等)
messagesList<TaskMessage>否任务信息列表 存储任务执行中的日志、错误信息,支持 JSON 格式序列化
moduleString否模块编码(标识操作所属模块,如order
、customer
)
moduleDefinitionModuleDefinition否所属应用对象(关联实体,通过 module 字段关联)
createUserNameString否创建人名称(非持久化字段,通过关联用户信息获取)
writeUserNameString否修改人名称(非持久化字段,通过关联用户信息获取)
modelString否模型编码(标识操作对应的业务模型,如com.example.Order
)

13、导入任务(ExcelImportTask)

字段名类型是否必选默认值描述
filePamirsFile是导入文件对象(关联文件存储系统实体,需提前上传文件)
eachImportBoolean否是否启用逐行导入模式 启用后将逐行处理数据,失败行可生成错误文件
hasErrorRollbackBoolean否出现错误时是否回滚已导入数据 需与 maxErrorLength 配合使用
maxErrorLengthInteger否允许的最大错误数 超过该数量时任务终止(默认继承基类配置)
errorFilePamirsFile否导入失败文件(逐行模式下生成,包含错误行数据)
importDataListList<String>否导入数据列表(内存存储,用于临时数据传递,不存入数据库)
readCallbackListList<ExcelReadCallback>否读取回调列表(运行时注册的回调函数,序列化时忽略)

14、导出任务(ExcelExportTask)

字段名类型是否必选默认值描述
filePamirsFile否导出文件对象(关联文件存储系统实体)
fileTypeExcelExportFileTypeEnum否导出文件类型(Excel 或 CSV 格式)
conditionWrapperConditionWrapper否查询条件包装器 支持 RSQL 表达式及动态条件组合,非持久化存储
rsqlString否RSQL 过滤条件 自动同步 conditionWrapper 中的 rsql 字段,方便快速查询
syncBoolean否false是否同步执行导出任务 同步模式下将阻塞等待导出完成,异步模式返回任务 ID
exportMethodExcelExportMethodEnum否TEMPLATE导出方法 TEMPLATE:基于模板导出;DIRECT:直接导出数据
selectedFieldsList<ModelField>否选择字段列表 当 exportMethod 为 DIRECT 时,指定要导出的模型字段
requestIdString否单次请求 ID 同步下载时用于标识 Redis 中存储的临时数据

15、任务消息(TaskMessage)

继承:TransientModel

字段名类型是否必选默认值描述
idLong否消息唯一标识(可选,用于分页查询或定位特定消息)
levelTaskMessageLevelEnum是消息级别 ERROR:错误;WARNING:警告;INFO:提示
recordDateDate否消息记录时间(自动生成,精确到毫秒)
rowIndexInteger否关联的 Excel 行号(导入任务中用于定位错误行,从 1 开始计数)
messageString是消息内容 支持国际化翻译(translate=true
)

(二)枚举

1、模板类型(ExcelTemplateTypeEnum)

值显示名称描述
IMPORT_EXPORT全部全部
IMPORT导入仅用作导入
EXPORT导出仅用作导出

2、Office 版本(OfficeVersionEnum)

值显示名称描述对应 Excel 类型
AUTO自动识别自动识别文件类型,无法识别时会出错XLSX
OLD旧版指 2003 年及之前发行的 office 版本,使用旧的文件后缀XLS
NEW新版指 2003 年之后发行的 office 版本,使用新的文件后缀XLSX

3、数据状态(DataStatusEnum)

值显示名称描述
DRAFT草稿草稿状态
NOT_ENABLED未启用未启用状态
ENABLED已启用已启用状态
DISABLED已禁用已禁用状态

4、导入策略(ExcelImportStrategyEnum)

值显示名称描述
STANDARD标准模式标准模式(默认),开发人员自行控制所有导入过程
EACH逐行导入自动收集导入错误并生成错误文件,适合大数据量分步处理
ALL全部成功自动开启导入事务,出现任何错误立即中断并回滚,确保数据一致性
ALL_EACH全部成功并收集错误自动开启导入事务,同时收集导入错误生成错误文件,兼顾一致性和错误定位

5、导出策略(ExcelExportStrategyEnum)

值显示名称描述
STANDARD默认获取默认使用单个扩展点获取整个工作簿的全部数据(适合小数据量场景)
SINGLE单一函数获取使用工作簿中定义的函数完整获取整个工作簿的全部数据(统一数据源)
BLOCK多函数获取使用每个块定义的函数分别获取对应块中的数据(适合多数据源分块场景)
STREAM流式获取通过分页获取每个块中的数据,数据获取与填充交替执行(优化内存占用)

6、模板来源(ExcelTemplateSourceEnum)

值显示名称描述
SYSTEM系统生成当前模型不存在模板时通过系统自动生成,创建新模板后自动删除,编辑后保留
INITIALIZATION初始化生成系统初始化时创建的模板,不允许编辑或删除
CUSTOM自定义由用户手动创建或编辑的模板

7、导入模式(ExcelImportModeEnum)

值显示名称描述
MULTI_MODEL多模型一个模板对应多个不同的 Sheet,默认模式
SINGLE_MODEL单模型所有 Sheet 共用同一个数据模型

8、解析类型(ExcelAnalysisTypeEnum)

值显示名称描述
FIXED_HEADER固定表头固定表头
FIXED_FORMAT固定格式固定格式

9、排列方向(ExcelDirectionEnum)

值显示名称描述对应 EasyExcel 写入方向
HORIZONTAL水平排列子元素水平排列,垂直填充VERTICAL
VERTICAL垂直排列子元素垂直排列,水平填充HORIZONTAL

10、值类型(ExcelValueTypeEnum)

值显示名称描述默认格式(示例)
STRING文本普通文本-
INTEGER整数整数数值0
NUMBER数字带小数的数字0.00
DATETIME日期 + 时间日期时间组合yyyy-MM-dd HH:mm:ss
(对应DATETIME
格式)
FORMULA公式Excel 公式-
BOOLEAN布尔布尔值(是 / 否){"true":"是","false":"否"}
CALENDAR日历日期类型-
COMMENT备注单元格备注-
HYPER_LINK超链接超链接地址-
RICH_TEXT_STRING富文本富文本内容-
ENUMERATION枚举枚举值-
BIT二进制枚举二进制枚举值-
OBJECT对象复杂对象-

11、水平对齐方式(ExcelHorizontalAlignmentEnum)

值显示名称描述对应 POI 枚举值
GENERAL默认文本居左;数字、日期和时间居右;布尔居中HorizontalAlignment.GENERAL
LEFT左对齐内容左对齐HorizontalAlignment.LEFT
CENTER居中对齐内容居中对齐HorizontalAlignment.CENTER
RIGHT右对齐内容右对齐HorizontalAlignment.RIGHT
FILL填充对齐内容重复填充以适应单元格宽度HorizontalAlignment.FILL
JUSTIFY左右对齐内容两端对齐,自动调整字间距(适用于多行文本)HorizontalAlignment.JUSTIFY
CENTER_SELECTION居中选择对齐内容在选定区域内居中(需配合单元格合并使用)HorizontalAlignment.CENTER_SELECTION
DISTRIBUTED分散对齐内容均匀分布,两端与单元格边界对齐HorizontalAlignment.DISTRIBUTED

12、垂直对齐方式(ExcelVerticalAlignmentEnum)

值显示名称描述对应 POI 枚举值
TOP顶部对齐内容与单元格顶部对齐VerticalAlignment.TOP
CENTER居中对齐内容垂直居中对齐VerticalAlignment.CENTER
BOTTOM底部对齐内容与单元格底部对齐VerticalAlignment.BOTTOM
JUSTIFY上下对齐内容两端对齐,自动调整行间距(适用于多行文本)VerticalAlignment.JUSTIFY
DISTRIBUTED分散对齐内容均匀分布,上下与单元格边界对齐VerticalAlignment.DISTRIBUTED

13、边框样式(ExcelBorderStyleEnum)

值显示名称描述对应 POI 枚举值
NONE无边框无边框BorderStyle.NONE
THIN细边框细实线边框BorderStyle.THIN
MEDIUM常规边框中等粗细实线边框BorderStyle.MEDIUM
THICK粗边框粗实线边框BorderStyle.THICK
DASHED虚线边框虚线边框BorderStyle.DASHED
DOTTED点线边框点线边框BorderStyle.DOTTED
DOUBLE双线边框双实线边框BorderStyle.DOUBLE
HAIR发线边框极细实线边框(发丝状)BorderStyle.HAIR
MEDIUM_DASHED中虚线边框中等虚线边框BorderStyle.MEDIUM_DASHED
DASH_DOT点划线边框点 - 划虚线边框BorderStyle.DASH_DOT
MEDIUM_DASH_DOT中划线点边框中等点 - 划虚线边框BorderStyle.MEDIUM_DASH_DOT
DASH_DOT_DOT点 - 点 - 点边框点 - 点 - 划虚线边框BorderStyle.DASH_DOT_DOT
MEDIUM_DASH_DOT_DOT中短划线 - 点 - 点边框中等点 - 点 - 划虚线边框BorderStyle.MEDIUM_DASH_DOT_DOT
SLANTED_DASH_DOT斜线点边框倾斜点 - 划虚线边框BorderStyle.SLANTED_DASH_DOT

14、填充类型(ExcelFillPatternTypeEnum)

值显示名称描述对应 POI 枚举值
NO_FILL不填充无背景填充FillPatternType.NO_FILL
SOLID_FOREGROUND纯色填充单一颜色背景填充FillPatternType.SOLID_FOREGROUND
FINE_DOTS细点填充细密点状背景填充FillPatternType.FINE_DOTS
ALT_BARS交替条纹填充横向交替条纹背景填充FillPatternType.ALT_BARS
SPARSE_DOTS稀疏点填充稀疏点状背景填充FillPatternType.SPARSE_DOTS
THICK_HORZ_BANDS粗横向条纹填充粗横向条纹背景填充FillPatternType.THICK_HORZ_BANDS
THICK_VERT_BANDS粗纵向条纹填充粗纵向条纹背景填充FillPatternType.THICK_VERT_BANDS
THICK_BACKWARD_DIAG粗斜线向后填充粗斜线(从左上到右下)背景填充FillPatternType.THICK_BACKWARD_DIAG
THICK_FORWARD_DIAG粗斜线向前填充粗斜线(从右上到左下)背景填充FillPatternType.THICK_FORWARD_DIAG
BIG_SPOTS大点填充大尺寸点状背景填充FillPatternType.BIG_SPOTS
BRICKS砖块填充砖块纹理背景填充FillPatternType.BRICKS
THIN_HORZ_BANDS细横向条纹填充细横向条纹背景填充FillPatternType.THIN_HORZ_BANDS
THIN_VERT_BANDS细纵向条纹填充细纵向条纹背景填充FillPatternType.THIN_VERT_BANDS
THIN_BACKWARD_DIAG细斜线向后填充细斜线(从左上到右下)背景填充FillPatternType.THIN_BACKWARD_DIAG
THIN_FORWARD_DIAG细斜线向前填充细斜线(从右上到左下)背景填充FillPatternType.THIN_FORWARD_DIAG

15、字体(ExcelTypefaceEnum)

值显示名称描述
SONG宋体宋体(中文常用正文字体)
REGULAR_SCRIPT楷体楷体(手写风格字体)
BOLDFACE黑体黑体(粗体无衬线字体)
YAHEIMicrosoft YaHei微软雅黑(清晰易读的无衬线字体)

16、字符偏移类型(ExcelTypeOffsetEnum)

值显示名称描述对应 POI 值
NORMAL常规正常显示Font.SS_NONE(0)
SUPER上标文字偏上显示Font.SS_SUPER(1)
SUB下标文字偏下显示Font.SS_SUB(2)

17、下划线类型(ExcelUnderlineEnum)

值显示名称描述对应 POI 值
NONE无无下划线Font.U_NONE(0)
SINGLE单下划线单实线下划线Font.U_SINGLE(1)
DOUBLE双下划线双实线下划线Font.U_DOUBLE(2)
SINGLE_ACCOUNTING会计风格单下划线适配会计报表的单下划线(与单元格等宽)Font.U_SINGLE_ACCOUNTING(3)
DOUBLE_ACCOUNTING会计风格双下划线适配会计报表的双下划线(与单元格等宽)Font.U_DOUBLE_ACCOUNTING(4)

18、任务状态(ExcelTaskStateEnum)

值显示名称描述
PROCESSING处理中任务正在执行中(进行数据读写或转换)
SUCCESS成功任务已成功完成(所有数据处理完毕且无错误)
FAILURE失败任务执行失败(因错误终止,需检查日志或错误文件)

19、导出文件类型(ExcelExportFileTypeEnum)

值显示名称描述
EXCELExcel 格式导出 Excel 格式文件(.xlsx)
CSVCSV 格式导出 CSV 格式文件(逗号分隔值)

20、导出方式(ExcelExportMethodEnum)

值显示名称描述
TEMPLATE根据模板导出使用预定义的 Excel 模板进行数据填充导出
SELECT_TEMPLATE_FIELD根据模板选择字段导出基于模板选择部分字段进行数据过滤后导出
SELECT_FIELD根据模型选择字段导出直接从数据模型中选择字段生成 Excel 导出

21、消息级别(TaskMessageLevelEnum)

值显示名称描述处理逻辑
TIP提示控制台输出的常规提示信息仅控制台打印,不存储
INFO信息任务执行的详细信息存储到信息列表,用于追溯
WARNING警告非阻塞性警告(不影响任务主流程)不回滚,需人工关注
ERROR异常导致任务失败的错误信息触发回滚,终止任务执行
编辑此页
最近更新:2026/1/14 08:45
上一页
工作流(Workflow)
下一页
资源 API(Resources API)
默认页脚
Copyright © 2026 Mr.Hope