目录1.创建API Key2.创建类(1)BaiduUtil工具类(2) BaiduMapVO (值对象类)(3) SysAreaMapper (数据访问层)(4) AreaSyncUtil (同步工具类)3.调用关系图执行流程说明1.创建API Key来到百度地图开放平台登录之后点击创建应用控制台 | 百度地图开放平台https://lbsyun.baidu.com/apiconsole/key名字随便填就行选择服务端请求校验方式选择sn校验方式2.创建类(1)BaiduUtil工具类作用封装百度地图API的调用逻辑主要功能构造API请求参数计算SN签名安全认证发送HTTP请求获取地区数据解析JSON响应并转换为BaiduMapVO对象核心方法requestGetSN()- 执行API调用并返回地区数据列表package com.qcby.util; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.convert.Convert; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.qcby.VO.BaiduMapVO; import lombok.extern.slf4j.Slf4j; import org.springframework.web.util.UriUtils; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.security.NoSuchAlgorithmException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; Slf4j public class BaiduMapUtil { // 用户服务访问密钥 public static String AK ak; // 用于生成请求url public static String SK sk; public static String URL https://api.map.baidu.com/api_region_search/v1/?; public static void main(String[] args) throws Exception { BaiduMapUtil snCal new BaiduMapUtil(); Map paramsMap new LinkedHashMapString, String(); // paramsMap.put(keyword, 石家庄市); paramsMap.put(keyword, 中国); paramsMap.put(sub_admin, 4); paramsMap.put(extensions_code, 1); System.out.println(JSONUtil.toJsonStr(snCal.requestGetSN(paramsMap))); } public static ListBaiduMapVO requestGetSN(MapString, String param) throws Exception { param.put(ak, AK); param.put(sn, caculateSn(param)); return requestGetSN(URL, param); } /** * 选择了ak使用SN校验 * 根据您选择的AK已为您生成调用代码 * 检测您当前的AK设置了sn检验本示例中已为您生成sn计算代码 * param strUrl * param param * throws Exception */ public static ListBaiduMapVO requestGetSN(String strUrl, MapString, String param) throws Exception { if (strUrl null || strUrl.length() 0 || param null || param.size() 0) { return null; } StringBuffer queryString new StringBuffer(); queryString.append(strUrl); for (Map.Entry?, ? pair : param.entrySet()) { queryString.append(pair.getKey() ); // 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可 // queryString.append(URLEncoder.encode((String) pair.getValue(), UTF-8).replace(, %20) ); queryString.append(UriUtils.encode((String) pair.getValue(), UTF-8) ); } if (queryString.length() 0) { queryString.deleteCharAt(queryString.length() - 1); } URL url new URL(queryString.toString()); log.info(请求百度地区url:{},queryString); URLConnection httpConnection url.openConnection(); httpConnection.connect(); InputStreamReader isr new InputStreamReader(httpConnection.getInputStream()); BufferedReader reader new BufferedReader(isr); StringBuffer buffer new StringBuffer(); String line; while ((line reader.readLine()) ! null) { buffer.append(line); } reader.close(); isr.close(); JSONObject jsonObject JSONUtil.parseObj(buffer.toString()); Object status jsonObject.get(status); if (status null || Convert.toInt(status)!0) { log.info(获取百度地区失败:{},buffer); return null; } JSONArray districts jsonObject.getJSONArray(districts); return BeanUtil.copyToList(districts, BaiduMapVO.class); } public static String caculateSn(Map paramsMap) throws UnsupportedEncodingException, NoSuchAlgorithmException { BaiduMapUtil snCal new BaiduMapUtil(); // 计算sn跟参数对出现顺序有关get请求请使用LinkedHashMap保存key,value该方法根据key的插入顺序排序post请使用TreeMap保存key,value该方法会自动将key按照字母a-z顺序排序。 // 所以get请求可自定义参数顺序sn参数必须在最后发送请求但是post请求必须按照字母a-z顺序填充bodysn参数必须在最后。 // 以get请求为例http://api.map.baidu.com/geocoder/v2/?address百度大厦outputjsonakyourakparamsMap中先放入address再放output然后放ak放入顺序必须跟get请求中对应参数的出现顺序保持一致。 //paramsMap // 调用下面的toQueryString方法对LinkedHashMap内所有value作utf8编码拼接返回结果address%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6outputjsonakyourak String paramsStr snCal.toQueryString(paramsMap); // 对paramsStr前面拼接上/geocoder/v2/?后面直接拼接yoursk得到/geocoder/v2/?address%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6outputjsonakyourakyoursk String wholeStr new String(/api_region_search/v1/? paramsStr SK); System.out.println(wholeStr); // 对上面wholeStr再作utf8编码 String tempStr URLEncoder.encode(wholeStr, UTF-8); // 调用下面的MD5方法得到最后的sn签名 String sn snCal.MD5(tempStr); System.out.println(sn); return sn; } // 对Map内所有value作utf8编码拼接返回结果 public String toQueryString(Map?, ? data) throws UnsupportedEncodingException { StringBuffer queryString new StringBuffer(); for (Map.Entry?, ? pair : data.entrySet()) { queryString.append(pair.getKey() ); // 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可 // queryString.append(URLEncoder.encode((String) pair.getValue(), UTF-8).replace(, %20) ); queryString.append(UriUtils.encode((String) pair.getValue(), UTF-8) ); } if (queryString.length() 0) { queryString.deleteCharAt(queryString.length() - 1); } return queryString.toString(); } // 来自stackoverflow的MD5计算方法调用了MessageDigest库函数并把byte数组结果转换成16进制 public String MD5(String md5) { try { java.security.MessageDigest md java.security.MessageDigest .getInstance(MD5); byte[] array md.digest(md5.getBytes()); StringBuffer sb new StringBuffer(); for (int i 0; i array.length; i) { sb.append(Integer.toHexString((array[i] 0xFF) | 0x100) .substring(1, 3)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { } return null; } }(2) BaiduMapVO(值对象类)作用百度地图API返回数据的实体类用于接收API响应数据属性code地区编码name地区名称level地区层级country/province/city/districtdistricts子地区列表支持递归嵌套package com.qcby.VO; import lombok.Data; import java.util.List; Data public class BaiduMapVO { private String code; private String name; private String level; private ListBaiduMapVO districts; }(3)SysAreaMapper(数据访问层)作用MyBatis-Plus的Mapper接口负责数据库操作主要功能继承BaseMapper提供基础的CRUD方法自定义查询方法如按层级查询、按父级ID查询等对应表sys_area系统地区表package com.qcby.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.qcby.VO.SysArea; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; Mapper public interface SysAreaMapper extends BaseMapperSysArea { /** * 查询所有省份 (level 1) */ Select(SELECT * FROM sys_area WHERE level 1 ORDER BY area_id) ListSysArea selectProvinces(); /** * 根据父级ID查询子区域 */ Select(SELECT * FROM sys_area WHERE parent_id #{parentId} ORDER BY area_id) ListSysArea selectByParentId(String parentId); /** * 查询所有区域按层级和ID排序 */ Select(SELECT * FROM sys_area ORDER BY level, area_id) ListSysArea selectAll(); /** * 根据层级查询 */ Select(SELECT * FROM sys_area WHERE level #{level} ORDER BY area_id) ListSysArea selectByLevel(Integer level); /** * 查询国家 (level 0) */ Select(SELECT * FROM sys_area WHERE level 0) ListSysArea selectCountry(); }(4)AreaSyncUtil(同步工具类)作用核心的业务逻辑类负责将百度API数据同步到数据库主要功能启动Spring容器调用百度API获取数据清空原有数据递归处理地区层级关系批量保存到数据库package com.qcby.util; import com.qcby.Application; import com.qcby.VO.BaiduMapVO; import com.qcby.VO.SysArea; import com.qcby.mapper.SysAreaMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.context.ApplicationContext; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * 地区数据同步工具类 * 可以直接运行main方法执行同步 * * 使用方法 * 1. 在IDE中直接运行此类 * 2. 或者在命令行中执行java -cp your-app.jar com.qcby.util.AreaSyncUtil */ Slf4j public class AreaSyncUtil { public static void main(String[] args) { // 启动Spring容器 ApplicationContext context SpringApplication.run(Application.class, args); // 获取Mapper SysAreaMapper areaMapper context.getBean(SysAreaMapper.class); // 执行同步 syncArea(areaMapper); // 同步完成后退出 SpringApplication.exit(context, () - 0); System.exit(0); } public static void syncArea(SysAreaMapper areaMapper) { try { log.info(开始同步百度地区数据...); // 构建请求参数 MapString, String params new LinkedHashMap(); params.put(keyword, 中国); params.put(sub_admin, 4); params.put(extensions_code, 1); // 调用百度API获取地区数据 ListBaiduMapVO baiduMapVOList BaiduMapUtil.requestGetSN(params); if (baiduMapVOList null || baiduMapVOList.isEmpty()) { log.error(获取百度地区数据失败); return; } // 清空现有数据 areaMapper.delete(null); log.info(已清空现有地区数据); // 递归处理并保存地区数据 ListSysArea areaList new ArrayList(); for (BaiduMapVO baiduMapVO : baiduMapVOList) { processArea(null, baiduMapVO, areaList); } // 批量保存到数据库 if (!areaList.isEmpty()) { for (SysArea area : areaList) { areaMapper.insert(area); } log.info(成功同步 {} 条地区数据, areaList.size()); } } catch (Exception e) { log.error(同步地区数据异常: {}, e.getMessage(), e); } } private static void processArea(String parentId, BaiduMapVO baiduMapVO, ListSysArea areaList) { SysArea area new SysArea(); String areaId baiduMapVO.getCode(); area.setAreaId(areaId); area.setAreaName(baiduMapVO.getName()); area.setLevel(convertLevel(baiduMapVO.getLevel())); area.setParentId(parentId); areaList.add(area); ListBaiduMapVO districts baiduMapVO.getDistricts(); if (districts ! null !districts.isEmpty()) { for (BaiduMapVO mapVO : districts) { processArea(areaId, mapVO, areaList); } } } private static Integer convertLevel(String level) { if (level null) return null; // 如果已经是数字字符串直接转换 try { return Integer.parseInt(level); } catch (NumberFormatException e) { // 如果不是数字再按字符串映射 switch (level) { case country: return 0; case province: return 1; case city: return 2; case district: return 3; default: log.warn(未知的层级: {}, 默认设为4, level); return 4; } } } }3.调用关系图执行流程说明启动阶段运行AreaSyncUtil.main()启动Spring容器数据获取调用BaiduMapUtil.requestGetSN()获取百度API数据数据转换API返回BaiduMapVO树形结构processArea()递归遍历转换为SysArea扁平结构数据持久化先清空原有数据areaMapper.delete(null)逐条插入新数据areaMapper.insert(area)结束退出关闭Spring容器退出程序sql文件在顶部