义乌商城网站开发,临沂网站建设联系方式,公司做网站要花多少钱,企业网站怎么做的好看一#xff1a;高德开放平台的使用 高德开放平台 | 高德地图API 注册高德地图账号 认证填写个人信息#xff1a; 认证方式选择“个人认证开发者”即可#xff0c;然后完善信息 认证成功之后#xff0c;再次进入控制台#xff0c;创建关于地图的应用 创建Key#xff08;y…一高德开放平台的使用 高德开放平台 | 高德地图API 注册高德地图账号 认证填写个人信息 认证方式选择“个人认证开发者”即可然后完善信息 认证成功之后再次进入控制台创建关于地图的应用 创建Keyyml文件需要使用 以上步骤便可以完成高德地图的申请和key的创建。 开始Springboot的创建就不从0到一的创建了 二创建数据库我是用的是MySQL 建表语句
CREATE TABLE location_record (id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键ID,ip VARCHAR(50) DEFAULT NULL COMMENT 客户端IP地址,longitude DOUBLE(10,6) NOT NULL COMMENT 经度坐标精确到小数点后6位,latitude DOUBLE(10,6) NOT NULL COMMENT 纬度坐标精确到小数点后6位,address VARCHAR(255) DEFAULT NULL COMMENT 详细地址信息,formatted_address VARCHAR(255) DEFAULT NULL COMMENT 格式化后的完整地址,city VARCHAR(100) DEFAULT NULL COMMENT 所在城市名称,province VARCHAR(100) DEFAULT NULL COMMENT 所在省份名称,district VARCHAR(100) DEFAULT NULL COMMENT 新增所在区县名称,street VARCHAR(100) DEFAULT NULL COMMENT 新增街道信息,create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 记录创建时间,update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT 记录更新时间,PRIMARY KEY (id),KEY idx_location (longitude,latitude),KEY idx_create_time (create_time)
) ENGINEINNODB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_general_ci COMMENT地理位置信息记录表; 表中样式 三Springboot所需的依赖根据你的需求再去增加删除依赖
dependencies!-- FastJSON --dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.76/version/dependency!-- OkHttp --dependencygroupIdcom.squareup.okhttp3/groupIdartifactIdokhttp/artifactIdversion4.12.0/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion 1.2.23/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.33/version/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.6/version/dependency!-- Spring Boot Starter Web --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdversion2.6.13/version/dependency!-- Apache HttpClient --dependencygroupIdorg.apache.httpcomponents/groupIdartifactIdhttpclient/artifactIdversion4.5.13/version/dependency!-- Spring Boot Configuration Processor --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactIdversion2.6.13/versionoptionaltrue/optional/dependency!-- Lombok --dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion1.18.26/version/dependency
/dependencies 四yml文件的配置
server:port: 8096spring:amap:key: 你的高德地图key值version: 2.0geocode-url: https://restapi.amap.com/v3/geocode/geoip-url: https://restapi.amap.com/v3/ipregeo-url: https://restapi.amap.com/v3/geocode/regeodatasource: # 数据源配置driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/你所建表对应的数据库?serverTimezoneAsia/ShanghaiuseSSLfalseusername: rootpassword: roottype: com.alibaba.druid.pool.DruidDataSource# mybatis-plus配置,也可以不加,只是为了显示SQL,Springboot都自动配置好了
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl五所需要的类 AmapConfig
package com.qfedu.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;Data
Configuration
ConfigurationProperties(prefix spring.amap)
public class AmapConfig {private String key; // 对应配置中的 keyprivate String securityJsCode; // 对应 security-js-codeprivate String version 2.0; // 默认版本号private String geocodeUrl; // 对应 geocode.urlprivate String ipUrl; // 对应 ip.urlprivate String regeoUrl; // 对应 regeo.url
}
WebMvcConfig因为我的项目中配置了拦截器所以需要这样一个类
package com.qfedu.config;import com.qfedu.common.core.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;Configuration
public class WebMvcConfig implements WebMvcConfigurer {Beanpublic LoginInterceptor loginInterceptor() {return new LoginInterceptor();}Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor()).addPathPatterns(/**).excludePathPatterns(/api/amap/**,/api/location/** // 新增排除location相关路径);}
}
HttpUtils
package com.qfedu.utils;import com.sun.deploy.net.URLEncoder;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;import java.io.IOException;
import java.nio.charset.StandardCharsets;public class HttpUtil {public static String doGet(String url) throws IOException {if (url null || url.trim().isEmpty()) {throw new IllegalArgumentException(URL不能为空);}CloseableHttpClient httpClient HttpClients.createDefault();HttpGet httpGet new HttpGet(url);// 设置请求头httpGet.setHeader(Accept, application/json);httpGet.setHeader(Content-Type, application/json);try (CloseableHttpResponse response httpClient.execute(httpGet)) {int statusCode response.getStatusLine().getStatusCode();if (statusCode ! 200) {throw new IOException(HTTP请求失败状态码: statusCode , URL: url);}HttpEntity entity response.getEntity();if (entity null) {throw new IOException(响应体为空);}return EntityUtils.toString(entity, StandardCharsets.UTF_8);} finally {httpClient.close();}}public static String encodeUrlParam(String param) {try {return URLEncoder.encode(param, StandardCharsets.UTF_8.name());} catch (Exception e) {return param;}}
}
返回值R类我的R类没有使用泛型
package com.qfedu.common.core.common;public class R {/*** code,指状态码,* 随意定,20000 是正确,40000 错误* 50000 请求超时* 60000 没有权限* msg,指信息描述* data,返回的数据*/private int code;private String msg;private Object data;public static R ok() {R r new R();r.setCode(200);r.setMsg(成功);return r;}public static R ok(Object data) {R r new R();r.setCode(200);r.setMsg(成功);r.setData(data);return r;}public static R fail() {R r new R();r.setCode(500);r.setMsg(失败);return r;}public static R fail(String msg) {R r new R();r.setCode(500);r.setMsg(msg);return r;}public int getCode() {return code;}public void setCode(int code) {this.code code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg msg;}public Object getData() {return data;}public void setData(Object data) {this.data data;}}免登录自定义注解
package com.qfedu.common.core.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 以梦为马以汗为泉不忘初心不负韶华** author ${上官箫宇}* version 1.0* data 2025/6/3 16:08*/
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)//注解运行时生效
public interface NoLogin {
}拦截器
package com.qfedu.common.core.interceptor;import com.qfedu.common.core.annotation.NoLogin;
import com.qfedu.common.core.constants.CommonConstants;
import com.qfedu.common.core.utils.JwtUtils;
import com.qfedu.common.core.utils.UserUtils;
import io.jsonwebtoken.Claims;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;/*** ---Do First---** author:wellseasun* date:2025/5/22 下午 8:30* desc:*/
public class LoginInterceptor implements HandlerInterceptor {// preHandle执行时机访问接口前Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// handler表示处理某个请求的处理器对象// 如果是类级别的拦截器则handler为类对象如果是方法级别的拦截器则handler为方法对象if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod (HandlerMethod) handler;Method method handlerMethod.getMethod();boolean annotationPresent method.isAnnotationPresent(NoLogin.class);if (annotationPresent) {// 如果使用了注解直接放行return true;} else {// 没有使用注解需要从请求头中获取名为LOGIN_TOKEN的tokenString token request.getHeader(CommonConstants.LOGIN_TOKEN);if (token null || token.isEmpty()) {throw new RuntimeException(请重新登录);}try {JwtUtils jwtUtils new JwtUtils();Claims claims jwtUtils.parseJWT(token);UserUtils.setUid((Integer) claims.get(uid));} catch (Exception e) {throw e;}}}return true;}}没错下面就是层级关系
mapper
package com.qfedu.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qfedu.common.core.entity.LocationRecord;public interface LocationRecordMapper extends BaseMapperLocationRecord {
}
service
package com.qfedu.service;import com.alibaba.fastjson.JSONObject;
import com.qfedu.common.core.entity.LocationRecord;public interface AmapService {/*** IP定位* param ip IP地址* return 定位结果*/JSONObject ipLocation(String ip);/*** 逆地理编码* param longitude 经度* param latitude 纬度* return 地址信息*/JSONObject regeoLocation(Double longitude, Double latitude);/*** 地理编码* param address 地址* return 经纬度信息*/JSONObject geoLocation(String address);/*** 保存定位记录* param record 定位记录* return 是否成功*/boolean saveLocationRecord(LocationRecord record);
}
service实现类
package com.qfedu.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qfedu.common.core.entity.LocationRecord;
import com.qfedu.config.AmapConfig;
import com.qfedu.mapper.LocationRecordMapper;
import com.qfedu.service.AmapService;
import com.qfedu.utils.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import javax.annotation.PostConstruct;/*** 高德地图服务实现类* 提供IP定位、地理编码、逆地理编码等核心功能*/
Service
public class AmapServiceImpl extends ServiceImplLocationRecordMapper, LocationRecordimplements AmapService {private static final Logger logger LoggerFactory.getLogger(AmapServiceImpl.class);Autowiredprivate AmapConfig amapConfig; // 高德配置参数key/url等/*** 初始化时打印配置信息调试用*/PostConstructpublic void init() {logger.info(高德地图配置加载: key{}, ipUrl{}, geoUrl{}, regeoUrl{},amapConfig.getKey(),amapConfig.getIpUrl(),amapConfig.getGeocodeUrl(),amapConfig.getRegeoUrl());}// 核心服务方法 /*** IP定位服务* param ip 需要查询的IP地址* return 包含定位结果的JSON对象status1成功0失败*/Overridepublic JSONObject ipLocation(String ip) {// 参数校验if (!StringUtils.hasText(ip)) {return createErrorResponse(IP地址不能为空);}try {// 配置校验validateConfig(amapConfig.getKey(), amapConfig.getIpUrl());// 构建请求URL示例https://restapi.amap.com/v3/ip?keyxxxip8.8.8.8String url String.format(%s?key%sip%s,amapConfig.getIpUrl().trim(),amapConfig.getKey().trim(),HttpUtil.encodeUrlParam(ip));validateUrl(url); // URL格式校验logger.info(请求高德IP定位API: {}, url);// 发送HTTP请求并解析响应String response HttpUtil.doGet(url);logger.debug(高德IP定位API响应: {}, response);return parseResponse(response);} catch (Exception e) {logger.error(IP定位失败, IP: ip, e);return createErrorResponse(IP定位失败: e.getMessage());}}/*** 逆地理编码服务坐标→地址* param longitude 经度* param latitude 纬度* return 包含地址信息的JSON响应*/Overridepublic JSONObject regeoLocation(Double longitude, Double latitude) {if (longitude null || latitude null) {return createErrorResponse(经纬度不能为空);}try {validateConfig(amapConfig.getKey(), null); // 仅校验Key// 构建请求URL示例https://restapi.amap.com/v3/geocode/regeo?keyxxxlocation116.4,39.9String location longitude , latitude;String url String.format(%s?key%slocation%s,amapConfig.getRegeoUrl(),amapConfig.getKey(),HttpUtil.encodeUrlParam(location));logger.debug(请求高德逆地理编码API: {}, url);String response HttpUtil.doGet(url);logger.debug(高德逆地理编码API响应: {}, response);return parseResponse(response);} catch (Exception e) {logger.error(逆地理编码失败, 位置: longitude , latitude, e);return createErrorResponse(逆地理编码失败: getErrorMessage(e));}}/*** 地理编码服务地址→坐标* param address 结构化地址如北京市海淀区中关村大街1号* return 包含经纬度的JSON响应*/Overridepublic JSONObject geoLocation(String address) {if (!StringUtils.hasText(address)) {return createErrorResponse(地址不能为空);}try {validateConfig(amapConfig.getKey(), null); // 仅校验Key// 构建请求URL示例https://restapi.amap.com/v3/geocode/geo?keyxxxaddress北京String url String.format(%s?key%saddress%s,amapConfig.getGeocodeUrl(),amapConfig.getKey(),HttpUtil.encodeUrlParam(address));logger.debug(请求高德地理编码API: {}, url);String response HttpUtil.doGet(url);logger.debug(高德地理编码API响应: {}, response);return parseResponse(response);} catch (Exception e) {logger.error(地理编码失败, 地址: address, e);return createErrorResponse(地理编码失败: getErrorMessage(e));}}/*** 保存定位记录到数据库* param record 定位记录实体* return 是否保存成功*/Overridepublic boolean saveLocationRecord(LocationRecord record) {try {return this.save(record); // 调用MyBatis-Plus的save方法} catch (Exception e) {logger.error(保存定位记录失败, e);return false;}}// 内部工具方法 /*** 校验高德配置参数* param key 高德API Key* param url 需要校验的API地址可选* throws IllegalStateException 当配置不合法时抛出*/private void validateConfig(String key, String url) {if (amapConfig null || !StringUtils.hasText(key)) {throw new IllegalStateException(高德地图配置未正确初始化);}if (url ! null !StringUtils.hasText(url)) {throw new IllegalStateException(高德API地址未配置);}}/*** 校验URL合法性* param url 待校验的URL* throws IllegalArgumentException 当URL非法时抛出*/private void validateUrl(String url) {if (!url.startsWith(http)) {throw new IllegalArgumentException(无效的API URL: url);}}/*** 解析高德API响应* param response 原始JSON字符串* return 解析后的JSONObject*/private JSONObject parseResponse(String response) {if (!StringUtils.hasText(response)) {return createErrorResponse(空响应);}try {JSONObject result JSON.parseObject(response);return result ! null ? result : createErrorResponse(响应解析失败);} catch (Exception e) {logger.error(解析高德API响应失败, e);return createErrorResponse(响应解析失败: e.getMessage());}}/*** 创建错误响应* param message 错误信息* return 标准化错误JSONstatus0*/private JSONObject createErrorResponse(String message) {JSONObject result new JSONObject();result.put(status, 0); // 高德标准错误码result.put(info, message);return result;}/*** 提取异常信息避免null*/private String getErrorMessage(Exception e) {return e.getMessage() ! null ? e.getMessage() : 未知错误;}
}
controller
package com.qfedu.controller;import com.alibaba.fastjson.JSONObject;
import com.qfedu.common.core.annotation.NoLogin;
import com.qfedu.common.core.common.R;
import com.qfedu.common.core.entity.LocationRecord;
import com.qfedu.service.AmapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.Date;/*** 高德地图定位服务控制器* 提供IP定位、逆地理编码、地理编码三大核心功能*/
RestController
RequestMapping(/api/location)
public class LocationController {Autowiredprivate AmapService amapService; // 高德地图服务接口/*** IP定位接口* param ip 需要定位的IP地址如8.8.8.8* return 标准化响应RJSONObject包含定位结果或错误信息*/GetMapping(/ip)NoLoginpublic R locateByIp(RequestParam String ip) {// 调用高德IP定位服务JSONObject result amapService.ipLocation(ip);// 校验高德API返回状态码1成功if (result ! null 1.equals(result.getString(status))) {saveLocationRecord(ip, result); // 持久化定位记录return R.ok(result); // 返回成功响应}// 失败时返回错误信息优先使用高德返回的info字段return R.fail(result ! null ? result.getString(info) : IP定位服务不可用);}/*** 逆地理编码接口坐标→地址* param longitude 经度如116.404* param latitude 纬度如39.915* return 包含地址信息的标准化响应*/GetMapping(/regeo)NoLoginpublic R regeo(RequestParam Double longitude,RequestParam Double latitude) {JSONObject result amapService.regeoLocation(longitude, latitude);if (result ! null 1.equals(result.getString(status))) {saveLocationRecord(null, longitude, latitude, result); // IP传nullreturn R.ok(result);}return R.fail(result ! null ? result.getString(info) : 逆地理编码服务不可用);}/*** 地理编码接口地址→坐标* param address 结构化地址如北京市海淀区中关村大街1号* return 包含经纬度的标准化响应*/GetMapping(/geo)NoLoginpublic R geo(RequestParam String address) {JSONObject result amapService.geoLocation(address);if (result ! null 1.equals(result.getString(status))) {return R.ok(result); // 地理编码不保存记录}return R.fail(result ! null ? result.getString(info) : 地理编码服务不可用);}// 内部工具方法/*** 从IP定位结果提取经纬度并保存记录* param ip IP地址* param result 高德API返回的完整结果*/private void saveLocationRecord(String ip, JSONObject result) {JSONObject locationObj result.getJSONObject(location);if (locationObj ! null) {saveLocationRecord(ip,locationObj.getDouble(lng), // 经度字段locationObj.getDouble(lat), // 纬度字段result);}}/*** 保存定位记录到数据库核心方法* param ip 可能为null当来源是逆地理编码时* param longitude 经度必填* param latitude 纬度必填* param result 高德API原始结果用于提取地址信息*/private void saveLocationRecord(String ip,Double longitude,Double latitude,JSONObject result) {if (result null) return;// 1. 构建定位记录实体LocationRecord record new LocationRecord();record.setIp(ip); // IP可能为nullrecord.setLongitude(longitude);record.setLatitude(latitude);// 2. 提取格式化地址如北京市海淀区中关村大街1号String formattedAddress result.getString(formatted_address);record.setAddress(formattedAddress);record.setFormattedAddress(formattedAddress);// 3. 提取结构化地址组件省、市、区等JSONObject addressComponent result.getJSONObject(addressComponent);if (addressComponent ! null) {record.setProvince(addressComponent.getString(province));record.setCity(addressComponent.getString(city));// 可扩展district区、street街道等字段}// 4. 设置时间戳并保存record.setCreateTime(new Date());amapService.saveLocationRecord(record); // 调用MyBatis-Plus持久化}
}
下面就是启动类首先不要忘记启动类的扫描注解还有就是本次用到的注解
EnableConfigurationProperties({AmapConfig.class})// 启用AmapConfig配置类
package com.qfedu;import com.qfedu.config.AmapConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;SpringBootApplication
MapperScan(com.qfedu.mapper)
EnableConfigurationProperties({AmapConfig.class})// 启用AmapConfig配置类public class MicroServeAmapApplication {public static void main(String[] args) {SpringApplication.run(MicroServeAmapApplication.class, args);}}写一个简单的页面展示吧位置你们知道吧我就不说详细了
展示台湾省地图吧中国一点都不能少 下面就是展示的代码我只不过吧经纬度写成死值了
!DOCTYPE html
html
headmeta charsetutf-8title高德地图展示测试台湾省台北市地图 - 台北101/title!-- 引入高德地图JS API --script srchttps://webapi.amap.com/maps?v2.0key你的高德地图key/scriptstyle#container {width: 100%;height: 600px;}/style
/head
body
!-- 地图容器 --
div idcontainer/divscript// 初始化地图中心点设为台北市var map new AMap.Map(container, {zoom: 14, // 缩放级别center: [121.5654, 25.0330], // 台北市坐标viewMode: 2D // 2D地图});// 添加标记点台北101大楼var marker new AMap.Marker({position: [121.5654, 25.0330], // 台北101坐标map: map});// 信息窗口内容var infoWindow new AMap.InfoWindow({content: div stylepadding:5px;台北101大楼/div,offset: new AMap.Pixel(0, -30)});infoWindow.open(map, marker.getPosition());
/script
/body
/html