package com.doumee.core.utils.Tencent; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.doumee.core.utils.Http; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.IOException; import java.net.URLEncoder; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * 腾讯地图工具类 * * @Author : Rk * @create 2026/4/14 15:58 */ @Slf4j @Component public class MapUtil { private static String tencentKey; /** 距离矩阵API */ public static final String MATRIX_URL = "https://apis.map.qq.com/ws/distance/v1/matrix"; /** 支持的模式 */ private static final List SUPPORTED_MODES = Arrays.asList("driving", "bicycling"); @Value("${tencent_key}") public void setTencentKey(String tencentKey) { MapUtil.tencentKey = tencentKey; } /** * 批量距离矩阵计算 * * @param mode 模式:driving(驾车)、bicycling(自行车) * @param fromPoints 起点列表,格式:lat,lng(最多10个) * @param toPoints 终点列表,格式:lat,lng(最多20个) * @return result.rows 矩阵数据,每个元素包含 distance(米) 和 duration(秒) */ public static JSONObject distanceMatrix(String mode, List fromPoints, List toPoints) { if (!SUPPORTED_MODES.contains(mode)) { throw new IllegalArgumentException("不支持的模式: " + mode + ",仅支持: " + SUPPORTED_MODES); } if (fromPoints == null || fromPoints.isEmpty() || toPoints == null || toPoints.isEmpty()) { throw new IllegalArgumentException("起点和终点列表不能为空"); } String from = String.join(";", fromPoints); String to = String.join(";", toPoints); try { String url = MATRIX_URL + "?key=" + tencentKey + "&mode=" + mode + "&from=" + URLEncoder.encode(from, "UTF-8") + "&to=" + URLEncoder.encode(to, "UTF-8"); log.info("腾讯地图矩阵API请求: mode={}, from={}, to={}", mode, from, to); JSONObject json = new Http().build(url) .setConnectTimeout(5000) .setReadTimeout(10000) .get() .toJSONObject(); log.info("腾讯地图矩阵API响应: {}", json); if (json.getIntValue("status") != 0) { throw new RuntimeException("腾讯地图矩阵API调用失败: " + json.getString("message")); } return json.getJSONObject("result"); } catch (IOException e) { log.error("腾讯地图矩阵API调用异常", e); throw new RuntimeException("腾讯地图矩阵API调用异常", e); } } /** * 单对距离计算(便捷方法) * * @param mode 模式:driving(驾车)、bicycling(自行车) * @param from 起点格式:lat,lng * @param to 终点格式:lat,lng * @return 第一个元素的 distance(米) 和 duration(秒) */ public static JSONObject distanceSingle(String mode, String from, String to) { JSONObject result = distanceMatrix(mode, Arrays.asList(from), Arrays.asList(to)); JSONArray rows = result.getJSONArray("rows"); if (rows != null && !rows.isEmpty()) { JSONArray elements = rows.getJSONObject(0).getJSONArray("elements"); if (elements != null && !elements.isEmpty()) { return elements.getJSONObject(0); } } return new JSONObject(); } /** * 多起点到单终点(便捷方法) * 返回每个起点到终点的距离和耗时,顺序与fromPoints对应 * * @param mode 模式 * @param fromPoints 起点列表 * @param to 单个终点 * @return 距离耗时列表,顺序与fromPoints对应 */ public static List distanceToOne(String mode, List fromPoints, String to) { JSONObject result = distanceMatrix(mode, fromPoints, Arrays.asList(to)); JSONArray rows = result.getJSONArray("rows"); return rows == null ? Arrays.asList() : rows.stream() .map(row -> ((JSONObject) row).getJSONArray("elements").getJSONObject(0)) .collect(Collectors.toList()); } }