跳转到内容

模型导入导出开发和使用指南

概念理解

示例代码

简介

模型数据的导入和导出是业务系统中一个很常用的功能。通常在业务系统中,模型数据的导入需要完成文件的上传、文件数据读取、转化为业务模型数据、模型数据校验、数据入库、以及导入结果统计和展示等;模型数据的导出则需要完成数据的查询、数据写入文件、以及导出结果的统计和展示等。
但是业务系统中一般有很多模型数据需要支持导入导出,在开发过程中会产生很多相似的重复代码。因为 Trantor 管理了业务模型的所有元信息,可以将模型导入导出过程中一些重复工作在框架层处理掉。对于业务开发人员,模型导入时主要关注于模型数据的校验和存储,模型导出时主要关注于模型数据的获取,从而节省了开发人员的开发时间和维护成本。

工作流程

对于业务人员,业务操作主要发生在统一工作台。在导入业务数据时,只需要选择需要导入的数据文件,然后查看导入结果;在导出业务数据时,可以根据系统预置的模板导出,或者自定义导出配置需要导出的列,然后下载查看导出文件。对于业务开发人员,需要提前在交付控制台中完成导入导出模板的配置,然后发布到运行环境中,业务人员才能使用导入导出功能。

Trantor 模型数据导入导出,一般有以下工作流程:

  1. 配置模型支持导入导出;
  2. 编写模型数据导入和导出业务函数;
  3. 在交付控制台中配置导入导出模板
  4. 发布导入导出模板所属业务域到运行环境;
  5. 在统一工作台业务操作页面中查看是否生效,业务人员使用导入导出功能,可以创建自定义导出模板,以及查看导入导出结果。

模型导入导出配置

业务开发人员只有在模型类中开启了导入导出功能,才能使用导入和导出功能。可以通过下面方式:

  • 实体模型
@Model(
config = @ModelConfig(
enableImport = true,
enableExport = true
)
)
  • 瞬时模型

    @TransientModel(
    config = @ModelConfig(
    enableImport = true,
    enableExport = true
    )
    )
  • 搜索模型

@SearchModel
@Model(
config = @ModelConfig(
enableImport = true,
enableExport = true
)
)

对于实体模型,一般建议支持导入导出的至少配置一个唯一索引,比如商品编码、物流运单号等,强烈不建议把 ID 透露给终端用户。唯一索引字段一般用来判断导入的数据行是进行插入或者更新操作。如果模型上配置了唯一索引,在配置导入导出模板时,用户无法选择 ID 字段,即不透出 ID 字段给终端用户,否则用户可以选择 ID 字段。例如:

@Model(
name = "商品",
mainField = ItemBO.itemName_field,
indexes = {
@Index(name = "item_code_index",columns ={ItemBO.itemCode_field}, unique = true)
},
config = @ModelConfig(
enableImport = true,
enableExport = true
)
)
public class ItemBO extends BaseModel<Long>{
}

另外需要注意的是:

  • 对于拓展模型(或者二开模型),需要在原模型上配置支持导入导出。

  • 如果模型在导入或导出时,需要包含关联模型数据,那么关联模型也需要配置支持导入导出。

模型配置了支持导入或导出后,上报所在业务域到研发态后,可以在模型详情页可以查看到开启了导入或导出。如下图所示:

image-20210708153401911

导入导出函数开发

模型导入导出结合了 Trantor 提供的逻辑流可编排服务机制,导入导出函数可以是逻辑流或者可编排服务,只是对函数的入参和出参进行了约束,具体规则和使用方法如下文所述。

模型数据导入函数

使用方式一(不推荐)

对于业务开发人员,在模型数据导入函数中需要完成模型数据的校验、数据的存储,以及操作结果返回。在 Trantor 框架层完成了 Excel 数据的读取,模型数据去重、关联模型数据合并,以及导入结果统计和展示。

  • 模型导入函数的入参只能有一个。如果单条数据导入,入参是模型,出参也是模型;如果批量数据导入,入参是模型列表,出参也是模型列表;
  • 导入函数一般需要开启事务,即在 execute 方法上添加注解@DSTransaction,导入失败时抛出 BusinessException;
  • 出参模型数据的 ID 需要赋值,否则认为数据未导入成功

例如:

  • 单个模型数据导入
@Function(name = "商品创建函数")
public interface ItemCreateFun {
ItemBO execute(ItemBO item);
}
@FunctionImpl(name = "商品创建函数实现类")
public class ItemCreateFunImpl implements ItemCreateFun {
@Override
public ItemBO execute(ItemBO item) {
IntResult result = DS.create(item);
item.setId(result.getValue().longValue());
return item;
}
}
  • 批量数据导入
