wlzboy
2025-11-16 f67945d53b20f6a45ae50b27d74c966eb1355bb4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleGpsController.java
@@ -3,9 +3,15 @@
import java.util.*;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.service.*;
import com.ruoyi.common.config.TencentMapConfig;
import com.ruoyi.common.config.BaiduMapConfig;
import com.ruoyi.common.config.TiandituMapConfig;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -24,6 +30,7 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.http.HttpUtils;
/**
 * 车辆GPS坐标Controller
@@ -51,6 +58,15 @@
    @Autowired
    private ICmsGpsCollectService cmsGpsCollectService;
    @Autowired
    private TencentMapConfig tencentMapConfig;
    @Autowired
    private BaiduMapConfig baiduMapConfig;
    @Autowired
    private TiandituMapConfig tiandituMapConfig;
   /**
     * 查询车辆GPS坐标列表
@@ -233,13 +249,14 @@
            }
            // 处理开始时间
                beginTime = beginTime.replace("T", " ").replace(" ","%20");
                beginTime = beginTime.replace("T", " ");
                if (beginTime.split(":").length == 2) { // 只有小时和分钟
                    beginTime += ":00";
                }
                
                // 处理结束时间
                endTime = endTime.replace("T", " ").replace(" ","%20");
//                endTime = endTime.replace("T", " ").replace(" ","%20");
            endTime = endTime.replace("T", " ");
                if (endTime.split(":").length == 2) { // 只有小时和分钟
                    endTime += ":59";
                }
@@ -368,4 +385,752 @@
            throw new Error("查询车辆轨迹失败:" + e.getMessage());
        }
    }
}
    /**
     * 腾讯地图地址搜索接口代理
     */
    @Anonymous()
    @GetMapping("/address/search")
    public AjaxResult searchAddress(String keyword, String region) {
        try {
            // 构建腾讯地图搜索API URL
            String url = "https://apis.map.qq.com/ws/place/v1/search";
            String params = "keyword=" + URLEncoder.encode(keyword, StandardCharsets.UTF_8.toString()) +
                           "&boundary=region(" + (region != null ? region : "广州") + ")" +
                           "&key=" + tencentMapConfig.getKey();
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("查询成功", response);
        } catch (Exception e) {
            logger.error("地址搜索失败", e);
            return AjaxResult.error("地址搜索失败:" + e.getMessage());
        }
    }
    /**
     * 腾讯地图逆地址解析接口代理
     */
    @Anonymous()
    @GetMapping("/address/geocoder")
    public AjaxResult reverseGeocoder(Double lat, Double lng) {
        try {
            // 检查参数
            logger.info("逆地址解析请求参数: lat={}, lng={}", lat, lng);
            if (lat == null || lng == null) {
                logger.warn("参数不完整,缺少经纬度坐标: lat={}, lng={}", lat, lng);
                return AjaxResult.error("参数不完整,缺少经纬度坐标");
            }
            // 检查参数有效性
            if (Double.isNaN(lat) || Double.isNaN(lng) ||
                Double.isInfinite(lat) || Double.isInfinite(lng)) {
                logger.warn("参数无效,经纬度坐标包含非法值: lat={}, lng={}", lat, lng);
                return AjaxResult.error("参数无效,经纬度坐标格式错误");
            }
            // 构建腾讯地图逆地址解析API URL
            String url = "https://apis.map.qq.com/ws/geocoder/v1/";
            String params = "location=" + lat + "," + lng +
                           "&key=" + tencentMapConfig.getKey() +
                           "&get_poi=1";
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("查询成功", response);
        } catch (Exception e) {
            logger.error("逆地址解析失败: lat={}, lng={}", lat, lng, e);
            return AjaxResult.error("逆地址解析失败:" + e.getMessage());
        }
    }
    /**
     * 腾讯地图路线规划接口代理(计算两点间距离)
     */
    @Anonymous()
    @GetMapping("/route/distance")
    public AjaxResult calculateDistance(Double fromLat, Double fromLng, Double toLat, Double toLng) {
        try {
            // 检查参数
            if (fromLat == null || fromLng == null || toLat == null || toLng == null) {
                return AjaxResult.error("参数不完整,缺少起点或终点坐标");
            }
            // 构建腾讯地图路线规划API URL
            String url = "https://apis.map.qq.com/ws/distance/v1/";
            String params = "mode=driving" +
                           "&from=" + fromLat + "," + fromLng +
                           "&to=" + toLat + "," + toLng +
                           "&key=" + tencentMapConfig.getKey();
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("计算成功", response);
        } catch (Exception e) {
            logger.error("距离计算失败", e);
            return AjaxResult.error("距离计算失败:" + e.getMessage());
        }
    }
    /**
     * 百度地图地理编码接口代理(地址转坐标)
     */
    @Anonymous()
    @GetMapping("/baidu/geocoding")
    public AjaxResult baiduGeocoding(String address, String city) {
        try {
            // 检查参数
            if (address == null || address.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少地址信息");
            }
            // 构建百度地图地理编码API URL
            String url = "https://api.map.baidu.com/geocoding/v3/";
            String params = "address=" + URLEncoder.encode(address, StandardCharsets.UTF_8.toString()) +
                           (city != null && !city.trim().isEmpty() ?
                            "&city=" + URLEncoder.encode(city, StandardCharsets.UTF_8.toString()) : "") +
                           "&output=json" +
                           "&ak=" + baiduMapConfig.getAk();
            logger.info("百度地图地理编码请求: address={}, city={}", address, city);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("查询成功", response);
        } catch (Exception e) {
            logger.error("百度地图地理编码失败", e);
            return AjaxResult.error("地理编码失败:" + e.getMessage());
        }
    }
    /**
     * 百度地图路线规划接口代理(计算两个坐标之间的驾车距离)
     */
    @Anonymous()
    @GetMapping("/baidu/route/driving")
    public AjaxResult baiduRouteDriving(String origin, String destination) {
        try {
            // 检查参数
            if (origin == null || origin.trim().isEmpty() ||
                destination == null || destination.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少起点或终点坐标");
            }
            // 验证坐标格式(纬度,经度)
            String[] originParts = origin.split(",");
            String[] destParts = destination.split(",");
            if (originParts.length != 2 || destParts.length != 2) {
                return AjaxResult.error("坐标格式错误,应为:纬度,经度");
            }
            // 构建百度地图路线规划API URL
            String url = "https://api.map.baidu.com/directionlite/v1/driving";
            String params = "origin=" + origin +
                           "&destination=" + destination +
                           "&ak=" + baiduMapConfig.getAk();
            logger.info("百度地图路线规划请求: origin={}, destination={}", origin, destination);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("计算成功", response);
        } catch (Exception e) {
            logger.error("百度地图路线规划失败", e);
            return AjaxResult.error("路线规划失败:" + e.getMessage());
        }
    }
    /**
     * 百度地图计算两个地址之间的距离(组合接口:地址转坐标 + 路线规划)
     */
    @Anonymous()
    @GetMapping("/baidu/distance/byAddress")
    public AjaxResult baiduDistanceByAddress(String fromAddress, String fromCity,
                                             String toAddress, String toCity) {
        try {
            // 检查参数
            if (fromAddress == null || fromAddress.trim().isEmpty() ||
                toAddress == null || toAddress.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少起点或终点地址");
            }
            logger.info("开始计算地址距离: fromAddress={}, fromCity={}, toAddress={}, toCity={}",
                       fromAddress, fromCity, toAddress, toCity);
            // 第一步:起点地址转坐标
            String geocodingUrl1 = "https://api.map.baidu.com/geocoding/v3/";
            String geocodingParams1 = "address=" + URLEncoder.encode(fromAddress, StandardCharsets.UTF_8.toString()) +
                                     (fromCity != null && !fromCity.trim().isEmpty() ?
                                      "&city=" + URLEncoder.encode(fromCity, StandardCharsets.UTF_8.toString()) : "") +
                                     "&output=json" +
                                     "&ak=" + baiduMapConfig.getAk();
            String geocodingResponse1 = HttpUtils.sendGet(geocodingUrl1, geocodingParams1);
            logger.info("起点地理编码响应: {}", geocodingResponse1);
            // 解析起点坐标
            com.alibaba.fastjson2.JSONObject geocodingJson1 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse1);
            if (geocodingJson1.getInteger("status") != 0) {
                logger.error("起点地理编码失败: {}", geocodingResponse1);
                return AjaxResult.error("起点地址解析失败");
            }
            com.alibaba.fastjson2.JSONObject location1 = geocodingJson1.getJSONObject("result").getJSONObject("location");
            double fromLat = location1.getDouble("lat");
            double fromLng = location1.getDouble("lng");
            logger.info("起点坐标: lat={}, lng={}", fromLat, fromLng);
            // 第二步:终点地址转坐标
            String geocodingUrl2 = "https://api.map.baidu.com/geocoding/v3/";
            String geocodingParams2 = "address=" + URLEncoder.encode(toAddress, StandardCharsets.UTF_8.toString()) +
                                     (toCity != null && !toCity.trim().isEmpty() ?
                                      "&city=" + URLEncoder.encode(toCity, StandardCharsets.UTF_8.toString()) : "") +
                                     "&output=json" +
                                     "&ak=" + baiduMapConfig.getAk();
            String geocodingResponse2 = HttpUtils.sendGet(geocodingUrl2, geocodingParams2);
            logger.info("终点地理编码响应: {}", geocodingResponse2);
            // 解析终点坐标
            com.alibaba.fastjson2.JSONObject geocodingJson2 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse2);
            if (geocodingJson2.getInteger("status") != 0) {
                logger.error("终点地理编码失败: {}", geocodingResponse2);
                return AjaxResult.error("终点地址解析失败");
            }
            com.alibaba.fastjson2.JSONObject location2 = geocodingJson2.getJSONObject("result").getJSONObject("location");
            double toLat = location2.getDouble("lat");
            double toLng = location2.getDouble("lng");
            logger.info("终点坐标: lat={}, lng={}", toLat, toLng);
            // 第三步:调用路线规划接口计算距离
            String routeUrl = "https://api.map.baidu.com/directionlite/v1/driving";
            String origin = fromLat + "," + fromLng;
            String destination = toLat + "," + toLng;
            String routeParams = "origin=" + origin +
                                "&destination=" + destination +
                                "&ak=" + baiduMapConfig.getAk();
            logger.info("路线规划请求: origin={}, destination={}", origin, destination);
            String routeResponse = HttpUtils.sendGet(routeUrl, routeParams);
            logger.info("路线规划响应: {}", routeResponse);
            // 解析距离结果
            com.alibaba.fastjson2.JSONObject routeJson = com.alibaba.fastjson2.JSONObject.parseObject(routeResponse);
            if (routeJson.getInteger("status") != 0) {
                logger.error("路线规划失败: {}", routeResponse);
                return AjaxResult.error("路线规划失败");
            }
            // 提取距离信息(单位:米)
            com.alibaba.fastjson2.JSONObject result = routeJson.getJSONObject("result");
            com.alibaba.fastjson2.JSONArray routes = result.getJSONArray("routes");
            if (routes == null || routes.isEmpty()) {
                logger.error("未找到路线信息");
                return AjaxResult.error("未找到路线信息");
            }
            com.alibaba.fastjson2.JSONObject route = routes.getJSONObject(0);
            int distance = route.getInteger("distance"); // 距离,单位:米
            int duration = route.getInteger("duration"); // 时长,单位:秒
            logger.info("计算成功: 距离={}米, 时长={}秒", distance, duration);
            // 构建返回结果
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("distance", distance); // 距离(米)
            resultMap.put("duration", duration); // 时长(秒)
            resultMap.put("distanceKm", String.format("%.1f", distance / 1000.0)); // 距离(公里)
            resultMap.put("durationMin", duration / 60); // 时长(分钟)
            // 起点坐标
            Map<String, Object> fromLocation = new HashMap<>();
            fromLocation.put("lat", fromLat);
            fromLocation.put("lng", fromLng);
            resultMap.put("fromLocation", fromLocation);
            // 终点坐标
            Map<String, Object> toLocation = new HashMap<>();
            toLocation.put("lat", toLat);
            toLocation.put("lng", toLng);
            resultMap.put("toLocation", toLocation);
            return AjaxResult.success("计算成功", resultMap);
        } catch (Exception e) {
            logger.error("计算地址距离失败", e);
            return AjaxResult.error("计算距离失败:" + e.getMessage());
        }
    }
    /**
     * 百度地图地址搜索提示API(输入联想)
     * Place Suggestion API - 用于地址输入时的智能提示
     */
    @Anonymous()
    @GetMapping("/baidu/place/suggestion")
    public AjaxResult baiduPlaceSuggestion(String query, String region) {
        try {
            // 检查参数
            if (query == null || query.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少搜索关键词");
            }
            // 构建百度地图 Place Suggestion API URL
            String url = "https://api.map.baidu.com/place/v2/suggestion";
            String params = "query=" + URLEncoder.encode(query, StandardCharsets.UTF_8.toString()) +
                           (region != null && !region.trim().isEmpty() ?
                            "&region=" + URLEncoder.encode(region, StandardCharsets.UTF_8.toString()) : "") +
                           "&output=json" +
                           "&ak=" + baiduMapConfig.getAk();
            logger.info("百度地图地址搜索提示请求: query={}, region={}", query, region);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            logger.debug("百度地图地址搜索提示响应: {}", response);
            // 解析响应
            com.alibaba.fastjson2.JSONObject jsonResponse = com.alibaba.fastjson2.JSONObject.parseObject(response);
            if (jsonResponse.getInteger("status") != 0) {
                logger.error("地址搜索提示失败: {}", response);
                return AjaxResult.error("地址搜索失败");
            }
            // 提取提示列表
            com.alibaba.fastjson2.JSONArray results = jsonResponse.getJSONArray("result");
            if (results == null || results.isEmpty()) {
                logger.info("未找到匹配的地址");
                return AjaxResult.success("查询成功", new ArrayList<>());
            }
            // 构建返回结果
            List<Map<String, Object>> suggestions = new ArrayList<>();
            for (int i = 0; i < results.size(); i++) {
                com.alibaba.fastjson2.JSONObject item = results.getJSONObject(i);
                Map<String, Object> suggestion = new HashMap<>();
                suggestion.put("name", item.getString("name")); // 名称
                suggestion.put("address", item.getString("address")); // 地址
                suggestion.put("province", item.getString("province")); // 省
                suggestion.put("city", item.getString("city")); // 市
                suggestion.put("district", item.getString("district")); // 区
                suggestion.put("uid", item.getString("uid")); // 地点UID
                // 经纬度信息
                com.alibaba.fastjson2.JSONObject location = item.getJSONObject("location");
                if (location != null) {
                    Map<String, Object> locationMap = new HashMap<>();
                    locationMap.put("lat", location.getDouble("lat"));
                    locationMap.put("lng", location.getDouble("lng"));
                    suggestion.put("location", locationMap);
                }
                suggestions.add(suggestion);
            }
            logger.info("地址搜索提示成功: 找到{}  条结果", suggestions.size());
            return AjaxResult.success("查询成功", suggestions);
        } catch (Exception e) {
            logger.error("地址搜索提示失败", e);
            return AjaxResult.error("地址搜索失败:" + e.getMessage());
        }
    }
    // ==================== 天地图接口 ====================
    /**
     * 天地图地理编码接口代理(地址转坐标)
     * 文档:https://lbs.tianditu.gov.cn/server/geocoding.html
     */
    @Anonymous()
    @GetMapping("/tianditu/geocoding")
    public AjaxResult tiandituGeocoding(String address) {
        try {
            // 检查参数
            if (address == null || address.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少地址信息");
            }
            // 构建天地图地理编码API URL
            String url = "http://api.tianditu.gov.cn/geocoder";
            String params = "ds={\"keyWord\":\"" + address + "\"}&tk=" + tiandituMapConfig.getTk();
            logger.info("天地图地理编码请求: address={}", address);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("查询成功", response);
        } catch (Exception e) {
            logger.error("天地图地理编码失败", e);
            return AjaxResult.error("地理编码失败:" + e.getMessage());
        }
    }
    /**
     * 天地图逆地理编码接口代理(坐标转地址)
     * 文档:https://lbs.tianditu.gov.cn/server/geocoding.html
     */
    @Anonymous()
    @GetMapping("/tianditu/reverseGeocoding")
    public AjaxResult tiandituReverseGeocoding(Double lon, Double lat) {
        try {
            // 检查参数
            if (lat == null || lon == null) {
                return AjaxResult.error("参数不完整,缺少经纬度坐标");
            }
            // 检查参数有效性
            if (Double.isNaN(lat) || Double.isNaN(lon) ||
                Double.isInfinite(lat) || Double.isInfinite(lon)) {
                return AjaxResult.error("参数无效,经纬度坐标格式错误");
            }
            // 构建天地图逆地理编码API URL
            String url = "http://api.tianditu.gov.cn/geocoder";
            String params = "postStr={\"lon\":" + lon + ",\"lat\":" + lat + ",\"ver\":1}" +
                           "&type=geocode" +
                           "&tk=" + tiandituMapConfig.getTk();
            logger.info("天地图逆地理编码请求: lon={}, lat={}", lon, lat);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("查询成功", response);
        } catch (Exception e) {
            logger.error("天地图逆地理编码失败: lon={}, lat={}", lon, lat, e);
            return AjaxResult.error("逆地理编码失败:" + e.getMessage());
        }
    }
    /**
     * 天地图地点搜索接口代理(POI搜索)
     * 文档:https://lbs.tianditu.gov.cn/server/search.html
     */
    @Anonymous()
    @GetMapping("/tianditu/place/search")
    public AjaxResult tiandituPlaceSearch(String keyWord, String queryType, String level,
                                          String mapBound, Integer start, Integer count) {
        try {
            // 检查参数
            if (keyWord == null || keyWord.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少搜索关键词");
            }
            // 设置默认值
            if (queryType == null || queryType.trim().isEmpty()) {
                queryType = "1"; // 1-普通搜索,7-周边搜索
            }
            if (start == null) {
                start = 0;
            }
            if (count == null) {
                count = 10;
            }
            // 构建天地图POI搜索API URL
            String url = "http://api.tianditu.gov.cn/search";
            StringBuilder paramsBuilder = new StringBuilder();
            paramsBuilder.append("postStr={\"keyWord\":\"").append(keyWord).append("\"");
            paramsBuilder.append(",\"queryType\":\"").append(queryType).append("\"");
            if (level != null && !level.trim().isEmpty()) {
                paramsBuilder.append(",\"level\":\"").append(level).append("\"");
            }
            if (mapBound != null && !mapBound.trim().isEmpty()) {
                paramsBuilder.append(",\"mapBound\":\"").append(mapBound).append("\"");
            }
            paramsBuilder.append(",\"start\":\"").append(start).append("\"");
            paramsBuilder.append(",\"count\":\"").append(count).append("\"");
            paramsBuilder.append("}" );
            paramsBuilder.append("&type=query");
            paramsBuilder.append("&tk=").append(tiandituMapConfig.getTk());
            String params = paramsBuilder.toString();
            logger.info("天地图POI搜索请求: keyWord={}, queryType={}", keyWord, queryType);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("查询成功", response);
        } catch (Exception e) {
            logger.error("天地图POI搜索失败", e);
            return AjaxResult.error("POI搜索失败:" + e.getMessage());
        }
    }
    /**
     * 天地图路线规划接口代理(驾车路径规划)
     * 文档:https://lbs.tianditu.gov.cn/server/drive.html
     */
    @Anonymous()
    @GetMapping("/tianditu/route/driving")
    public AjaxResult tiandituRouteDriving(String orig, String dest, String mid, String style) {
        try {
            // 检查参数
            if (orig == null || orig.trim().isEmpty() ||
                dest == null || dest.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少起点或终点坐标");
            }
            // 验证坐标格式(经度,纬度)
            String[] origParts = orig.split(",");
            String[] destParts = dest.split(",");
            if (origParts.length != 2 || destParts.length != 2) {
                return AjaxResult.error("坐标格式错误,应为:经度,纬度");
            }
            // 设置默认值
            if (style == null || style.trim().isEmpty()) {
                style = "0"; // 0-推荐,1-避开高速
            }
            // 构建天地图驾车路径规划API URL
            String url = "http://api.tianditu.gov.cn/drive";
            StringBuilder paramsBuilder = new StringBuilder();
            paramsBuilder.append("postStr={\"orig\":\"").append(orig).append("\"");
            paramsBuilder.append(",\"dest\":\"").append(dest).append("\"");
            if (mid != null && !mid.trim().isEmpty()) {
                paramsBuilder.append(",\"mid\":\"").append(mid).append("\"");
            }
            paramsBuilder.append(",\"style\":\"").append(style).append("\"");
            paramsBuilder.append("}" );
            paramsBuilder.append("&tk=").append(tiandituMapConfig.getTk());
            String params = paramsBuilder.toString();
            logger.info("天地图驾车路径规划请求: orig={}, dest={}", orig, dest);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            // 返回结果
            return AjaxResult.success("计算成功", response);
        } catch (Exception e) {
            logger.error("天地图驾车路径规划失败", e);
            return AjaxResult.error("路径规划失败:" + e.getMessage());
        }
    }
    /**
     * 天地图计算两个地址之间的距离(组合接口:地址转坐标 + 路径规划)
     */
    @Anonymous()
    @GetMapping("/tianditu/distance/byAddress")
    public AjaxResult tiandituDistanceByAddress(String fromAddress, String toAddress) {
        try {
            // 检查参数
            if (fromAddress == null || fromAddress.trim().isEmpty() ||
                toAddress == null || toAddress.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少起点或终点地址");
            }
            logger.info("开始计算地址距离: fromAddress={}, toAddress={}", fromAddress, toAddress);
            // 第一步:起点地址转坐标
            String geocodingUrl1 = "http://api.tianditu.gov.cn/geocoder";
            String geocodingParams1 = "ds={\"keyWord\":\"" + fromAddress + "\"}" +
                                     "&tk=" + tiandituMapConfig.getTk();
            String geocodingResponse1 = HttpUtils.sendGet(geocodingUrl1, geocodingParams1);
            logger.info("起点地理编码响应: {}", geocodingResponse1);
            // 解析起点坐标
            com.alibaba.fastjson2.JSONObject geocodingJson1 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse1);
            if (!"0".equals(geocodingJson1.getString("status"))) {
                logger.error("起点地理编码失败: {}", geocodingResponse1);
                return AjaxResult.error("起点地址解析失败");
            }
            com.alibaba.fastjson2.JSONObject location1 = geocodingJson1.getJSONObject("location");
            if (location1 == null) {
                return AjaxResult.error("起点地址未找到对应坐标");
            }
            double fromLon = location1.getDouble("lon");
            double fromLat = location1.getDouble("lat");
            logger.info("起点坐标: lon={}, lat={}", fromLon, fromLat);
            // 第二步:终点地址转坐标
            String geocodingUrl2 = "http://api.tianditu.gov.cn/geocoder";
            String geocodingParams2 = "ds={\"keyWord\":\"" + toAddress + "\"}" +
                                     "&tk=" + tiandituMapConfig.getTk();
            String geocodingResponse2 = HttpUtils.sendGet(geocodingUrl2, geocodingParams2);
            logger.info("终点地理编码响应: {}", geocodingResponse2);
            // 解析终点坐标
            com.alibaba.fastjson2.JSONObject geocodingJson2 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse2);
            if (!"0".equals(geocodingJson2.getString("status"))) {
                logger.error("终点地理编码失败: {}", geocodingResponse2);
                return AjaxResult.error("终点地址解析失败");
            }
            com.alibaba.fastjson2.JSONObject location2 = geocodingJson2.getJSONObject("location");
            if (location2 == null) {
                return AjaxResult.error("终点地址未找到对应坐标");
            }
            double toLon = location2.getDouble("lon");
            double toLat = location2.getDouble("lat");
            logger.info("终点坐标: lon={}, lat={}", toLon, toLat);
            // 第三步:调用路径规划接口计算距离
            String routeUrl = "http://api.tianditu.gov.cn/drive";
            String orig = fromLon + "," + fromLat;
            String dest = toLon + "," + toLat;
            String routeParams = "postStr={\"orig\":\"" + orig + "\",\"dest\":\"" + dest + "\",\"style\":\"0\"}" +
                                "&tk=" + tiandituMapConfig.getTk();
            logger.info("路径规划请求: orig={}, dest={}", orig, dest);
            String routeResponse = HttpUtils.sendGet(routeUrl, routeParams);
            logger.info("路径规划响应: {}", routeResponse);
            // 解析距离结果
            com.alibaba.fastjson2.JSONObject routeJson = com.alibaba.fastjson2.JSONObject.parseObject(routeResponse);
            if (!"0".equals(routeJson.getString("status"))) {
                logger.error("路径规划失败: {}", routeResponse);
                return AjaxResult.error("路径规划失败");
            }
            // 提取距离信息
            com.alibaba.fastjson2.JSONObject result = routeJson.getJSONObject("result");
            if (result == null) {
                logger.error("路径规划结果为空");
                return AjaxResult.error("路径规划失败");
            }
            com.alibaba.fastjson2.JSONArray routes = result.getJSONArray("routes");
            if (routes == null || routes.isEmpty()) {
                logger.error("未找到路线信息");
                return AjaxResult.error("未找到路线信息");
            }
            com.alibaba.fastjson2.JSONObject route = routes.getJSONObject(0);
            double distance = route.getDoubleValue("distance"); // 距离,单位:米
            double duration = route.getDoubleValue("duration"); // 时长,单位:秒
            logger.info("计算成功: 距离={}米, 时长={}秒", distance, duration);
            // 构建返回结果
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("distance", (int)distance); // 距离(米)
            resultMap.put("duration", (int)duration); // 时长(秒)
            resultMap.put("distanceKm", String.format("%.1f", distance / 1000.0)); // 距离(公里)
            resultMap.put("durationMin", (int)(duration / 60)); // 时长(分钟)
            // 起点坐标
            Map<String, Object> fromLocation = new HashMap<>();
            fromLocation.put("lon", fromLon);
            fromLocation.put("lat", fromLat);
            resultMap.put("fromLocation", fromLocation);
            // 终点坐标
            Map<String, Object> toLocation = new HashMap<>();
            toLocation.put("lon", toLon);
            toLocation.put("lat", toLat);
            resultMap.put("toLocation", toLocation);
            return AjaxResult.success("计算成功", resultMap);
        } catch (Exception e) {
            logger.error("计算地址距离失败", e);
            return AjaxResult.error("计算距离失败:" + e.getMessage());
        }
    }
    /**
     * 天地图输入提示接口代理(地址联想)
     * 文档:https://lbs.tianditu.gov.cn/server/suggestion.html
     */
    @Anonymous()
    @GetMapping("/tianditu/place/suggestion")
    public AjaxResult tiandituPlaceSuggestion(String keyWord, String region, String city, Integer count) {
        try {
            // 检查参数
            if (keyWord == null || keyWord.trim().isEmpty()) {
                return AjaxResult.error("参数不完整,缺少搜索关键词");
            }
            // 设置默认值
            if (count == null) {
                count = 10;
            }
            // 构建天地图输入提示API URL
            String url = "http://api.tianditu.gov.cn/search";
            StringBuilder paramsBuilder = new StringBuilder();
            paramsBuilder.append("postStr={\"keyWord\":\"").append(keyWord).append("\"");
            if (region != null && !region.trim().isEmpty()) {
                paramsBuilder.append(",\"region\":\"").append(region).append("\"");
            }
            if (city != null && !city.trim().isEmpty()) {
                paramsBuilder.append(",\"city\":\"").append(city).append("\"");
            }
            paramsBuilder.append(",\"count\":\"").append(count).append("\"");
            paramsBuilder.append("}" );
            paramsBuilder.append("&type=suggest");
            paramsBuilder.append("&tk=").append(tiandituMapConfig.getTk());
            String params = paramsBuilder.toString();
            logger.info("天地图输入提示请求: keyWord={}, region={}", keyWord, region);
            // 发送HTTP请求
            String response = HttpUtils.sendGet(url, params);
            logger.debug("天地图输入提示响应: {}", response);
            // 解析响应
            com.alibaba.fastjson2.JSONObject jsonResponse = com.alibaba.fastjson2.JSONObject.parseObject(response);
            if (!"0".equals(jsonResponse.getString("status"))) {
                logger.error("输入提示失败: {}", response);
                return AjaxResult.error("地址搜索失败");
            }
            // 提取提示列表
            com.alibaba.fastjson2.JSONArray results = jsonResponse.getJSONArray("suggests");
            if (results == null || results.isEmpty()) {
                logger.info("未找到匹配的地址");
                return AjaxResult.success("查询成功", new ArrayList<>());
            }
            // 构建返回结果
            List<Map<String, Object>> suggestions = new ArrayList<>();
            for (int i = 0; i < results.size(); i++) {
                com.alibaba.fastjson2.JSONObject item = results.getJSONObject(i);
                Map<String, Object> suggestion = new HashMap<>();
                suggestion.put("name", item.getString("name")); // 名称
                suggestion.put("address", item.getString("address")); // 地址
                suggestion.put("province", item.getString("province")); // 省
                suggestion.put("city", item.getString("city")); // 市
                suggestion.put("district", item.getString("district")); // 区
                // 经纬度信息
                com.alibaba.fastjson2.JSONObject location = item.getJSONObject("location");
                if (location != null) {
                    Map<String, Object> locationMap = new HashMap<>();
                    locationMap.put("lon", location.getDouble("lon"));
                    locationMap.put("lat", location.getDouble("lat"));
                    suggestion.put("location", locationMap);
                }
                suggestions.add(suggestion);
            }
            logger.info("地址搜索提示成功: 找到{}条结果", suggestions.size());
            return AjaxResult.success("查询成功", suggestions);
        } catch (Exception e) {
            logger.error("地址搜索提示失败", e);
            return AjaxResult.error("地址搜索失败:" + e.getMessage());
        }
    }
}