Java后台以树形结构返回省市区三级区域信息

欢迎访问个人博客德鲁大叔撸代码
在前台页面很常见的一种需求是,以树形结构展示具有层级关系的数据。比如企业的部门信息、大部分管理系统的菜单、省市区信息等等。
具体如下图所示
德鲁大叔管理系统
若依管理系统

一、表创建以及数据导入

执行下面创建表sql,创建sys_region存具体信息,表信息下载地址sys_region 行政区域表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE `sys_region`  (
`id` int(11) NOT NULL COMMENT '区域主键',
`name` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '区域名称',
`pid` int(11) NULL DEFAULT NULL COMMENT '区域上级标识',
`sname` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地名简称',
`level` int(11) NULL DEFAULT NULL COMMENT '区域等级',
`citycode` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '区域编码',
`postcode` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮政编码',
`mername` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '组合名称',
`lng` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '经度',
`lat` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '纬度',
`pinyin` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '拼音',
`gmt_create` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建时间',
`gmt_modified` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
`status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '状态 0正常 1停用',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

二、编写Region实体类(mybatis generator自动生成后增加子区域属性)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Data
public class Region {
private Integer id;

private String name;

private Integer pid;

private String sname;

private Integer level;

private String citycode;

private String postcode;

private String mername;

private String lng;

private String lat;

private String pinyin;

private Date gmtCreate;

private Date gmtModified;

private String status;

/**
* 子区域
*/
private List<Region> children = new ArrayList<>();
}

三、查询接口和mybatis映射文件(省略,只展示查询sql)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="regionList" parameterType="com.kyrie.boot.entity.Region" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from sys_region
<where>
<if test="name != null and name != ''">
AND name like concat('%', #{name}, '%')
</if>
<if test="citycode != null and citycode != ''">
AND citycode = #{citycode}
</if>
</where>
order by id
</select>

四、组装树形菜单(根据步骤三查到的列表)

1、控制转发层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/region")
public class RegionController {


@Autowired
private RegionService regionService;
@GetMapping("/list")
public AjaxResult list(Region region){
List<Region> regionList = regionService.selectRegionList(region); // 根据步骤三查到的集合(region表的全部数据)
List<Region> tree = regionService.buildAreaTree(regionList); // 组装一个菜单树
return AjaxResult.success(tree); // 响应返回给前端页面的数据
}

}

2、组装树形菜单入口

1
2
3
4
5
6
7
8
9
10
11
@Override
public List<Region> buildAreaTree(List<Region> regions) {
List<Region> tree = new ArrayList<>();
for(Iterator<Region> it = regions.iterator();it.hasNext();){
Region region = (Region) it.next();
if(region.getPid() == 0){
recursionFn(regions, region); //递归列表
tree.add(region);
}
}
return tree;

3、递归列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 递归列表
* @param list
* @param region
*/
private void recursionFn(List<Region> list, Region region){
// 得到子节点列表
List<Region> childList = getChildList(list, region);
region.setChildren(childList);
for(Region child : childList){
if(hasChild(list,child)){
//判断是否有子节点
Iterator<Region> it = childList.iterator();
while (it.hasNext()){
Region n = (Region) it.next();
recursionFn(list, n);
}
}
}
}

4、得到子节点列表(得到下级区域)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 得到子节点列表
* @param list
* @param region
* @return
*/
private List<Region> getChildList(List<Region> list, Region region){
List<Region> tlist = new ArrayList<Region>();
Iterator<Region> it = list.iterator();
while (it.hasNext()){
Region region1 = (Region) it.next();
if(region1.getPid().longValue() == region.getId().longValue()){
tlist.add(region1);
}
}
return tlist;
}

5、判断是否有下级区域

1
2
3
4
5
6
7
8
9
10
/**
* 判断是否有子节点
* @param list
* @param region
* @return
*/
private boolean hasChild(List<Region> list, Region region)
{
return getChildList(list, region).size() > 0 ? true : false;
}

五、postman测试

以省/直辖市为根节点,组装结果35
在这里插入图片描述
以陕西省为例,展示具体的市级信息
在这里插入图片描述

坚持原创技术分享,您的支持将鼓励我继续创作!