@Flow(name = "商品批量创建流程")
public interface ItemListCreateFlow {
List<ItemBO> execute(List<ItemBO> itemList);
}
@FlowImpl(name = "商品批量创建实现类")
public class ItemListCreateFlowImpl implements ItemListCreateFlow {
@Override
@DSTransaction
public List<ItemBO> execute(List<ItemBO> itemList) {
List<IntResult> ids = DS.create(items);
for (int i = 0; i < items.size(); i++) {
items.get(i).setId(ids.get(i).getValue().longValue());
}
return items;
}
}

在批量数据导入时,建议调用 DS.create(List<Model>)方法,而不是循环调用 DS.create(Model) ,因为这样导入时会耗费更多的时间。根据测试结果,保存 1 万条模型数据保存,批量保存保存只需耗用大概 8 秒时间,但是循环调用单个导入会好用半个多小时时间。无论是单个模型导入,还是批量导入,都需要在模型创建或者更新成功后,设置其 ID 值。

使用方式一存在以下问题,已经不推荐使用,建议升级 framework 到 0.17.94.RELEASE 版本使用下面的使用方式二。

1.模型数据导入成功的依据是根据是否返回数据ID,而不是业务侧返回成功失败的标识;
2.模型批量导入如果有任何一条数据导入失败时,框架层退化成单条循环导入,存在很明显的性能问题。框架层这样处理的目的是确保没有问题的数据都能导入,存在问题的数据都不能导入。

使用方式二(推荐)

  • 模型导入函数的入参只能有一个。如果单条数据导入,入参是模型,出参类型是 ModelImportResult;如果批量数据导入,入参是模型列表,出参类型是 List
  • 导入函数可以不开启事务,导入失败时建议主动捕获异常,否则框架层捕获异常后会退化成单条循环导入。

例如:

  • 单个模型数据导入

    @Override
    @DSTransaction
    public ModelImportResult execute(ItemBO item) {
    ModelImportResult importResult = new ModelImportResult();
    //单条导入时,非必须设置数据索引
    importResult.setIndex(0);
    if(StringUtils.isEmpty(item.getItemCode())){
    importResult.setException(new BusinessException("item code is null"));
    //导入失败时,如果显示有异常消息,非必要设置导入状态。
    importResult.setStatus(ModelImportResult.Status.FAILED);
    return importResult;
    }
    IntResult idResult = DS.create(item);
    if (idResult != null && idResult.getValue() != null) {
    importResult.setStatus(ModelImportResult.Status.FAILED);
    //非必须设置ID
    importResult.setId(idResult.getValue().toString());
    }
    return importResult;
    }
  • 批量数据导入

@FunctionImpl(name = "商品批量创建函数")
public class ItemListCreateFunImpl implements ItemListCreateFunc {
@Override
public List<ModelImportResult> execute(List<ItemBO> items) {
List<ModelImportResult> importResults = new ArrayList<>();
List<ItemBO> errorItems = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
if(StringUtils.isEmpty(items.get(i).getItemCode())){
errorItems.add(items.get(i));
ModelImportResult importResult = new ModelImportResult();
importResult.setIndex(i);
importResult.setException(new BusinessException("item code is null"));
importResults.add(importResult);
}
}
// 剩下的需要导入的模型数据
// 不能通过 items.removeAll(errorItems), 后面会获取不到模型在原始集合的索引
List<ItemBO> toImportItems = items.stream().filter(item-> !errorItems.contains(item)).collect(Collectors.toList());
if(ImportFailStrategy.Terminate.equals(ModelImportContext.getFailStrategy())|| CollectionUtils.isEmpty(toImportItems)){
return importResults;
}
List<IntResult> intResults = DS.create(toImportItems);
for (int j = 0; j < toImportItems.size(); j++) {
//非必须设置ID
toImportItems.get(j).setId(intResults.get(j).getValue().longValue());
ModelImportResult importResult = new ModelImportResult();
importResult.setIndex(items.indexOf(toImportItems.get(j)));
importResult.setStatus(ModelImportResult.Status.SUCCEED);
importResults.add(importResult);
}
return importResults;
}
}

对于批量模型数据导入,如果业务函数的只返回部分主模型的导入结果数据,框架层会过滤掉已经处理的主模型数据,将剩下的模型数据继续提交到业务导入导出函数。业务如果需要先检查数据的有效性,可以立即返回存在问题的模型数据的导入结果。例如:

