1. 前言

最近做了一个比较有趣的需求。需要把树结构的目录通过Excel的方式导入到系统中,并且该目录层级可以是多级且不确定的。这可能是一个常见又不太常见的需求,一般目录都是在界面上操作创建,或者是系统初始化生成。很少在系统使用一段时间后还有导入新目录的需求。

2. 需求分析

2.1 需求难点

这个需求最大的难点就是如何找到父级节点。包括

1)如何让一个Excel表格实现不确定目录层级功能?

2)如何让子个节点能正确找到其父级节点?

3)如何在遍历完一个分枝后,还能从根节点继续遍历另外一个分枝?

2.2 解决难点

1)我们可以将目录层级作为用户输入项,由用户决定该数据处于第几层目录。解决目录层级不确定的需求。

2)我们可以用树节点深度遍历的思想,遍历一个个节点,使其找到其父节点。

3)我们同样可以用深度遍历的思想再结合先进后出操作,重新找回之前的根节点。

2.3 表格设计

我们可以用Level作为目录所在层级,一级目录的Level就是1,同理N级目录的Level就是N。且数据从上至下可以形成一个完整树分枝。

表格设计如下:

分类名称 级别Level 其他字段
A栋 1
A栋-1楼 2
B栋 1
B栋-1楼 2
B栋-1楼-A区 3
B栋-2楼 2
B栋-2楼-A区 3
B栋-2楼-B区 3

从表格中,我们应该可以得出以下结论:

1)A栋和B栋属于一级目录

2)A栋有一个子目录,A栋-1楼

3)B栋有两个子目录,分别是:B栋-1楼、B栋-2楼

4)B栋-1楼有一个子目录,B栋-1楼-A区

5)B栋-2楼有两个子目录,分别是:B栋-2楼-A区、B栋-2楼-B区

3. 功能实现

我们对需求做了简单的分析,现在就用代码来实现。从易到难,从一个分枝再到多个分枝来实现。

3.1 一个分枝

一个分枝的Level排序应该是:1-2-3-N

这种情况是最简单的,孤零零的一条直线。其父节点就是当前节点的上一个元素。

伪代码如下:

var categoryPathStack = mutableListOf<EquipmentCategory>()
for (i in sheet.firstRowNum..sheet.lastRowNum) {
val categoryName = row.getCell(0).stringCellValue
val categoryLevel = row.getCell(1).stringCellValue.toInt()
var parentCategory: EquipmentCategory? = null
if (categoryLevel > 1) {
parentCategory = categoryPathStack.last()
}
// todo save or update
categoryPathStack.add(equipmentCategory)
}

3.2 一个分枝多个树叶

一个分支多个树叶的Level排序应该是:1-2-3-3-3-3

这种情况稍微复杂了一点,如果只是获取当前节点的上一个元素是很难找到其父级节点的。我们需要把同一层的兄弟节点都剔除掉。

伪代码如下:

var categoryPathStack = mutableListOf<EquipmentCategory>()
for (i in sheet.firstRowNum..sheet.lastRowNum) {
val categoryName = row.getCell(0).stringCellValue
val categoryLevel = row.getCell(1).stringCellValue.toInt()
var parentCategory: EquipmentCategory? = null
// 将集合中大于或等于当前层级的数据剔除掉
while (categoryPathStack.isNotEmpty() && categoryPathStack.last().level >= categoryLevel) {
categoryPathStack = categoryPathStack.subList(0, categoryPathStack.size-1).toMutableList()
}
if (categoryLevel > 1) {
parentCategory = categoryPathStack.last()
}
// todo save or update
categoryPathStack.add(equipmentCategory)
}

3.3 多个分枝多个树叶

多个分支多个树叶的Level排序应该是:1-2-3-3-3-3-2-3-1-2-3

这种场景依然可以用一个分支多个树叶的代码实现,而后面来的1就像一个分割线,将前面先进来的数据隔离开。

4. 代码事例

4.1 目录实体结构

目录实体添加临时字段level方便逻辑判断。字段code是方便后期通过code作为StartingWith的查询条件,从而减少递归查询所有子级目录带来的性能损耗。code的生成规则是:父节点code拼接当前节点id,

class Category: AuditModel() {

    var name: String? = null
var description: String? = null
var isLeaf: Boolean = true
var parentId: String? = null
@Column(columnDefinition = "TEXT")
var code: String? = null @Transient
var level: Int = 0
}

4.2 Excel导入代码

以下只是删减过后的代码,具体业务场景会有具体的逻辑代码。

@Transactional
fun importCategoryData(file: MultipartFile, request: HttpServletRequest): OperateStatus {
// fileUtil.getExcelWorkbook 只是简单封装的读取excel方法
val work = fileUtil.getExcelWorkbook(file.inputStream, file.originalFilename!!)
// todo 清空旧数据 val sheet: Sheet = work.getSheetAt(0)
var categoryPathStack = mutableListOf<Category>()
for (i in sheet.firstRowNum..sheet.lastRowNum) {
val row = sheet.getRow(i)
if (row == null || row.rowNum == 0) {
continue
}
// todo 数据校验 val categoryName = row.getCell(0).stringCellValue
val categoryLevel = row.getCell(1).stringCellValue.toInt()
var parentCategory: Category? = null
while (categoryPathStack.isNotEmpty() && categoryPathStack.last().level >= categoryLevel) {
categoryPathStack = categoryPathStack.subList(0, categoryPathStack.size-1).toMutableList()
}
if (categoryLevel > 1) {
parentCategory = categoryPathStack.last()
}
var category = Category()
category.name = categoryName
category.parentId = parentCategory?.id
category = categoryRepository.save(category) if (parentCategory == null) {
category.code = category.id
} else {
category.code = "${parentCategory.code}-${category.id}"
category.isLeaf = true
parentCategory.isLeaf = false
categoryRepository.save(parentCategory)
}
categoryRepository.save(category)
category.level = categoryLevel
categoryPathStack.add(category)
}
work.close()
return OperateStatus("Import Category Success")
}

