Skip to content

7.4 数据导入导出 ​

概述

Excel 导入导出基于 xuri/excelize/v2(utility/excel 封装):导入解析逐行校验写入并返回统计,导出走 ChunkQuery 分批查询防 OOM。启用模块:user / level / example。

utility/excel API ​

text
ReadSheet(file FileOpener):首行表头,全单元格转字符串(TrimSpace),空行跳过;
  FileOpener 接口兼容 *ghttp.UploadFile 与 *multipart.FileHeader

WriteSheet(dir, baseName, headers, rows [][]any):写文件返回 (fileName, filePath)

导入流程(Import,真实模式摘自 example) ​

go
// Import 导入案例演示
// 表头为表单字段名,按列序写入
func (s *sExample) Import(ctx context.Context, req *v1.ExampleImportReq) (res *v1.ExampleImportRes, err error) {
	_, rows, err := excel.ReadSheet(req.File)
	if err != nil {
		return nil, err
	}
	res = &v1.ExampleImportRes{Total: len(rows)}
	for i, row := range rows {
		if cellAt(row, 0) == "" {
			res.Failed++
			res.Reasons = append(res.Reasons, fmt.Sprintf("第%d行:案例名称为空", i+2))
			continue
		}
		if _, e := dao.Example.Ctx(ctx).Data(map[string]any{
			"name":   cellAt(row, 0),
			"avatar": cellAt(row, 1),
			"type":   cellInt(row, 2, 0),
			"status": cellInt(row, 3, 0),
			"sort":   cellInt(row, 4, 0),
		}).Insert(); e != nil {
			res.Failed++
			res.Reasons = append(res.Reasons, fmt.Sprintf("第%d行:写入失败 %v", i+2, e))
			continue
		}
		res.Success++
	}
	return res, nil
}

要点:逐行独立成败(一行失败不影响后续);返回 Total/Success/Failed/Reasons(逐行原因);user 导入还含唯一校验/租户上限/默认密码 bcrypt 预计算/角色与城市解析(ResolveCityCodes)。

导出流程(Export,真实模式摘自 example) ​

go
// Export 导出案例演示
// 与列表同套筛选条件,枚举列输出字典文本
func (s *sExample) Export(ctx context.Context, req *v1.ExampleExportReq) (res *v1.ExampleExportRes, err error) {
	rows, err := exampleSpec.applyFilters(ctx, exampleSpec.Model(ctx), req).Order("sort, id").All()
	if err != nil {
		return nil, err
	}
	headers := []string{"案例名称", "案例图片", "案例类型", "案例状态", "案例排序"}
	data := make([][]any, 0, len(rows))
	for _, r := range rows {
		data = append(data, []any{r["name"].String(), r["avatar"].String(), DictGetText(ctx, "example_type", r["type"]), DictGetText(ctx, "example_status", r["status"]), r["sort"].String()})
	}
	fileName, _, err := excel.WriteSheet(upload.BaseDir(ctx), "案例演示数据", headers, data)
	if err != nil {
		return nil, err
	}
	url := upload.GetFileUrl(ctx, fileName)
	return &url, nil
}

要点:导出统一走 xxxSpec.applyFilters 与列表同套筛选(导出=所见);枚举列 DictGetText 转文本;大结果集用 ChunkQuery 分批(user 导出 500/页)。

导入导出规范 ​

项规范
接口Import POST multipart(p:"file");Export GET + 筛选参数
杅限sys:{module}:import / export
日志logType import / export("模块名-导入数据/导出数据")
模板表头=表单字段名(导出文件即导入模板)
文件位置导出写临时文件返回 URL(/upload/temp/...,受 temp 清理回收)
防 OOM大结果集 ChunkQuery 分批(默认 500/页)

前端组件 ​

text
Excel 组件(components/Excel + importFile):
  导入:选文件 → POST /api/{module}/import → 展示统计与失败原因
  导出:调 /api/{module}/export → 拿 URL → downloadFile 下载
详见 9.11 Excel 导入导出组件

开发新模块的导入导出 ​

text
1. 请求结构:ExampleImportReq(gmeta + File 字段)/ ExampleExportReq(gmeta + 筛选字段)
2. 接口声明:service 接口加 Import/Export 方法
3. logic 实现:按上方真实模式(ReadSheet 逐行 / applyFilters + WriteSheet)
4. 权限节点:import/export(生成器 HasImport/HasExport 默认 true 自动建)
5. 前端:导入按钮 + 导出按钮(模板参照 example 页面)

小蚂蚁云团队 · 提供技术支持