@FunctionImpl(name = "商品批量创建函数")
public class ItemListCreateFunImpl implements ItemListCreateFunc {
@Override
public List<ModelImportResult> execute(List<ItemBO> items) {
List<ModelImportResult> importResults = new ArrayList<>();
List<ItemBO> errorItems = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
if(StringUtils.isEmpty(items.get(i).getItemCode())){
errorItems.add(items.get(i));
ModelImportResult importResult = new ModelImportResult();
importResult.setIndex(i);
importResult.setException(new BusinessException("item code is null"));
importResults.add(importResult);
}
}
if(CollectionUtils.isNotEmpty(errorItems)){
return importResults;
}
List<IntResult> intResults = DS.create(items);
for (int j = 0; j < items.size(); j++) {
//非必须设置ID
items.get(j).setId(intResults.get(j).getValue().longValue());
ModelImportResult importResult = new ModelImportResult();
importResult.setIndex(items.indexOf(items.get(j)));
importResult.setStatus(ModelImportResult.Status.SUCCEED);
importResults.add(importResult);
}
return importResults;
}
}

模型数据导出函数

对于业务开发人员,在模型数据导出函数中需要根据查询条件获取模型数据。在 Trantor 框架层完成模型数据的分页提取、将导出函数返回的模型数据写入 Excel,以及导出结果统计和展示。

  • 模型导出的数据源可以和列表视图获取数据的方式一致。这种情况下业务开发人员可以不用额外编写模型导出函数。
  • 模型导出函数的入参只能有一个,且必须是导出模型的查询模型,出参为 List<模型>或者 Paging<模型>,导出失败时抛出 BusinessException。

例如:

  • 返回值是 List<模型>
@Flow(name = "商品列表数据")
public interface ItemListDataFlow {
List<ItemBO> execute(QItemBO itemBo);
}
@FlowImpl(name = "商品列表数据")
public class ItemListDataFlowImpl implements ItemListDataFlow {
@Autowired
private ItemCreateFun itemCreateFun;
@Override
public List<ItemBO> execute(QItemBO itemBo) {
//查询所有记录
List<ItemBO> result = DS.findAll(itemBo);
return result;
}
}

返回值是 List<模型>时,返回的数据不能太多,建议小于 500 条数据,否则可能处理的时候占用较高的 CPU 时间和内存空间。

  • 返回值是 Paging <模型>
@Function(name = "商品列表数据")
public interface ItemPagingDataFunc {
Paging<ItemBO> execute(QItemBO itemBo);
}
@FunctionImpl(name = "商品数据分页获取函数")
public class ItemPagingDataFunctionImpl implements ItemPagingDataFunc {
@Override
public Paging<ItemBO> execute(QItemBO itemBo) {
//分页查询所有记录
Paging<ItemBO> result = DS.paging(itemBo);
return result;
}
}

返回值是 Paging<模型>时,框架层会分页提取模型数据,直到获取到所有满足查询条件的所有数据。

  • 基于 SO 模型的模型数据导出

SO 模型的数据是从 ES 中间中查询出来的,默认加载的数据大小是 5 万。如果希望导出的数据量超过 5 万,需要滚动数据查询窗口。

@Override
public Paging<ItemSO> execute(QItemSO itemSo) {
SearchQuery searchQuery = new SearchQuery();
searchQuery.where(a -> a.bool().must(a.matchAllQuery()))
.from(itemSo.getQueryParams().getPage().getNo())
.size(itemSo.getQueryParams().getPage().getSize())
.setScrollRequest(ScrollRequest.builder()
.openScroll(true)
.scrollWaitTime(1) // scroll_id超时时间,单位分钟
.build());
if(!StringUtils.isNullOrEmpty(ModelImportContext.getScrollId())){
//设置当前查询的窗口ID
searchQuery.setScrollRequest(ScrollRequest.builder()
.openScroll(true)
.scrollWaitTime(1) // scroll_id超时时间,单位分钟
.scrollId(ModelImportContext.getScrollId())
.build());
}
SearchResult<ItemSO> searchResult = Search.scroll(ItemSO.class, searchQuery);
if(!StringUtils.isNullOrEmpty(searchResult.getScrollId())){
//将查询结果中的窗口ID放入上下文
ModelImportContext.setScrollId(searchResult.getScrollId());
}
//分页查询数据
Paging<ItemSO> result = new Paging<>();
result.setTotal(searchResult.getTotal());
result.setData(searchResult.getHits());
return result;
}

数据模板配置和发布

业务开发人员需要在交付控制台上配置模型的导入或者导出模板,并且随着业务域一起发布到运行环境后,才能在统一工作台中使用模型的导入导出功能。

模型的导入导出模板需要配置模型、导入或导出函数、绑定的视图和容器、以及模型字段。另外在配置模板时,可以设置字段在 Excel 中的显示顺序、是否必填、示例数据,以及字段的展示名称(或别名)。

其中:

  • 导入或导出函数参考前面章节介绍。

  • 导入导出模板需要绑定到具体的视图和容器。

    绑定的视图即模板需要显示在哪个页面上,模板中配置的视图只能是列表详情类型的视图。因为视图的主字段(mainField)是 name 属性,默认是视图文件的名称,建议设置视图的 name 属性值。

    容器标识即视图内具体的布局容器的 key,在当前页面中唯一,目前只能是Table容器,且 table 容器必须开启 advanced 属性。因为一个视图页面可能有多个列表容器需要支持导入导出,所以创建模板时需要指定容器标识。

    例如:视图 ItemList.view.xml

    <View title="商品列表" name="商品列表" forModel="trantor_import_export_demo_ItemBO" type="List" menuView="true" isDefault="true" version="2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="https://trantor-docs-dev.app.terminus.io/static/v0.16.x/schema/base.xsd">
    <Table key="table" model="trantor_import_export_demo_ItemBO" advanced="#{true}" dataCondition="">
    </Table>
    </View>

    则配置的视图标识为 trantor_import_export_demo_ItemBO_ItemList,容器标识是 table。

  • 模型字段即模板中包含的模型字段。

导入模板配置

导入模板的配置主要包含模板基本信息的设置、选择字段、以及设置字段排序。

在模板的基本信息中,模板除了设置模板名称和描述信息以外,还需要设置绑定的模型、视图和容器,以及导入数据的业务函数。

选择字段最多支持主子孙三级模型字段,更多层级字段不会显示;不显示图片、附件以及地址类型字段;关联模型字段如果没有开启支持导入,也不显示。如果模型定义了唯一索引字段,ID 字段不会显示,并在勾选同级字段时会默认被选中。

在设置字段排序时,用鼠标拖拽列头的方块移动改变字段的排列顺序。另外在最后一列可以设置字段的展示名称(或别名),在模板 Excel 文件的列头会显示别名,如果没有设置别名,直接显示的字段在模型中的名称。

port1

导出模板配置

导出模板的配置和导入模板的配置基本一致。但是因为导出获取数据的方式可以和列表视图一致,所有可以选择使用“界面数据源”,当然也可以选择“自定义数据源”,并设置具体的业务函数。

export

数据模板发布

系统内置的导入导出模板在交付控制台制品下配置,需要发布到运行态后,才能在统一工作台中使用。数据模板也属于业务域资源,和模型、字段、视图等业务域资源不同的是,数据模板是线上资源。如果制品中的业务域升级了版本号,线上的数据模板也会被复制一份,版本号和业务域版本一致。
数据模板所属的业务域和绑定的模型相同,可以查看导入导出模板列表中的“所属业务域”列。在新增发布计划时,资源类型需要选择“业务域”,然后执行发布计划。发布完成后,可以在运行环境中查看导入导出模板。

publish2

导入导出服务部署

导入导出服务部署

因为 Trantor 交付控制台也是使用 Trantor 机制自举方式开发的,但是因为历史的原因,底座没有采用最新的 0.17 机制翻新。导入导出业务域服务是在统一工作台运行的,使用了 0.17 机制自举开发。由于部分特性上的不兼容(主要是 TreeDataSource),没有直接内嵌在 Trantor 底座中运行,所以需要独立一个应用服务部署。

导入导出业务域(import_export_center)可以和底座一起部署,需要配置的环境变量和底座的基本相同。在部署的时候,如果底座启动出现异常提示导入导出模块找不到(“module import_export_center not found”),这是因为导入导出业务域元数据没有发布到运行环境导致的。

部署步骤:

(1)在完成交付控制台底座服务部署后,需要联系 Trantor 技术支持上报导入导出业务域元数据到交付控制台;

(2) 在第一次部署统一工作台底座服务时,先配置不部署导入导出镜像服务。如果没有导入导出镜像,可以在 Trantor 答疑社区平台首页中获取。等待统一工作台底座服务运行正常后,

(3)发布导入导出业务域元数据到运行环境;

(4)在统一工作台底座 dice.yml 文件中添加导入导出镜像服务,配置 meta-store 服务的网关地址,如下所示。然后重新拉起流水线。

import-export:
image: "registry.cn-hangzhou.aliyuncs.com/terminus/trantor-import-export-center:{版本号}"
ports:
- 8080
resources:
cpu: 1
mem: 2048
network:
mode: "container"
deployments:
replicas: 1
expose:
- 8080
envs:
META_STORE_URL: "{从MS服务网关地址,协议使用https}"
DS_SUBSCRIBE: false

业务服务部署相关配置

导入导出核心逻辑功能被内置在业务服务依赖的 trantor framework 中,可以配置以下环境变量影响导入导出的数据处理过程。

TRANTOR_IMPORT_PER_TIME: 每批次处理的导入数据量,默认5000
TRANTOR_EXPORT_PER_TIME: 每批次处理的导出数据量,默认5000
TRANTOR_EXPORT_MAX_NUMBER: 最大允许导出的模型数据量,默认100万

