一、需求描述
接口功能描述:
查询国内完整的行政区划信息,即所有的省份、省份下所有的地级市、地级市下所有的区县,所有的数据只返回 id、admincode、name、simple_name、lng、lat 六个字段。
二、背景概述
目标表结构:
CREATE TABLE `lbs_district` (
`id` char(19) NOT NULL DEFAULT '0' COMMENT '19位ID',
`parent_id` char(19) NOT NULL DEFAULT '0' COMMENT '上级行政区划的19位ID',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '名称',
`level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '类型:1:国家与地区,2:省份,3:地级市,4:区县,5:街道,6:乡镇、其他,7:面商圈,8:点商圈',
`level_name` varchar(20) NOT NULL DEFAULT '' COMMENT '类型名称:1:country,2:province,3:city,4:county,5:town,6:town,7:business,8:business',
`lng` float(10,6) NOT NULL DEFAULT '0.000000' COMMENT '中心点 longitude,即经度坐标',
`lat` float(10,6) NOT NULL DEFAULT '0.000000' COMMENT '中心点 latitude,即纬度坐标',
`admincode` char(6) NOT NULL DEFAULT '0' COMMENT '标准国家行政区划6位编码',
`is_foreign` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '类型:1:大陆及港澳,2:台湾及国外',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '标识删除状态:0:不删除,1:删除',
`deltime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '删除时间',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='行政区划和商圈信息表';
从库中获取行政区划数据后,以树形结构输出省、市、区县三级行政区划数据。
三、代码实现
考虑到从库中获取的数据为3000多条,基本囊括了全国的数据,所以考虑用递归实现也是可以的。
<?php
/**
* <p>
* 接口功能描述:查询国内完整的行政区划信息
* </p>
*
* <p>
* 接口输出的数据结构说明:输出数据以JSON数组形式返回,例:
* <blockquote><pre>
* [{
* "id":"8008611000000000000",
* "parent_id":"8008600000000000000",
* "admincode":"110000",
* "name":"北京市",
* "simple_name":"北京",
* "lng":"116.405289",
* "lat":"39.904987"
* },
* {
* "id":"8008612010500000000",
* "parent_id":"8008600000000000000",
* "admincode":"120105",
* "name":"河北省",
* "simple_name":"河北",
* "lng":"117.201569",
* "lat":"39.156631"
* }]
* <blockquote><pre>
* </p>
*
* @author bingqing5
* @date 2022/05/16
*/
header('Content-type: application/json');
require_once 'district_common.php';
/**
* 生成树形结构数据
* @param $items 以数组形式存储的处理目标数据
* @param $parentId 父节点的ID
* @return array
*/
function generateTree($items , $parentId)
{
// 初始化一个数组,用以存储输出数据
$tree = array();
/* 遍历数组,对数据递归处理*/
foreach($items as $k => $v)
{
/* 判断当前节点的parentId是否与参数相同?
* 如果相同,沿着同父节点向下递归。进一步
* 判断当前节点的子节点是否不为空,如果不
* 为空,将当前节点归入当前父节点的children中
*/
if($v['parent_id'] == $parentId)
{
$set = generateTree($items , $v['id']);
if (!empty($set))
{
$v['children'] = generateTree($items , $v['id']);
}
$tree[] = $v;
}
}
return $tree;
}
$sql = "
SELECT
ld.id,
ld.parent_id,
ld.admincode,
ld.name,
lde.simple_name,
ld.lng,
ld.lat,
ld.level
FROM
lbs_district ld
LEFT JOIN lbs_district_extends lde ON ld.id = lde.lbs_district_id
WHERE
ld.is_foreign = 1
AND ld.level IN (2,3,4)
ORDER BY ld.id ASC";
$return = $db->getAll($sql);
// 接口要求获取国内省、市、区县三级行政区划树形结构,所以根节点取中国行政区划ID
$treeRes = generateTree($return, '8008600000000000000');
echo json_encode($treeRes);;
$db->close();
?>
以上代码因为公司内部项目环境原因,使用的是PHP5,递归部分的代码用PHP7可以这样写:
function generateTree($items , $parentId)
{
$tree = [];
foreach($items as $k => $v)
{
if($v['parent_id'] == $parentId)
{
if (!empty(generateTree($items , $v['id'])))
{
$v['childs'] = generateTree($items , $v['id']);
}
$tree[] = $v;
}
}
return $tree;
}
话说,PHP真的是坑,笔者用PHP7的写法线上跑,总是报错。因为笔者本职是Java开发,对PHP那是一窍不通,也是在组长要求下临时承担了PHP开发任务,在写代码的时候难免总是带着Java的语法习惯。。。
四、优化
将代码提交后,线上测试,结果如图:
很显然,接口拿到数据需要20s,这是一个不可接受的事。之后,同事在此基础上完成了优化,代码如下:
<?php
/**
* <p>
* 接口功能描述:查询国内完整的行政区划信息
* </p>
*
* <p>
* 接口输出的数据结构说明:输出数据以JSON数组形式返回,例:
* <blockquote><pre>
* [{
* "id":"8008611000000000000",
* "parent_id":"8008600000000000000",
* "admincode":"110000",
* "name":"北京市",
* "simple_name":"北京",
* "lng":"116.405289",
* "lat":"39.904987"
* },
* {
* "id":"8008612010500000000",
* "parent_id":"8008600000000000000",
* "admincode":"120105",
* "name":"河北省",
* "simple_name":"河北",
* "lng":"117.201569",
* "lat":"39.156631"
* }]
* <blockquote><pre>
* </p>
*
* @author bingqing5
* @date 2022/05/16
*/
header('Content-type: application/json');
require_once 'district_common.php';
/**
* 生成树形结构数据
* @param $items 以数组形式存储的处理目标数据
* @param $parentId 父节点的ID
* @return array
*/
function generateTree($items , $parentId) {
// 初始化一个数组,用以存储输出数据
$tree = array();
/* 遍历数组,对数据递归处理*/
foreach($items as $k => $v) {
/* 判断当前节点的parentId是否与参数相同?
* 如果相同,沿着同父节点向下递归。进一步
* 判断当前节点的子节点是否不为空,如果不
* 为空,将当前节点归入当前父节点的children中
*/
if(isset($v['parent_id']) && $v['parent_id'] == $parentId) {
if($v['level']!=4){//只有省和地级市才有下级子节点
$set = generateTree($items , $v['id']);
if (!empty($set)) {
$v['children'] = generateTree($items , $v['id']);
}
// $v['children'] = generateTree($items , $v['id']);
}
unset($v['parent_id']);//较少输出字段,提高接口速度
$tree[] = $v;
}
}
return $tree;
}
//数据太多,只输出一些基础数据,取消关联,否会会很慢
/*
$sql = "
SELECT
ld.id,
ld.parent_id,
ld.admincode,
ld.name,
lde.simple_name,
ld.lng,
ld.lat,
ld.level
FROM
lbs_district ld
LEFT JOIN lbs_district_extends lde ON ld.id = lde.lbs_district_id
WHERE
ld.is_foreign = 1
AND ld.level IN (2,3,4)
ORDER BY ld.id ASC";
*/
$sql = "
SELECT
ld.id,
ld.parent_id,
ld.name,
ld.lng,
ld.lat,
ld.level
FROM
lbs_district ld
WHERE
ld.is_foreign = 1
AND ld.level IN (2,3,4)
ORDER BY ld.id ASC";
$return = $db->getAll($sql);
// 接口要求获取国内省、市、区县三级行政区划树形结构,所以根节点取中国行政区划ID
$treeRes = generateTree($return, '8008600000000000000');
echo json_encode($treeRes);;
$db->close();
?>
优化后接口的响应速度大大提升,如图: