疯狂餐厅
86.88M · 2026-03-21
大家好,我是小悟。
腾讯地图(Tencent Map)是腾讯公司推出的一款数字地图服务,提供丰富的地图展示、定位、搜索、导航等功能。作为国内领先的地图服务提供商,腾讯地图拥有以下特点:
使用IDE创建项目,选择以下依赖:
src/main/java/com/example/mapdemo/
├── MapDemoApplication.java
├── config/
│ └── TencentMapConfig.java
├── controller/
│ └── MapController.java
├── service/
│ ├── TencentMapService.java
│ └── impl/
│ └── TencentMapServiceImpl.java
├── dto/
│ ├── request/
│ │ └── LocationRequest.java
│ └── response/
│ └── MapResponse.java
└── utils/
└── HttpClientUtil.java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
d">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
</parent>
<groupId>com.example</groupId>
<artifactId>tencent-map-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Configuration Processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<!-- FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<!-- Commons Lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
server:
port: 8080
tencent:
map:
key: 你的腾讯地图Key
secret-key: 你的密钥(可选,用于数字签名)
base-url:
connect-timeout: 5000
read-timeout: 5000
logging:
level:
com.example.mapdemo: DEBUG
package com.example.mapdemo.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "tencent.map")
public class TencentMapConfig {
private String key;
private String secretKey;
private String baseUrl = "https://apis.map.qq.com";
private int connectTimeout = 5000;
private int readTimeout = 5000;
}
LocationRequest.java - 请求参数
package com.example.mapdemo.dto.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class LocationRequest {
@NotBlank(message = "地址不能为空")
private String address;
private String city; // 城市名称,可选
private Double latitude; // 纬度
private Double longitude; // 经度
private Integer radius = 1000; // 搜索半径,默认1000米
private String keyword; // 搜索关键词
}
MapResponse.java - 响应结果
package com.example.mapdemo.dto.response;
import lombok.Builder;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
@Builder
public class MapResponse<T> {
private Integer status; // 状态码,0为成功
private String message; // 状态信息
private T data; // 返回数据
private Long requestTime; // 请求时间戳
public static <T> MapResponse<T> success(T data) {
return MapResponse.<T>builder()
.status(0)
.message("success")
.data(data)
.requestTime(System.currentTimeMillis())
.build();
}
public static <T> MapResponse<T> error(Integer status, String message) {
return MapResponse.<T>builder()
.status(status)
.message(message)
.requestTime(System.currentTimeMillis())
.build();
}
}
package com.example.mapdemo.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Slf4j
@Component
public class HttpClientUtil {
private final CloseableHttpClient httpClient;
private final RequestConfig requestConfig;
public HttpClientUtil() {
this.httpClient = HttpClients.createDefault();
this.requestConfig = RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.setConnectionRequestTimeout(5000)
.build();
}
/**
* GET请求
*/
public String doGet(String url, Map<String, String> params) {
try {
URIBuilder uriBuilder = new URIBuilder(url);
if (params != null && !params.isEmpty()) {
params.forEach(uriBuilder::addParameter);
}
URI uri = uriBuilder.build();
HttpGet httpGet = new HttpGet(uri);
httpGet.setConfig(requestConfig);
httpGet.setHeader("Content-Type", "application/json;charset=utf8");
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
log.debug("GET请求响应: {}", result);
return result;
}
}
} catch (Exception e) {
log.error("GET请求异常", e);
}
return null;
}
/**
* POST请求(JSON格式)
*/
public String doPostJson(String url, String json) {
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
StringEntity stringEntity = new StringEntity(json, StandardCharsets.UTF_8);
httpPost.setEntity(stringEntity);
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
log.debug("POST请求响应: {}", result);
return result;
}
}
} catch (Exception e) {
log.error("POST请求异常", e);
}
return null;
}
}
package com.example.mapdemo.service;
import com.example.mapdemo.dto.request.LocationRequest;
import com.example.mapdemo.dto.response.MapResponse;
import java.util.Map;
public interface TencentMapService {
/**
* 地理编码(地址转坐标)
*/
MapResponse<?> geocoder(String address, String city);
/**
* 逆地理编码(坐标转地址)
*/
MapResponse<?> reverseGeocoder(Double latitude, Double longitude);
/**
* 地点搜索
*/
MapResponse<?> searchPoi(String keyword, Double latitude, Double longitude, Integer radius);
/**
* 驾车路线规划
*/
MapResponse<?> drivingRoute(String origin, String destination);
/**
* 距离矩阵计算
*/
MapResponse<?> distanceMatrix(String[] origins, String[] destinations);
/**
* IP定位
*/
MapResponse<?> ipLocation(String ip);
/**
* 天气查询
*/
MapResponse<?> weather(Double latitude, Double longitude);
}
package com.example.mapdemo.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.mapdemo.config.TencentMapConfig;
import com.example.mapdemo.dto.response.MapResponse;
import com.example.mapdemo.service.TencentMapService;
import com.example.mapdemo.utils.HttpClientUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class TencentMapServiceImpl implements TencentMapService {
private final TencentMapConfig mapConfig;
private final HttpClientUtil httpClientUtil;
/**
* 地理编码 - 地址转坐标
* API文档:
*/
@Override
public MapResponse<?> geocoder(String address, String city) {
try {
Map<String, String> params = new HashMap<>();
params.put("key", mapConfig.getKey());
params.put("address", address);
if (StringUtils.hasText(city)) {
params.put("region", city);
}
String url = mapConfig.getBaseUrl() + "/ws/geocoder/v1/";
String result = httpClientUtil.doGet(url, params);
JSONObject jsonResult = JSON.parseObject(result);
if (jsonResult.getInteger("status") == 0) {
JSONObject location = jsonResult.getJSONObject("result").getJSONObject("location");
return MapResponse.success(location);
} else {
return MapResponse.error(jsonResult.getInteger("status"),
jsonResult.getString("message"));
}
} catch (Exception e) {
log.error("地理编码失败", e);
return MapResponse.error(-1, "地理编码失败:" + e.getMessage());
}
}
/**
* 逆地理编码 - 坐标转地址
*/
@Override
public MapResponse<?> reverseGeocoder(Double latitude, Double longitude) {
try {
Map<String, String> params = new HashMap<>();
params.put("key", mapConfig.getKey());
params.put("location", latitude + "," + longitude);
params.put("get_poi", "1"); // 是否返回周边POI
String url = mapConfig.getBaseUrl() + "/ws/geocoder/v1/";
String result = httpClientUtil.doGet(url, params);
JSONObject jsonResult = JSON.parseObject(result);
if (jsonResult.getInteger("status") == 0) {
return MapResponse.success(jsonResult.getJSONObject("result"));
} else {
return MapResponse.error(jsonResult.getInteger("status"),
jsonResult.getString("message"));
}
} catch (Exception e) {
log.error("逆地理编码失败", e);
return MapResponse.error(-1, "逆地理编码失败:" + e.getMessage());
}
}
/**
* 地点搜索
*/
@Override
public MapResponse<?> searchPoi(String keyword, Double latitude, Double longitude, Integer radius) {
try {
Map<String, String> params = new HashMap<>();
params.put("key", mapConfig.getKey());
params.put("keyword", keyword);
params.put("boundary", "nearby(" + latitude + "," + longitude + "," + radius + ")");
params.put("page_size", "20");
params.put("page_index", "1");
String url = mapConfig.getBaseUrl() + "/ws/place/v1/search/";
String result = httpClientUtil.doGet(url, params);
JSONObject jsonResult = JSON.parseObject(result);
if (jsonResult.getInteger("status") == 0) {
return MapResponse.success(jsonResult.getJSONObject("data"));
} else {
return MapResponse.error(jsonResult.getInteger("status"),
jsonResult.getString("message"));
}
} catch (Exception e) {
log.error("地点搜索失败", e);
return MapResponse.error(-1, "地点搜索失败:" + e.getMessage());
}
}
/**
* 驾车路线规划
*/
@Override
public MapResponse<?> drivingRoute(String origin, String destination) {
try {
Map<String, String> params = new HashMap<>();
params.put("key", mapConfig.getKey());
params.put("from", origin);
params.put("to", destination);
params.put("output", "json");
String url = mapConfig.getBaseUrl() + "/ws/direction/v1/driving/";
String result = httpClientUtil.doGet(url, params);
JSONObject jsonResult = JSON.parseObject(result);
if (jsonResult.getInteger("status") == 0) {
return MapResponse.success(jsonResult.getJSONObject("result"));
} else {
return MapResponse.error(jsonResult.getInteger("status"),
jsonResult.getString("message"));
}
} catch (Exception e) {
log.error("路线规划失败", e);
return MapResponse.error(-1, "路线规划失败:" + e.getMessage());
}
}
/**
* 距离矩阵计算
*/
@Override
public MapResponse<?> distanceMatrix(String[] origins, String[] destinations) {
try {
Map<String, String> params = new HashMap<>();
params.put("key", mapConfig.getKey());
params.put("from", String.join(";", origins));
params.put("to", String.join(";", destinations));
params.put("mode", "driving"); // 驾车模式
String url = mapConfig.getBaseUrl() + "/ws/distance/v1/matrix/";
String result = httpClientUtil.doGet(url, params);
JSONObject jsonResult = JSON.parseObject(result);
if (jsonResult.getInteger("status") == 0) {
return MapResponse.success(jsonResult.getJSONObject("result"));
} else {
return MapResponse.error(jsonResult.getInteger("status"),
jsonResult.getString("message"));
}
} catch (Exception e) {
log.error("距离矩阵计算失败", e);
return MapResponse.error(-1, "距离矩阵计算失败:" + e.getMessage());
}
}
/**
* IP定位
*/
@Override
public MapResponse<?> ipLocation(String ip) {
try {
Map<String, String> params = new HashMap<>();
params.put("key", mapConfig.getKey());
params.put("ip", ip);
params.put("output", "json");
String url = mapConfig.getBaseUrl() + "/ws/location/v1/ip/";
String result = httpClientUtil.doGet(url, params);
JSONObject jsonResult = JSON.parseObject(result);
if (jsonResult.getInteger("status") == 0) {
return MapResponse.success(jsonResult.getJSONObject("result"));
} else {
return MapResponse.error(jsonResult.getInteger("status"),
jsonResult.getString("message"));
}
} catch (Exception e) {
log.error("IP定位失败", e);
return MapResponse.error(-1, "IP定位失败:" + e.getMessage());
}
}
/**
* 天气查询
*/
@Override
public MapResponse<?> weather(Double latitude, Double longitude) {
try {
Map<String, String> params = new HashMap<>();
params.put("key", mapConfig.getKey());
params.put("location", latitude + "," + longitude);
params.put("output", "json");
String url = mapConfig.getBaseUrl() + "/ws/weather/v1/";
String result = httpClientUtil.doGet(url, params);
JSONObject jsonResult = JSON.parseObject(result);
if (jsonResult.getInteger("status") == 0) {
return MapResponse.success(jsonResult.getJSONObject("result"));
} else {
return MapResponse.error(jsonResult.getInteger("status"),
jsonResult.getString("message"));
}
} catch (Exception e) {
log.error("天气查询失败", e);
return MapResponse.error(-1, "天气查询失败:" + e.getMessage());
}
}
}
package com.example.mapdemo.controller;
import com.example.mapdemo.dto.request.LocationRequest;
import com.example.mapdemo.dto.response.MapResponse;
import com.example.mapdemo.service.TencentMapService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Slf4j
@RestController
@RequestMapping("/api/map")
@RequiredArgsConstructor
public class MapController {
private final TencentMapService mapService;
/**
* 地理编码(地址转坐标)
*/
@GetMapping("/geocoder")
public MapResponse<?> geocoder(
@RequestParam String address,
@RequestParam(required = false) String city) {
log.info("地理编码请求 - 地址: {}, 城市: {}", address, city);
return mapService.geocoder(address, city);
}
/**
* 逆地理编码(坐标转地址)
*/
@GetMapping("/reverse-geocoder")
public MapResponse<?> reverseGeocoder(
@RequestParam Double latitude,
@RequestParam Double longitude) {
log.info("逆地理编码请求 - 坐标: ({}, {})", latitude, longitude);
return mapService.reverseGeocoder(latitude, longitude);
}
/**
* 地点搜索
*/
@GetMapping("/search")
public MapResponse<?> search(
@RequestParam String keyword,
@RequestParam Double latitude,
@RequestParam Double longitude,
@RequestParam(defaultValue = "1000") Integer radius) {
log.info("地点搜索请求 - 关键词: {}, 坐标: ({}, {}), 半径: {}",
keyword, latitude, longitude, radius);
return mapService.searchPoi(keyword, latitude, longitude, radius);
}
/**
* 路线规划
*/
@GetMapping("/route")
public MapResponse<?> route(
@RequestParam String origin,
@RequestParam String destination) {
log.info("路线规划请求 - 起点: {}, 终点: {}", origin, destination);
return mapService.drivingRoute(origin, destination);
}
/**
* IP定位
*/
@GetMapping("/ip-location")
public MapResponse<?> ipLocation(@RequestParam String ip) {
log.info("IP定位请求 - IP: {}", ip);
return mapService.ipLocation(ip);
}
/**
* 天气查询
*/
@GetMapping("/weather")
public MapResponse<?> weather(
@RequestParam Double latitude,
@RequestParam Double longitude) {
log.info("天气查询请求 - 坐标: ({}, {})", latitude, longitude);
return mapService.weather(latitude, longitude);
}
/**
* 距离矩阵计算
*/
@PostMapping("/distance-matrix")
public MapResponse<?> distanceMatrix(@Valid @RequestBody LocationRequest request) {
// 这里简化处理,实际应根据请求构建参数
String[] origins = {request.getLatitude() + "," + request.getLongitude()};
String[] destinations = {"39.984154,116.307490", "39.995120,116.327450"}; // 示例坐标
return mapService.distanceMatrix(origins, destinations);
}
}
package com.example.mapdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties
public class MapDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MapDemoApplication.class, args);
}
}
运行 MapDemoApplication.java 的 main 方法
curl "http://localhost:8080/api/map/geocoder?address=北京市海淀区&city=北京"
curl "http://localhost:8080/api/map/search?keyword=餐厅&latitude=39.984154&longitude=116.307490&radius=2000"
// 可以考虑使用Redis缓存高频查询结果
@Cacheable(value = "geocoder", key = "#address + '_' + #city")
public MapResponse<?> geocoder(String address, String city) {
// 实现代码
}
// 优化HttpClient配置
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(200); // 最大连接数
connectionManager.setDefaultMaxPerRoute(20); // 每个路由最大连接数
// 使用CompletableFuture实现异步调用
@Async
public CompletableFuture<MapResponse<?>> asyncGeocoder(String address) {
return CompletableFuture.completedFuture(geocoder(address, null));
}
// 添加签名验证(如腾讯地图支持)
public String generateSignature(Map<String, String> params) {
// 按照腾讯地图签名规则生成签名
// 1. 参数排序
// 2. 拼接字符串
// 3. MD5加密
}
// 添加接口限流
@RateLimiter(limit = 10, timeout = 1)
public MapResponse<?> geocoder(String address) {
// 实现代码
}
@Slf4j
@Component
public class MapApiInterceptor {
@Around("execution(* com.example.mapdemo.service.*.*(..))")
public Object logApiCall(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
String methodName = pjp.getSignature().getName();
try {
Object result = pjp.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("API调用 - {} - 耗时: {}ms", methodName, duration);
return result;
} catch (Exception e) {
log.error("API调用异常 - {}", methodName, e);
throw e;
}
}
}
@Endpoint(id = "map")
@Component
public class MapHealthEndpoint {
private final TencentMapService mapService;
@ReadOperation
public Map<String, Object> health() {
Map<String, Object> health = new HashMap<>();
try {
// 简单测试API可用性
mapService.geocoder("北京市", null);
health.put("status", "UP");
} catch (Exception e) {
health.put("status", "DOWN");
health.put("error", e.getMessage());
}
return health;
}
}
| 状态码 | 含义 | 解决方案 |
|---|---|---|
| 0 | 成功 | - |
| 110 | 请求来源非法 | 检查Key是否正确 |
| 311 | 参数缺失 | 检查必填参数 |
| 320 | 请求超过配额 | 升级服务或优化调用 |
| 403 | 请求被拒绝 | 检查IP白名单设置 |
通过以上步骤,实现了SpringBoot与腾讯地图SDK的集成,实现了以下核心功能:
谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。
您的一键三连,是我更新的最大动力,谢谢
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海