统一工作台导入导出管理页面

Trantor 内部预置了查看导入导出日志和模板管理页面,可以在交付控制台上配置这些内置的菜单,发布到运行态。内置页面的视图资源 Key 如下所述:

视图名称视图资源标识
导入日志列表import_export_center_ModelImportExportTO_ImportList
导出日志列表import_export_center_ModelImportExportTO_ExportList
导入模板列表import_export_center_ModelDataTemplateTO_ImportTemplateList
导出模板列表import_export_center_ModelDataTemplateTO_ExportTemplateList

在创建菜单,设置绑定视图时,可以查到系统内置的导入导出管理相关视图资源,如下所示:

image-20210705173656015

根据项目的需要,将上述菜单视图放入模块下面,发布模块到运行环境,即可以在统一工作台中使用这些 Trantor 内置的导入导出管理页面。

image-20210705173730153

模板管理页面
>

image-20210715143034149

模型导入导出日志

模型数据导入和导出

模型数据导入

在业务模型的列表视图中,如果对该视图创建了导入模板,即导入模板绑定到当前列表视图和容器后,才能在列表视图右上方显示“导入”按钮。如果当前页面创建了多个导入模板,导入按钮可以下拉显示多个模板。

image-20210706140622130

在导入按钮下方显示了系统所有预置的导入模板。点击模板列表中每一行的右侧有个小眼睛的按钮,可以查看模板的详情,可以查看模板的基本信息和配置的字段列表。如下图所示

image-20210706140648235

点击“导入”按钮下方的任意一个导入模板,弹出新建导入任务对话框。模型数据导入前,需要下载导入模板,模板文件是 Excel 格式。在 Excel 中录入数据完成后,选择文件导入,点击确定按钮后,提示用户前往导入中心查看数据导入记录的状态。如下入所示:

modelImport2

在导入日志列表中,用户可以查看数据导入状态信息,其中包括导入成功数量、导出失败数量以及失败原因。

只有全局失败时才会显示失败原因,如果 Excel 部分行数据导入失败,可以下载失败文件,在 Excel 对应行中查看具体的失败原因。如果失败原因不明,会给用户一个相对友好的提示“模型数据导入失败,请求联系管理员”,具体需要结合业务服务日志获取详细的错误信息。

模型数据导出

模型数据导出时支持在列表的搜索组中设置字段查询条件,过滤出需要导出的模型数据。

modelExport2

模型导出成功后,可以进入到模型导出日志列表中下载导出的数据文件。

自定义模板导出

自定义导出模板和预置导出模板的的区别可以查看概念文档数据模板

自定义模板导出需要先创建自定义导出模板。在导出按钮下拉选项中点击“自定义导出”按钮,弹出模板编辑对话框。输入模板名称、描述信息,选择需要导出的字段。点击“下一步”按钮,可以设置字段的排列顺序,以及字段的展示名称。最后点击“提交”按钮,可以在导出按钮下拉选项中可以查看到刚才创建的自定义导出模板。 自定义导出模板只有创建用户和超级管理员可以使用,可以在统一工作台编辑和删除自定义导出模板。

Export3

Trantor 导入导出日志接入接口

导入导出日志基本属性

属性名称类型说明是否必填
id字符串日志唯一标志
type枚举日志类型(导入或导出)
modelKey字符串模型标识
modelName字符串模型名称
templateKey字符串模板标识
templateName字符串模板名称
userId字符串用户 ID
userName字符串用户名
fileName字符串文件名称
fileOssUrl字符串文件 OSS 地址
status枚举导入或导出状态
totalNum整型总数量
successNum整型成功数量
failNum整型失败数量
result字符串导入或导出结果,一般记录失败原因
failStrategy枚举导入失败处理策略
business字符串后加业务属性,可以业务侧赋值

其中 business 属性是后来添加,允许业务在请求创建导入或导出日志的时候,可以根据业务需要赋值。在分页查询日志时,支持根据该属性过滤导入导出日志。

导入导出日志接口

在 trantor runtime 包中提供了接入 Trantor 导入导出日志的工具类 ModelImportExportClient,其中提供了创建导入日志、创建导出日志、更新导入或导出日志状态、分页查询导入导出日志、以及日志详情的接口。

@Autowired
private ModelImportExportClient importExportClient;

创建导入日志

入参:

属性名称说明是否必填
templateId模板 ID
templateKey模板标识 key
modelKey模型名称
fileName导入文件名称
fileOssUrl导入文件 oss 地址
failStrategy导入失败处理策略
business业务额外属性
viewKey导入按钮当前的视图资源标识

出参:导入导出日志记录。包含记录 id,用于后面更新日志

