文章目录
- 前言
- 一、非树型机构信息
-
- 1.示例数据
- 2.机构编码规则
- 二、转换为树型机构
-
- 1.转换逻辑
- 2.具体实现
-
- 2.1.将excel文件读取到程序中
- 2.2.解析机构编码并获取所有的父级编码候选值
- 2.3.设置所有节点的ParentCode
- 2.4.查找机构的根节点
- 2.5.通过ParentCode构建完整的树型结构
- 2.6.将树形结构数据写入到JSON文件中
- 2.7.将树型结构的机构数据展开写入到EXCEL文件中
- 三、附属的支持类信息
-
- 1.样例机构数据原始映射模型
- 2.树型结构转换模型
- 3.SHEET表格模型
- 4.CELL单元格模型
- 总结
前言
统计平台项目,后端采用一套开源框架,框架内封装了完善的用户、角色、菜单、组织机构、数据字典等基础功能,支持访问授权、按钮权限、数据权限等。新系统的这部分基础数据,完全来源甲方原有老系统。新系统的组织机构设计采用树形结构,而甲方现有系统的机构数据并非树型结构。为了将现有系统的机构数据导入到新系统中,支持统计平台的机构管理,需要写一套转换程序,将非树型结构的机构信息转为树型结构导入到统计平台中。具体过程如下所示。
一、非树型机构信息
1.示例数据
2.机构编码规则
经过从甲方现有系统中获取的机构数据分析,得到如下规则:
- 机构编码由数字或者字母组成的定长字符串(长度为12位,不足12位的在末尾自动补0)。
- 机构编码字符串每两位为一组,实际编码不足偶数位的,自动在末尾用0补齐偶数位。
- 现有系统的机构信息没有父子级联关系,是以固定的前缀匹配规则进行查询的,因而能够模拟树型结构进行层级展示。
二、转换为树型机构
1.转换逻辑
- 将给定的样例数据的excel文件读取到程序中。
- 设置每行数据的父级编码信息。
- 查找根节点信息。
- 构建机构树信息。
2.具体实现
2.1.将excel文件读取到程序中
将用户给定的样例数据excel文件解析并读取到程序中,可以采用Apache POI工具包,也可以采用其他的java-office工具包进行操作。本示例采用阿里巴巴的开源组件EasyExcel进行操作,它是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。
- 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
- 文档地址
阿里巴巴开源组件easyexcel官方地址地址 - 读取代码示例
/**
* <h3>简单读取excel文件中的记录行, 不剔除末尾0补码</h3>
*
* @return
*/
public static List<TreeModel> excelSimpleRead() {
return EasyExcel.read("E:\\demo-code\\jzdata-demo\\data\\调试机构数据.xlsx").head(TreeModel.class).sheet().doReadSync();
}
2.2.解析机构编码并获取所有的父级编码候选值
按照数据样例的规则,数据编码长度固定为12位,每两位为一组进行分组解析。
- 首先需要获取机构的真实编码,及剔除末尾补位的0后,剩余编码如果为基数位则在末尾补0,构造成偶数位,这就是现有系统机构的真实编码。
- 将真实的偶数位的机构编码,跳过代表机构本身这一级的两位编码,从高位到地位按两位为一组,循环机构编码,遇到两位不都为0时,则获取从字符串开始到当前位的子串,并按照编码规则用0补全12位字符串,作为当前机构的候选的父级编码值。
具体实现如下:
/**
* <h3>构建候选的父级编码列表</h3>
* <ul>
* <li>从左到右以每两位为一个基数,判断当前组是否是全0,如果不是则拆分,如果是全0则继续循环,并且从高位开始拆分</li>
* <li>从左到右进行编码拆分,比如“410000020100”被拆分为:“410000000000”,“410000020000”,比如“411000010000”被拆分为:“410000000000
* ”,“411000000000”</li>
* <li>将拆分后子串,自动补全12位编码,不够的位数在末尾补0</li>
* <li>返回值示例,“411000010000”的后续父编码为:[411000000000, 410000000000]</li>
* </ul>
*
* @param target
* @return
*/
public static List<String> getCandidateParentCode(String target) {
//step1.将目标编码处理成最小的偶数位编码
String pariCode = target.replaceAll(SUFFIX_ZERO_REGEX, "");
pariCode = pariCode.length() % 2 == 0 ? pariCode : pariCode + "0";
//step2.构建候选的父级编码值
List<String> parentCodes = new ArrayList<>();
for (int size = pariCode.length() - 2, i = size; i > 1; i -= 2) {
//只有当临近的两位不都为0时才进行拆分
if (pariCode.charAt(i) != '0' || pariCode.charAt(i - 1) != '0') {
String subStr = pariCode.substring(0, i);
String formatted = String.format(BLANK_FORMAT, subStr);
String parentCode = formatted.replaceAll(BLANK_REGEX, "0");
parentCodes.add(parentCode);
}
}
return parentCodes;
}
2.3.设置所有节点的ParentCode
通过循环获取每个机构的候选的父级编码集合,并通过嵌套循环判断并设置每个机构的ParentCode的值。具体实现如下:
/**
* <h3>设置数据的ParentC