Skip to content

数据导入

数据导入功能用于导入 excel 文件到数据库中。

前提条件

  • 导入的文件需要是 Excel 文件,后缀名为.xlsx
  • excel 文件中的第一行作为数据表头,第二行开始数据作为数据内容。
  • 在运行处理器之前需要把文件上传到服务器上,并保存到 data 目录下。

处理过程

  • 加载导入配置文件,提前配置数据导入的映射关系与处理器,此一步由程序员或是管理员配置。
  • 加载数据,用户调用导入数据处理器,读取数据文件,进行数据导入。
  • 数据清洗,根据导入配置文件中的配置调用脚本处理器进行数据校验或是处理。
  • 保存数据。

数据准备

首先准备一个名称为sku.xlsxExcel 文件,文件内容如下,保存到目录 data 下,文件访问路径:/data/sku.xlsx

物资名称规格型号预警库存类目单位
测试X,x1,x210成品KG
测试 2X,x1,x210成品KG

创建模型配置

创建一个模型配置/models/material/sku.mod.json,生成对应的数据库表,用于保存导入的数据。

json
{
  "name": "物资单品",
  "table": {
    "name": "sku",
    "comment": "物资单品"
  },
  "columns": [
    { "label": "ID", "name": "id", "type": "ID" },
    {
      "label": "物资",
      "name": "material_id",
      "type": "bigInteger",
      "index": true,
      "comment": "所属物资"
    },
    {
      "label": "编码",
      "name": "sku_sn",
      "type": "string",
      "length": 32,
      "nullable": true,
      "unique": true,
      "comment": "SKU编码"
    },
    {
      "label": "单位",
      "name": "unit",
      "type": "string",
      "nullable": true,
      "comment": "计量单位"
    },
    {
      "label": "单价",
      "name": "price",
      "type": "decimal",
      "index": true,
      "nullable": true,
      "comment": "无税单价"
    },
    {
      "label": "税率",
      "name": "tax_rate",
      "type": "decimal",
      "index": true,
      "nullable": true,
      "comment": "税率"
    },
    {
      "label": "含税单价",
      "name": "tax_price",
      "type": "decimal",
      "index": true,
      "nullable": true,
      "comment": "含税单价"
    },
    {
      "label": "列账单价",
      "name": "fin_price",
      "type": "decimal",
      "index": true,
      "nullable": true,
      "comment": "列账单价"
    },
    {
      "label": "规格",
      "name": "specs",
      "type": "json",
      "nullable": true,
      "comment": "规格数值(KEY-VALUE)"
    },
    {
      "label": "预警库存",
      "name": "stock",
      "type": "bigInteger",
      "index": true,
      "default": 100,
      "comment": "预警库存"
    },
    { "label": "图片", "name": "images", "type": "json", "nullable": true },
    { "label": "介绍", "name": "detail", "type": "text", "nullable": true },
    {
      "label": "状态",
      "name": "status",
      "type": "enum",
      "default": "在产",
      "option": ["在产", "停产"],
      "index": true,
      "comment": "状态: 在产, 停产"
    }
  ],
  "relations": {
    "material": {
      "type": "hasOne",
      "model": "material",
      "key": "id",
      "foreign": "material_id",
      "query": {
        "withs": { "category": {}, "supplier": {} }
      }
    }
  },
  "option": { "timestamps": true, "soft_deletes": true }
}

配置规则制定

在使用数据导入之前需要作一些配置,在应用目录 imports 下创建一个文件,文件名格式为:<导入规则>.json,例如:sku.json。后缀名也可以是yao,jsonc。 配置规则包含以下的内容:

  • 配置导入数据的处理器。
  • 配置每一列数据的校验与更新的处理器。
  • 配置模型字段与 exceel 文件中的字段的映射关系。
json
{
  "title": "导入单品",
  "process": "scripts.imports.run.SKU",
  "output": "scripts.imports.run.SKUOUTPUT",
  "columns": [
    {
      "label": "物资名称",
      "name": "name",
      "match": ["物资名称", "物资", "名称", "material", "name"],
      "rules": [
        "scripts.imports.validate.Require",
        "scripts.imports.validate.Len"
      ]
    },
    {
      "label": "规格型号",
      "name": "specs",
      "match": ["规格型号", "规格", "型号", "specs"],
      "rules": ["scripts.imports.validate.Specs"]
    },
    {
      "label": "预警库存",
      "name": "stock",
      "match": ["预警库存", "库存预警", "stock"],
      "rules": [
        "scripts.imports.validate.Require",
        "scripts.imports.validate.Int"
      ]
    },
    {
      "label": "类目",
      "name": "category",
      "match": ["类目", "category", "类型"],
      "rules": [
        "scripts.imports.validate.Require",
        "scripts.imports.validate.Len"
      ]
    },
    {
      "label": "单位",
      "name": "unit",
      "match": ["单位", "包装", "unit"],
      "rules": ["scripts.imports.validate.Require"]
    }
  ],
  "option": {
    "chunkSize": 200,
    "mappingPreview": "auto",
    "dataPreview": "auto"
  },
  "rules": {
    "scripts.imports.validate.Require": "必填项验证",
    "scripts.imports.validate.Len": "长度校验",
    "scripts.imports.validate.Int": "数字校验",
    "scripts.imports.validate.Specs": "规格校验"
  }
}