说明:

  1. 如果是基于模板,templateId 或者 templateKey 其一不能为空。因为模板基本属性包含模型信息,此时 modelKey 非必填;

  2. 如果业务上不是基于模板创建导入导出日志,则 modelKey 必填。modelKey 可以通过模型类获取到,例如:

    ModelGetter.instance().getModelByClass(ItemBO.class);
  3. fileOssUrl 为导入文件在 OSS 上的路径,可以为空,调用导入日志状态接口中更新该属性的值。

  4. 除了 fileOssUrl 和 business 属性,其他属性在创建导入日志后不能修改。

创建导出日志

入参:

属性名称说明是否必填
templateId模板 ID
templateKey模板标识 key
modelKey模型名称
fileName导入文件名称
business业务额外属性
viewKey导出按钮当前的视图资源标识

出参:导入导出日志记录。包含记录 id,用于后面更新日志

更新导入或导出日志状态

入参:

属性名称说明是否必填
id日志 id
totalNum模板 ID
successNum模板标识 key
failNum模型名称
result导入文件名称
business业务额外属性
status导入或导出状态
fileOssUrl导入或导出文件 oss 地址

出参:导入导出日志记录。包含记录 id,用于后面更新日志

分页查询导入导出日志

入参:

属性名称说明是否必填模糊查询
id日志 id
status导入或导出状态
userName用户名
templateType导入或导出类型
templateName模板名称或模板 key
modelName模型名称或者模型 key
fileName文件名称
business业务额外属性
keyword关键字查询,模糊匹配模型名称或模板名称或业务属性

出参:导入导出日志记录分页对象。

查询导入导出日志详情

入参:

属性名称说明是否必填
id日志 id

出参:导入导出日志记录。

FAQ

  1. 模型数据导入时是否支持关联模型数据导入?

    支持。但是需要关联模型也配置支持导入。其中关联模型是指模型中的字段也是一个模型对象,添加了@LinkMeta,@LookupMeta 注解。

    目前仅支持主模型的 1 对多关系数据导入,可以有多个 1 对多关系。不支持配置子模型的 1 对多,仅支持子模型的 1 对 1 关联导入。例如:

    • 如果导入 A 模型数据,含有 2 个属性分别关联模型 B 和 C,A 和 B 是 1:N(N>=1)关系,A 和 C 也是 1:N((N>=1))关系,支持同时导入 A、B、C 三个模型的关系数据。
    • 如果导入 A 模型数据,含有属性关联模型 B,A 和 B 是 1:N(N>1)关系,同时 B 模型含有属性关联 C 模型,如果 B 和 C 是 1:1 关系,支持同时导入 A、B、C 三个模型的关系数据;但是如果 B 和 C 是 1:N(N>1)关系,则仅支持导入 A、B 的关系数据,不支持导入 B 和 C 的关系数据。
  2. 非持久化模型支持导入导出吗?

    支持。非持久化模型也需要配置支持导入导出,对应的业务函数在保存模型数据时,需要转化为持久化模型存储到数据库。

  3. Json 类型字段支持导入和导出吗?

    支持。如果 Json 类型字段也是一个模型或者模型集合,也是支持导入和导出的,在 Excel 中显示方式和关联模型的一致。

  4. 1 对多关联模型如何导入?

    对于 1 对多场景,Excel 文件中主模型信息重复,子模型数据分行展示。在主模型有多个 1 对多关系数据时,Excel 中主模型重复行数和 1 对多关系列最多行相同。Excel 被解析处理后,业务函数接收到的模型列表主模型是不会重复的。

    例如导入商品模型数据,包含属性关联模型商品类别、商品属性、供应商。商品类别和商品是 1 对多关系,商品和商品属性时 1 对多关系,商品和供应商是多对多关系。如果商品 1 时,其关联了商品类别 1,关联了商品属性 1、2,关联了供应商 1、2、3;商品 2 时,其关联了商品类别 1,关联了商品属性 3,关联了供应商 1、3、4,则在 Excel 文件中输入数据方式如下:

    商品商品类别商品属性供应商
    1111
    122
    13
    2131
    23
    24
  5. 导出的模型数据文件支持直接导入吗?

    支持。导出的模型数据文件格式和导入模型数据文件是对称的。在选择导出的 Excel 模型数据文件进行导入时,请确保模型是一致的,并且导入模板中包含 Excel 文件中的列。

  6. 导入导出的数据量是否有限制?

    早期设计时对导入导出的数据量进行了限制,最大允许导入导出数据量均是 5 万条,对于大数据量数据导入导出,采取了数据切割分批导入导出的方式,默认每批次处理的数据数量是 500 条。

    后来对导出最大允许的数量放开了限制,默认是 100 万,业务服务单元可以通过环境变量修改默认的数值。每批出处理的模型数量也增大了 5000 条,业务服务单元可以根据 CPU 和内存大小进行配置。

    需要说明的是,搜索模型(SO 模型)导出默认仅支持导出 5 万条数据,如果需要导出超过 5 万条数据,需要滚动导出,具体可以参考正文 “基于 SO 模型的模型数据导出” 部分的说明写导出函数。

    TRANTOR_IMPORT_PER_TIME: 每批次处理的导入数据量,默认5000
    TRANTOR_EXPORT_PER_TIME: 每批次处理的导出数据量,默认5000
    TRANTOR_EXPORT_MAX_NUMBER: 最大允许导出的模型数据量,默认100万
  7. 模型数据导出是否进行了数据权限控制?

    是的,用户只能导出自己允许访问的数据。

  8. 模型数据导出是否可以筛选数据?

    是的,用户可以在列表视图顶部设置搜索条件,过滤想要导出的数据。

  9. 模型数据导出支持国际化吗?

    模型数据导出的时候支持对模板文件名称、列名称、以及其中字典、Boolean 数据值的翻译:

    • 在交付控制台->国际化->语言包->文本中,配置模板名称、模板字段别名的翻译文本。

    • 在交付控制台->国际化->语言包->模型中,配置模型名称、字段名称、Boolean 字段元信息的翻译。

    • 在交付控制台->国际化->语言包->字典中,配置字典项的翻译。

  10. 如何将制品版本中某个业务域下的导入导出模板,复制到新的制品版本?

