亲宝软件园·资讯

展开

工作日志,Excel导入树结构数据

ITDragon龙 人气:0
[TOC] ## 1. 前言 最近做了一个比较有趣的需求。需要把[树结构的目录通过Excel的方式导入到系统](https://www.cnblogs.com/itdragon/)中,并且该目录层级可以是多级且不确定的。这可能是一个常见又不太常见的需求,一般目录都是在界面上操作创建,或者是系统初始化生成。很少在系统使用一段时间后还有导入新目录的需求。 ## 2. 需求分析 ### 2.1 需求难点 这个需求最大的难点就是如何找到父级节点。包括 1)如何让一个Excel表格实现不确定目录层级功能? 2)如何让子个节点能正确找到其父级节点? 3)如何在遍历完一个分枝后,还能从根节点继续遍历另外一个分枝? ### 2.2 解决难点 1)[我们]()可以将目录层级作为用户输入项,由用户决定该数据处于第几层目录。解决目录层级不确定的需求。 2)[我们]()可以用树节点深度遍历的思想,遍历一个个节点,使其找到其父节点。 3)[我们]()同样可以用深度遍历的思想再结合先进后出操作,重新找回之前的根节点。 ### 2.3 表格设计 [我们](https://www.cnblogs.com/itdragon/)可以用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 | | 从表格中,[我们](https://www.cnblogs.com/itdragon/)应该可以得出以下结论: 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. 功能实现 [我们](https://www.cnblogs.com/itdragon/)对需求做了简单的分析,现在就用代码来实现。从易到难,从一个分枝再到多个分枝来实现。 ### 3.1 一个分枝 一个分枝的Level排序应该是:1-2-3-N 这种情况是最简单的,孤零零的一条直线。其父节点就是当前节点的上一个元素。 伪代码如下: ```kotlin var categoryPathStack = mutableListOf() 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 这种情况稍微复杂了一点,如果只是获取当前节点的上一个元素是很难找到其父级节点的。[我们](https://www.cnblogs.com/itdragon/)需要把同一层的兄弟节点都剔除掉。 伪代码如下: ```kotlin var categoryPathStack = mutableListOf() 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, ```kotlin 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导入代码 以下只是删减过后的代码,具体业务场景会有具体的逻辑代码。 ```kotlin @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() 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博客

加载全部内容

相关教程
猜你喜欢
用户评论