最近做了一套国家三级地址库前后端生成方案。因为是从国家民政部获取的官方数据,与网络上各种各样的地址库产品相比,自己比较满意。
给大家介绍一下思路:
- 获取国家民政部官方数据:http://www.mca.gov.cn/article/sj/xzqh/2020/
- 后端ETL这些数据,放入数据库中。
- 前端按照Element UI的组件element-china-area-data引入,替换其中省市区数据源,分离一个app.js,即可使用。
结果展示:
具体方案如图:
[文字留底]
地址库组件
生成
数据库:省市区表
1. 从国家民政部获取最新行政区划代码。
http://www.mca.gov.cn/article/sj/xzqh/2020/
2. 放入Excel,按照尾部00、0000拆分出省、市、区。
3. 用kettle按照设计表的规则导入mysql,不存在的省或市表主键设置为0。
4. 生成insert语句。
前端element-china-area-data控件
后端写一个小程序,依照“数据库表”和“element-china-area-data数据格式”生成前端行政区划json数据。
https://www.npmjs.com/package/element-china-area-data
json数据,压缩成一行,替换element-china-area-data控件数据。
https://www.sojson.com/yasuo.html
维护
民政部数据更新
大约几个月会更新一次。
数据库省市区表最新版与上一版数据比对
差异
区划代码
名称
所属二级或一级
影响是什么?
新增
无影响
修改
新旧的区划代码不一致
删除
合并
新旧的区划代码不一致
方案:存储时,区划代码和名称都存储,作为快照;变更仅影响新的。
不用比对
element-china-area-data控件json数据与民政部数据比对?
不用
[后端]
相关表
1 CREATE TABLE `bd_province` ( 2 `province_id` bigint(20) UNSIGNED NOT NULL COMMENT '表主键id 全局唯一, snowflake id', 3 `adr_code` int(11) NOT NULL COMMENT '行政区划代码 administrative division 唯一依据', 4 `adr_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '行政区划单位名称 有一个名称最长45位', 5 `full_pinyin` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称全拼 需要200的长度,用于搜索', 6 `short_pinyin` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称短拼音 有一个名称最长45位,用于搜索', 7 `longitude` decimal(32, 10) NULL DEFAULT NULL COMMENT '中心点经度 从高德地图接口获取', 8 `latitude` decimal(32, 10) NULL DEFAULT NULL COMMENT '中心点纬度 从高德地图接口获取', 9 `creator_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建人ID 默认0', 10 `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', 11 `modifier_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '修改人ID 默认0', 12 `modify_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间 自动更新', 13 `deleted` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '逻辑删除标识 0是未删除,1是已删除。', 14 PRIMARY KEY (`province_id`) USING BTREE, 15 UNIQUE INDEX `uk_adr_code`(`adr_code`) USING BTREE 16 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '行政划分-省和直辖市表 ' ROW_FORMAT = Dynamic;
1 CREATE TABLE `bd_city` ( 2 `city_id` bigint(20) UNSIGNED NOT NULL COMMENT '表主键id 全局唯一, snowflake id', 3 `province_id` bigint(20) UNSIGNED NOT NULL COMMENT '所属省ID', 4 `province_code` int(11) NULL DEFAULT NULL COMMENT '所属省代码 冗余', 5 `adr_code` int(11) NOT NULL COMMENT '行政区划代码 administrative division 唯一依据', 6 `adr_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '行政区划单位名称 有一个名称最长45位', 7 `full_pinyin` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称全拼 需要200的长度,用于搜索', 8 `short_pinyin` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称短拼音 有一个名称最长45位,用于搜索', 9 `longitude` decimal(32, 10) NULL DEFAULT NULL COMMENT '中心点经度 从高德地图接口获取', 10 `latitude` decimal(32, 10) NULL DEFAULT NULL COMMENT '中心点纬度 从高德地图接口获取', 11 `creator_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建人ID 默认0', 12 `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', 13 `modifier_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '修改人ID 默认0', 14 `modify_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间 自动更新', 15 `deleted` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '逻辑删除标识 0是未删除,1是已删除。', 16 PRIMARY KEY (`city_id`) USING BTREE, 17 UNIQUE INDEX `uk_adr_code`(`adr_code`) USING BTREE 18 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '行政划分-城市表 ' ROW_FORMAT = Dynamic;
1 CREATE TABLE `bd_district` ( 2 `district_id` bigint(20) UNSIGNED NOT NULL COMMENT '表主键id 全局唯一, snowflake id', 3 `city_id` bigint(20) UNSIGNED NOT NULL COMMENT '所属市ID', 4 `city_code` int(11) NULL DEFAULT NULL COMMENT '所属市代码 冗余', 5 `adr_code` int(11) NOT NULL COMMENT '行政区划代码 唯一依据', 6 `adr_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '行政区划单位名称 有一个名称最长45位', 7 `full_pinyin` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称全拼 需要200的长度,用于搜索', 8 `short_pinyin` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称短拼音 有一个名称最长45位,用于搜索', 9 `longitude` decimal(32, 10) NULL DEFAULT NULL COMMENT '中心点经度', 10 `latitude` decimal(32, 10) NULL DEFAULT NULL COMMENT '中心点纬度', 11 `creator_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建人ID 默认0', 12 `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', 13 `modifier_id` bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '修改人ID 默认0', 14 `modify_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间 自动更新', 15 `deleted` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '逻辑删除标识 0是未删除,1是已删除;默认0。', 16 PRIMARY KEY (`district_id`) USING BTREE, 17 UNIQUE INDEX `uk_adr_code`(`adr_code`) USING BTREE 18 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '行政划分-区县表 ' ROW_FORMAT = Dynamic;
相关代码
1 package cn.com.service.impl; 2 3 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 import org.springframework.beans.BeanUtils; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Service; 7 8 import java.util.*; 9 10 /** 11 * 区县实现类 12 * 13 * @author Mike 14 * 2020/9/1 15:05 15 */ 16 @Service 17 public class DistrictServiceImpl implements DistrictService { 18 19 @Autowired 20 private ProvinceMapper provinceMapper; 21 22 @Autowired 23 private CityMapper cityMapper; 24 25 @Autowired 26 private DistrictMapper districtMapper; 27 28 /** 29 * 生成Element UI 地址库控件数据源,用于替换为最新国家民政部发布的三级地址库信息。 30 * 31 * @author Mike 32 * 2020/8/27 17:39 33 */ 34 @Override 35 public Result generateAdrInfo() { 36 Map<String, Map> result = new HashMap(); 37 QueryWrapper queryWrapper = new QueryWrapper(); 38 39 //省 40 queryWrapper.eq("deleted", DeletableEnum.NOT_DELETED.ordinal()); 41 HashMap hmProvince = new HashMap(); 42 queryWrapper.select("adr_code", "adr_name"); 43 List<Province> provinceList = provinceMapper.selectList(queryWrapper); 44 for (Province item : provinceList) { 45 hmProvince.put(item.getAdrCode(), item.getAdrName()); 46 } 47 result.put("86", hmProvince); 48 int countProvince = hmProvince.size(); 49 50 //市 51 queryWrapper.clear(); 52 queryWrapper.eq("deleted", DeletableEnum.NOT_DELETED.ordinal()); 53 queryWrapper.select("province_code", "adr_code", "adr_name"); 54 queryWrapper.orderByAsc("province_code", "adr_code"); 55 List<City> cityList = cityMapper.selectList(queryWrapper); 56 Integer tempProvinceCode = cityList.get(0).getProvinceCode(); 57 HashMap hmCity = new HashMap(); 58 int countCity = 0; 59 for (City item : cityList) { 60 if (!item.getProvinceCode().equals(tempProvinceCode)) { 61 HashMap tempHashMap = (HashMap) hmCity.clone(); 62 result.put(tempProvinceCode.toString(), tempHashMap); 63 tempProvinceCode = item.getProvinceCode(); 64 countCity = countCity + hmCity.size(); 65 hmCity = new HashMap(); 66 } 67 hmCity.put(item.getAdrCode(), item.getAdrName()); 68 } 69 result.put(cityList.get(cityList.size() - 1).getProvinceCode().toString(), hmCity); 70 countCity = countCity + hmCity.size(); 71 72 //区 73 queryWrapper.clear(); 74 queryWrapper.eq("deleted", DeletableEnum.NOT_DELETED.ordinal()); 75 queryWrapper.select("city_code", "adr_code", "adr_name"); 76 queryWrapper.orderByAsc("city_code", "adr_code"); 77 List<District> districtList = districtMapper.selectList(queryWrapper); 78 Integer tempCityCode = districtList.get(0).getCityCode(); 79 HashMap hmDistrict = new HashMap(); 80 int countDistrict = 0; 81 for (District item : districtList) { 82 if (!item.getCityCode().equals(tempCityCode)) { 83 HashMap tempHashMap = (HashMap) hmDistrict.clone(); 84 result.put(tempCityCode.toString(), tempHashMap); 85 tempCityCode = item.getCityCode(); 86 countDistrict = countDistrict + hmDistrict.size(); 87 hmDistrict = new HashMap(); 88 } 89 hmDistrict.put(item.getAdrCode(), item.getAdrName()); 90 } 91 result.put(districtList.get(districtList.size() - 1).getCityCode().toString(), hmDistrict); 92 countDistrict = countDistrict + hmDistrict.size(); 93 94 return new Result(SUCCESS, 95 String.format("%tF 最新省市区数据生成成功!其中省(直辖市)有%d个,市有%d个,区有%d个。", 96 new Date(), countProvince, countCity, countDistrict), 97 result); 98 } 99 }
以上文件在百度网盘可下载(永久有效):
链接:https://pan.baidu.com/s/1_OIgSUULPZDcn0W8yG7Thg
提取码:x6ig