配置文件具体说明如下:

ts
export namespace YaoImport {
  /**数据导入器 */
  export interface Importer {
    /**版本【管理字段】 */
    version?: string;
    /**描述【管理字段】 */
    decription?: string;
    /**备注【管理字段】 */
    comment?: string;
    /**导入名称*/
    title?: string;
    /**处理器名称*/
    process: string;
    /**The process import output*/
    output?: string;
    /**字段列表,此字段是必须选项*/
    columns: Column[];
    /**导入配置项*/
    option?: Option;
    /**许可导入规则,配置用途,配置规则对应的名称或是说明*/
    rules?: { [key: string]: string };
  }

  /**导入字段定义 */
  export interface Column {
    /**字段标签 */
    label: string;
    /**模型字段名称 */
    name: string;
    /**字段名称(原始值),不需要配置*/
    field?: string;
    /**匹配建议 */
    match?: string[];
    /**清洗规则定义 */
    rules?: string[];
    /**是否可以为空 */
    nullable?: boolean;
    /**是否为主键 */
    primary?: boolean;
  }

  /**导入配置项定 */
  export interface Option {
    /**使用已匹配过的模板 */
    useTemplate?: boolean;
    /**默认数据模板链接 */
    templateLink?: string;
    /**每次处理记录数量,默认500条一次 */
    chunkSize?: number;
    /**显示字段映射界面方式 auto 匹配模板失败显示, always 一直显示, never 不显示 */
    mappingPreview?: string;
    /**数据预览界面方式 auto 有异常数据时显示, always 一直显示, never 不显示 */
    dataPreview?: string;
  }

  /**字段映射表*/
  export interface Mapping {
    /**数据表名称 */
    sheet: string;
    /**第一列的位置,一般是0 */
    colStart: number;
    /**第一行的位置,一般是0,会自动的跳过标题行 */
    rowStart: number;
    /**字段数据列表 */
    data: Binding[];
    /**是否自动匹配,未使用 */
    autoMatching: boolean;
    /**是否通过已传模板匹配,未使用 */
    templateMatching: boolean;
  }

  /** 数据绑定*/
  export interface Binding {
    /**Column中配置的字段名称 */
    label: string;
    /**模型字段名称 */
    field: string;
    /**EXCEL中Sheet表的字段名称 */
    name: string;
    /**源关联字段坐标 A1,B1,C1,D1*/
    axis: string;
    /**示例数据 */
    value: string;
    /**具体每一个字段对应的清洗规则,处理器列表 */
    rules: string[];
  }
}

校验处理

js
/**
 * 校验或是更新传入参数数值,用户可以检查数据是否合法,或是更新数据。
 * @param {*} value,当前字段值
 * @param {*} row,当前行数据
 * @returns {boolean} 返回数据组行数据表示成功更新,返回false表示此次检查失败,并不会更新数据。
 */
function Int(value, row) {
  if (parseInt(value) > 0) {
    return row;
  }
  return false;
}

处理列表

处理器说明新版处理器文档
xiang.import.Run导入数据yao.import.Run查看
xiang.import.Data数据预览yao.import.Data查看
xiang.import.Setting查询导入配置yao.import.Setting查看
xiang.import.DataSetting查询数据预览配置yao.import.DataSetting查看
xiang.import.Mapping查询字段映射yao.import.Mapping查看
xiang.import.MappingSetting查询字段映射配置yao.import.MappingSetting查看

查询字段映射

参数:

  • name: 映射名称
  • file: 文件路径
sh
yao run yao.import.Mapping 'sku' "sku.xlsx"

返回具体某一个 excel 表中每一个字段的映射关系。

json
{
  "autoMatching": true,
  "colStart": 1,
  "data": [
    {
      "axis": "A1",
      "field": "name",
      "label": "物资名称",
      "name": "物资名称",
      "rules": [
        "scripts.imports.validate.Require",
        "scripts.imports.validate.Len"
      ],
      "value": "测试"
    },
    {
      "axis": "B1",
      "field": "specs",
      "label": "规格型号",
      "name": "规格型号",
      "rules": ["scripts.imports.validate.Specs"],
      "value": "X,x1,x2"
    },
    {
      "axis": "C1",
      "field": "stock",
      "label": "预警库存",
      "name": "预警库存",
      "rules": [
        "scripts.imports.validate.Require",
        "scripts.imports.validate.Int"
      ],
      "value": "10"
    },
    {
      "axis": "D1",
      "field": "category",
      "label": "类目",
      "name": "类目",
      "rules": [
        "scripts.imports.validate.Require",
        "scripts.imports.validate.Len"
      ],
      "value": "成品"
    },
    {
      "axis": "E1",
      "field": "unit",
      "label": "单位",
      "name": "单位",
      "rules": ["scripts.imports.validate.Require"],
      "value": "KG"
    }
  ],
  "rowStart": 1,
  "sheet": "Sheet1",
  "templateMatching": false
}

获取字段映射配置规则

此处理器的功能主要是获取导入配置中每个字段的映射规则在前端的配置,主要用于前端界面的配置。 参数说明:

  • name:导入规则的名称,用于查找对应的导入配置文件。
  • filename:Excel 文件路径,用于查找对应的映射关系。
sh
yao run yao.import.MappingSetting 'sku' "sku.xlsx"

返回结果:

json
{
  "columns": {
    "字段名称": {
      "edit": {},
      "form": {},
      "label": "字段名称",
      "view": {
        "props": {
          "value": ":label"
        },
        "type": "label"
      }
    },
    "数据源": {
      "edit": {
        "props": {
          "options": [
            {
              "label": "物资名称",
              "value": "A1"
            },
            {
              "label": "规格型号",
              "value": "B1"
            },
            {
              "label": "预警库存",
              "value": "C1"
            },
            {
              "label": "类目",
              "value": "D1"
            },
            {
              "label": "单位",
              "value": "E1"
            }
          ],
          "value": ":axis"
        },
        "type": "select"
      },
      "form": {},
      "label": "数据源",
      "view": {
        "props": {
          "value": ":name"
        },
        "type": "label"
      }
    },
    "数据示例": {
      "edit": {},
      "form": {},
      "label": "数据示例",
      "view": {
        "props": {
          "value": ":value"
        },
        "type": "label"
      }
    },
    "清洗规则": {
      "edit": {
        "props": {
          "mode": "multiple",
          "options": [
            {
              "label": "数字校验",
              "value": "scripts.imports.validate.Int"
            },
            {
              "label": "长度校验",
              "value": "scripts.imports.validate.Len"
            },
            {
              "label": "必填项验证",
              "value": "scripts.imports.validate.Require"
            },
            {
              "label": "规格校验",
              "value": "scripts.imports.validate.Specs"
            }
          ],
          "value": ":rules"
        },
        "type": "select"
      },
      "form": {},
      "label": "清洗规则",
      "view": {
        "props": {
          "value": ":rules"
        },
        "type": "tag"
      }
    }
  },
  "filters": {},
  "list": {
    "layout": {
      "columns": [
        {
          "name": "字段名称"
        },
        {
          "name": "数据源"
        },
        {
          "name": "清洗规则",
          "width": 300
        },
        {
          "name": "数据示例"
        }
      ]
    },
    "option": {
      "operation": {
        "hideEdit": true,
        "hideView": true,
        "width": 0
      }
    },
    "primary": "field"
  }
}

预览数据

使用 yao.import.Data 处理器可以预览 Excel 文件的数据内容。 参数说明:

  • name:导入规则的名称,用于查找对应的导入配置文件。
  • filename:Excel 文件路径,用于查找对应的映射关系。
  • page: 分页参数,用于分页查询数据,当前第几页。
  • size: 每页显示的数据量。
  • mapping: 导入映射关系,用于将 Excel 中的数据映射到数据库中的字段。
sh
yao run yao.import.Data "sku" "sku.xlsx" 0 10 "null"

返回数据:

json
{
  "data": [
    {
      "__effected": true,
      "category": "成品",
      "id": 1,
      "name": "测试",
      "specs": "X,x1,x2",
      "stock": "10",
      "unit": "KG"
    },
    {
      "__effected": true,
      "category": "成品",
      "id": 2,
      "name": "测试2",
      "specs": "X,x1,x2",
      "stock": "10",
      "unit": "KG"
    }
  ],
  "next": 2,
  "page": 1,
  "pagecnt": 10,
  "pagesize": 10,
  "prev": 0
}

查询导入配置

使用 yao.import.Setting 处理器可以查询和设置数据导入的相关配置。

sh
yao run yao.import.Setting sku

返回结果:

json
{
  "dataPreview": "auto",
  "mappingPreview": "auto",
  "templateLink": "",
  "title": "导入单品"
}

数据预览配置

使用 yao.import.DataSetting 数据预览表格配置,主要用于前端显示配置。