因为导入导出模板属于业务域资源,只有当制品中的业务域版本升级的时候,导入导出模板才会被复制。比如:基于制品 1.0.0 创建制品版本 2.0.0 时,初始依赖的业务域版本还是 1.0.0,如果业务域升级到 2.0.0,线上的导入导出模板也会从 1.0.0 中复制一份到 2.0.0。复制后的导入导出模板和复制前的是独立的两份资源,归属于不同业务域版本。

如果希望将当前制品业务域中的导入导出模板和其他制品业务域中的相同,此时业务域版本一定是不一致的。一般需要先删除当前制品业务域下的导入导出模板,然后将业务域版本变更成一致,最后再升级版本号。例如:制品 gaia 1.0.0 中的业务域 trade 版本是 1.0.0,制品 gaia 2.0.0 中的业务域 trade 版本是 2.0.0,如果希望将 2.0.0 中的导入导出模板更改成和 1.0.0 中的相同,需要先删除制品 gaia 2.0.0 业务域 trade 下的导入导出模板,然后将业务域 trade 中 tranor.yml 中的 module.version 的版本号更改成 1.0.0,推送元信息到 gaia 2.0.0;最后将 module.version 的版本号更改成 2.0.0,再次推送元信息到 gaia 2.0.0,就可以看到制品 gaia 1.0.0 业务域 trade 的导入导出模板被复制到了 2.0.0 中。

  1. 导入导出模板对应的模型,可以和视图对应的模型不一致吗?

    不支持。目前模板和视图对应的模型必须是匹配的。

  2. 为什么在研发态配置了导入导出模板,也发布到了运行环境,但是在统一工作台业务页面看不到导入或导出按钮。

    • 可能发布的业务域不正确。导入导出模板属于业务域资源,发布时需要选择业务域,具体可以查看研发态导入导出模板列表中的“所属业务域”。发布完成后,请务必确认在运行环境导入导出模板中已经存在创建了的导入导出模板。

      需要注意的是,发布的业务域不一定是导入导出函数所在的模块,而是模板绑定的模型所在的模块。因为模板所属的业务域和绑定的模型所属业务域相同。导入导出函数可能和模型不在相同模块。

    • 导入导出模板中配置的视图或者容器不正确。导入导出模板需要绑定到具体的视图和容器。一个视图页面可能有多个列表容器需要支持导入导出,所以创建模板时需要指定容器标识。设置的容器 key 即视图内具体的布局容器的 key,在当前页面中唯一,比如 <Table key=“main”> 中的 main 即为容器 key

  3. 导入 Excel 列头的注释信息有什么作用,能删除吗?

    Excel 列头的注释不能删除,包含了列在导入模型中的字段元信息,删除后列数据不会被导入。

  4. 导入或导出失败显示异常消息“模型数据导入/导出失败,请求联系管理员”,是什么含义?

    这是给用户查看的一个通用异常友好提示,需要查看业务域服务后台查看具体的错误日志。如果业务函数处理导入导出异常时需要提供一个用户友好的提示,需要抛出 BusinessException。异常的处理逻辑如下图所示:

    image-20210708152956767
  5. 为什么导出的 Excel 表头有”xxx.yyy”的形式,能只显示”yyy”吗?

    支持在研发态交付控制台中对模板字段配置展示名称(即别名)。一般二级、三级字段都需要配置别名,否则导出的时候 Excel 表头显示的是 A.B.C 的格式;如果字段在不同场景下有不同语义,也建议配置别名。另外,别名国际化使用的视图文本翻译。

  6. 导入导出服务如何部署?

    请参考正文“导入导出服务部署”章节,按照相关说明操作。

  7. 在下载导出文档时,提示链接请求协议是 http 不安全,下载被拦截,如下图所示,一般需要如何处理?

    image-20210708152956767

    这个可以修改底座从 metastore 的环境变量,例如:

    删除环境变量 OSS_HOST=oss-cn-hangzhou.aliyuncs.com
    新增环境变量 OSS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com

    这样导出的文件链接请求协议是 https。

  8. 发布业务域资源时提示 View for data template xxx is not exist,如下图所示。这个需要如何解决?

    image-20210713155702383

    原因:错误提示的意思是 xxx 模板配置的视图不存在了。因为导入导出模板属于业务域的线上资源,如果业务域下创建了导入导出模板资源,发布的时候会检查模板信息的有效性。模板绑定了特定的视图,如果绑定的视图资源更改了识别标识或者删除了,发布的时候就会提示上面的错误。

    解决办法:

    (1)在研发态制品中的配置->数据模板菜单下,打开导入导出模板列表页面,找到上面提示的有问题模板。

    (2)编辑该模板时就会发现绑定的视图为空,需要重新配置绑定的视图。

    (3)再次进入到资源发布页面,重新发布业务域。

  9. 为什么研发态导入导出模板中配置的部分字段,在发布到运行环境后,查看到的导入导出模板中没有对应的字段?

    原因:这种情况一般是因为研发态导入导出模板中的部分字段无效了,无效的可能原因有:

    (1)字段名称发生了修改,引起了字段资源识别标识发生了变化,模板中的字段失效。比如商品模型有展示名称是编码的字段,对应的字段名称由 itemCode 更改成了 code。

    (2)字段被删除了,模板中的字段失效。

    (3)字段改变了层级。比如商品模型中有适应人群字段,但是后来这个字段被移动了关联模型商品属性中。

    对于研发态存在问题字段的导入导出模板,在发布到运行环境时,这部分模板字段被排除了,不会发布到运行环境,所以会出现模板在运行环境中缺少字段的情况。

    解决办法:一般需要模板创建人在研发态手动矫正模板的字段,比如先删除之前的字段,重新配置字段。最后重新发布模板所属的业务域。

  10. Trantor 模型数据导入内部的处理流程是怎么样的?

    下面是模型数据导入的基本流程图

    image-20210825205352

    其中需要注意的是:

    • 在框架层对 Excel 中的数据进行了切割分片处理,500 条为一个批次,而不是一次性提交所有数据到业务函数。因为导入的数据可能非常多,如果一次性提交到业务处理函数,保存时很容易导致事务太大而导入失败。

    • 导入业务函数在接收到一批模型数据时,如果发现数据行导入失败,需要抛出异常,整个导入事务回滚。框架层在遇到批量导入失败时,退化成单条循环导入,单条循环导入的耗时会比较长。

  11. 为什么模型导入日志统计结果中导入成功的行数比 Excel 中实际行数少?

    这是因为框架层对 Excel 中的数据进行了合并处理。合并的依据是主模型的基本属性字段(一般指不包含关联模型的字段)信息完全重复。这就要求导入模板中配置的主模型基本属性字段组合起来要有区分度,否则认为是同一条模型数据。

    在导入主模型和关联模型数据时,如果是 1 对多情形时,是要求主模型数据重复,关联模型数据分行显示(参考 FAQ4)。在导入的时候,主模型和关联模型数据合并。

    举例来说,如果导入订单和订单行模型数据,如果导入模板中只配置了订单模型的下单日期字段,如果 Excel 中有 2 行的下单日期数据重复,就会导致导入时这 2 行数据合并为一条数据。

  12. 统一工作台的导出按钮下拉选项中有一个“自定义导出”按钮,怎么去掉,只保留在交付控制台配置的导出模板?

    可以在 Table 容器中配置关闭自定义导出

    <Table customDerivedData="false">
    </Table>
  13. 支持不配置导出模板,勾选表格中的数据直接导出吗?

    支持。可以在 Table 容器中开启支持勾选数据导出。需要注意的是,这种方式导出的 Excel 表头的列名可能和页面中显示的列名称不一致。因为视图中配置的显示规则在导出的时候是获取不到的,也无法像模板一样配置列的别名(即展示名称)。

    <Table batchExport="true">
    </Table>

    image-20211223204400805