文章到这里就结束了,感谢观看。ITDragon博客

工作日志,Excel导入树结构数据的更多相关文章

  1. [办公自动化] 再读《让EXCEL飞》(从excel导入access数据时,union联合查询,数据源中没有包含可见的表格)

    一年多以前就买了@Mrexcel的<让excel飞>这本书.整体思路是利用access结合excel,大幅度提高数据分析效率. 最近又拿出来看了看.第十五章,比高级筛选更“高级”,P241 ...

  2. PHP 清除 Excel 导入的数据空格

    处理excel中的数据时,遇到了页面中显示为空格,审查元素时却显示为换行,使用replace函数也不管用,反正就是不知道是什么东西,看起来像空格 中文空格这里面有好几种:没有简单的解决问题的方式,比如 ...

  3. Talend 从Excel导入Saleforce数据(二) TMAP是精髓

    TMap LookUp 经过测试的结果: ------------------------------------------ LookUp最好从CSV读数据,这样是最快了(20万记录1s).从Sal ...

  4. Talend 从Excel导入Saleforce数据(一) 直接从salesforce lookup 性能的噩梦

    速度的瓶颈是在查询Sales force是否有该电话号码的联系人资料. TMap属性的 lookup Model, 如果用Load Once, 则会把SaleForce的contact全部load下来 ...

  5. excel导入mysql数据

    excel加载mysql数据 1.第一步,选择从mysql导入数据 2.单击会出现弹框: 3.可能有的同学的,这里缺少插件,例如: 4.去下载 这个 插件安装即可.https://dev.mysql. ...

  6. 将Excel导入到数据中

    常用的方式的有两种: 1. 通过 Microsoft.Jet.OLEDB.4.0 或  Microsoft.ACE.OLEDB.12.0 Microsoft.ACE.OLEDB.12.0 需要安装 A ...

  7. JeeSite中Excel导入导出

    在各种管理系统中,数据的导入导出是经常用到的功能,通常导入导出以Excel.CSV格式居多.如果是学习的过程中,最好是自己实现数据导入与导出的功能,然而在项目中,还是调用现成的功能比较好.近期一直使用 ...

  8. java利用jxl实现Excel导入功能

    本次项目实践基于Spring+SpringMvc+MyBatis框架,简单实现了Excel模板导出.和Excel批量导入的功能.实现过程如下:. 1.maven导入所需jar包 <depende ...

  9. 将datagrid中数据导出到excel中 -------<<工作日志2014-6-6>>

    前台datagrid数据绑定 #region 导出到excel中    /// <summary>    /// 2014-6-6    /// </summary>    / ...

随机推荐

  1. CSV文件注入漏洞简析

    “对于网络安全来说,一切的外部输入均是不可信的”.但是CSV文件注入漏洞确时常被疏忽,究其原因,可能是因为我们脑海里的第一印象是把CSV文件当作了普通的文本文件,未能引起警惕. 一.漏洞定义 攻击者通 ...

  2. js笔记系列之--时间及时间戳

    js入门系列之 时间及时间戳 时间及时间戳 时间及时间戳是js里面很常见的一个概念,在我们写前端页面的时候,经常会遇到需要获取当前时间的情况,所以,了解js中的时间概念非常重要.而时间戳是指格林威治时 ...

  3. Java基础--方法的定义

    1.为什么要有方法? 方法(又叫函数)就是一段特定功能的代码块.方法提高程序的复用性和可读性. 比如,有了方法,我们可以把要重复使用的一段代码提炼出来,然后在每个需要执行这段代码的地方去调用即可. 2 ...

  4. LaTex公式符号

    下面这个网站是我认为比较齐全的网站 http://www.mohu.org/info/symbols/symbols.htm

  5. 02 HDFS 分布式环境实战

    HDFS的主要设计理念 1.存储超大文件 这里的“超大文件”是指几百MB.GB甚至TB级别的文件. 2.最高效的访问模式是 一次写入.多次读取(流式数据访问)3.运行在普通廉价的服务器上 HDFS设计 ...

  6. 026.掌握Service-外部访问

    一 集群外部访问 由于Pod和Service都是Kubernetes集群范围内的虚拟概念,所以集群外的客户端默认情况,无法通过Pod的IP地址或者Service的虚拟IP地址:虚拟端口号进行访问.通常 ...

  7. Fortify Audit Workbench 笔记 SQL Injection SQL注入

    SQL Injection SQL注入 Abstract 通过不可信来源的输入构建动态 SQL 指令,攻击者就能够修改指令的含义或者执行任意 SQL 命令. Explanation SQL injec ...

  8. 回想笔记 瞎比比 域名注册 解析绑定ip 下载证书 设置证书 重定向http到https请求

    2019.7.27 回想笔记 拥有腾讯云服务器一台 阿里云注册5元域名,进行备案 完成之后 使用解析 绑定服务器ip地址 ,使用域名可以访问到web服务器而不是通过直接暴露ip地址进行访问 证书购买 ...

  9. python如何用format进行进制转换与如何删除进制前缀

    10进行十进制,十六进制,八进制,二进制的转换:(#:保留进制前缀)对于带着进制前缀的,如"0x"“0o”“0b”,可以直接在后面加上’x’‘o’'b’进行删除:其他的一些小技巧: ...

  10. axios请求拦截器

    import axios from 'axios';   // 创建axios实例   let service = null;   if (process.env.NODE_ENV === 'deve ...