参数说明

  • name: 规则名称

使用示例

bash
# 设置预览5行数据
yao run yao.import.DataSetting sku

返回结果:

json
{
  "columns": {
    "单位": {
      "edit": {},
      "form": {},
      "label": "单位",
      "view": {
        "props": {
          "value": ":unit"
        },
        "type": "label"
      }
    },
    "物资名称": {
      "edit": {},
      "form": {},
      "label": "物资名称",
      "view": {
        "props": {
          "value": ":name"
        },
        "type": "label"
      }
    },
    "类目": {
      "edit": {},
      "form": {},
      "label": "类目",
      "view": {
        "props": {
          "value": ":category"
        },
        "type": "label"
      }
    },
    "规格型号": {
      "edit": {},
      "form": {},
      "label": "规格型号",
      "view": {
        "props": {
          "value": ":specs"
        },
        "type": "label"
      }
    },
    "预警库存": {
      "edit": {},
      "form": {},
      "label": "预警库存",
      "view": {
        "props": {
          "value": ":stock"
        },
        "type": "label"
      }
    }
  },
  "filters": {},
  "list": {
    "actions": {
      "pagination": {
        "props": {
          "showTotal": true
        }
      }
    },
    "layout": {
      "columns": [
        {
          "name": "物资名称"
        },
        {
          "name": "规格型号"
        },
        {
          "name": "预警库存"
        },
        {
          "name": "类目"
        },
        {
          "name": "单位"
        }
      ]
    },
    "option": {
      "operation": {
        "checkbox": [
          {
            "status": [
              {
                "label": "有效",
                "value": true
              },
              {
                "label": "无效",
                "value": false
              }
            ],
            "value": ":__effected",
            "visible_label": false
          }
        ],
        "hideEdit": true,
        "hideView": true,
        "unfold": true,
        "width": 120
      }
    },
    "primary": "id"
  }
}

导入数据

使用 yao.import.Run 处理器可以将 Excel 文件数据导入到指定的数据表中。支持 .xlsx 和 .xls 格式的文件,可以设置字段映射关系和数据转换规则。

处理器会调用导入规则中的 process 处理器。用户需要在处理器处理数据的最终处理逻辑。

js
/**
 * 导入单品数据
 * @param {array} columns,数据库字段列表,最后一个字段是"__effected",表示是否检查成功。
 * @param {array} data,需要保存的数据,最后一个字段是true/false,true表示全部校验检查成功,false表示部分检查失败。
 * @param {string} uuid,导入数据的唯一标识,用于区分导入数据
 * @param {number} page,导入数据的页码,用于分页导入
 * @returns [failure,ignore] 导入失败的数据的数量,导入忽略的数据的数量
 */
function SKU(columns, rows, uuid, page) {
  console.log(columns, rows);

  columns = ['name', 'specs', 'stock', 'category', 'unit', '__effected'];
  rows = [
    ['测试', 'X,x1,x2', '10', '成品', 'KG', true],
    ['测试2', 'X,x1,x2', '10', '成品', 'KG', true]
  ];

  return [failure, ignore];
}

output 处理器

用户还可以配置 output 处理器,对导入的数据进行二次处理,比如对数据进行校验,或者对数据进行格式化等操作。

js
**
 * 导入后的数据处理
 * @param {object} data 导入结果数据
 * @returns {object} 返回处理后的统计结果,包含以下字段:
 *  - total: 总记录数
 *  - success: 成功导入数量
 *  - failure: 失败数量
 *  - ignore: 忽略数量
 */
function SKUOUTPUT(data) {
  const total = data.total || 0;
  const failed = data.failure || 0;
  const ignore = data.ignore || 0;

  return {
    total: total,
    success: data.success || 0,
    failure: failed,
    ignore: ignore
  };
}

注意事项:

  1. Excel 文件需要放在应用的 data 目录下
  2. 导入前建议先使用数据预览功能检查数据格式
  3. 用户需要自己实现数据保存处理逻辑
  4. 用户需要自己实现数据校验逻辑
  5. 支持大文件分批导入

错误处理

导入过程中可能遇到以下错误:

  1. 文件错误

    • 文件不存在或无法访问
    • 文件格式不正确
  2. 数据错误

    • 必填字段为空
    • 数据类型转换失败
    • 唯一性约束冲突
    • 外键约束失败
  3. 系统错误

    • 数据库连接失败
    • 内存不足
    • 磁盘空间不足

使用示例

bash
# 基本导入示例
# 需要3个参数,第一个参数IMPORT定义,第二个参数为文件路径,第三个参数为映射关系
# 映射关系结构类型参考Mapping,如果没有传入映射关系,会自动生成映射关系。
yao run yao.import.Run "sku" "sku.xlsx" "null"