feat(security): 新增高德逆地理编码接口并配置RestTemplate超时时间
This commit is contained in:
@@ -170,6 +170,8 @@ security:
|
||||
- /oa/attendanceRecord/**
|
||||
- /oa/oaWarehouse/**
|
||||
- /oa/oaWarehouseMaster/**
|
||||
# 高德逆地理(经纬度转城市等,供前端/H5 调用)
|
||||
- /oa/amap/**
|
||||
|
||||
# MyBatisPlus配置
|
||||
# https://baomidou.com/config/
|
||||
|
||||
@@ -2,13 +2,20 @@ package com.ruoyi.framework.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
private static final int CONNECT_TIMEOUT_MS = 5_000;
|
||||
private static final int READ_TIMEOUT_MS = 15_000;
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(CONNECT_TIMEOUT_MS);
|
||||
factory.setReadTimeout(READ_TIMEOUT_MS);
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.ruoyi.oa.controller;
|
||||
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.oa.domain.vo.AmapCityNameVo;
|
||||
import com.ruoyi.oa.service.IOaAmapGeocodeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
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 javax.validation.constraints.DecimalMax;
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 高德地图:经纬度逆地理编码(城市名等)
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/oa/amap")
|
||||
public class OaAmapController extends BaseController {
|
||||
|
||||
private final IOaAmapGeocodeService oaAmapGeocodeService;
|
||||
|
||||
/**
|
||||
* 根据经纬度获取城市名称(高德逆地理编码)
|
||||
*/
|
||||
@GetMapping("/city")
|
||||
public R<AmapCityNameVo> cityByLocation(
|
||||
@NotNull(message = "经度不能为空")
|
||||
@DecimalMin(value = "-180.0", message = "经度范围无效")
|
||||
@DecimalMax(value = "180.0", message = "经度范围无效")
|
||||
@RequestParam Double longitude,
|
||||
@NotNull(message = "纬度不能为空")
|
||||
@DecimalMin(value = "-90.0", message = "纬度范围无效")
|
||||
@DecimalMax(value = "90.0", message = "纬度范围无效")
|
||||
@RequestParam Double latitude
|
||||
) {
|
||||
AmapCityNameVo vo = oaAmapGeocodeService.reverseGeocodeCity(longitude, latitude);
|
||||
return R.ok(vo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ruoyi.oa.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 高德逆地理编码:城市名称(及可选行政区信息,便于展示/调试)
|
||||
*/
|
||||
@Data
|
||||
public class AmapCityNameVo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 城市名(直辖市、省直辖等场景可能为省名或区名,与高德 addressComponent 一致) */
|
||||
private String cityName;
|
||||
|
||||
/** 省 */
|
||||
private String province;
|
||||
|
||||
/** 区/县 */
|
||||
private String district;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ruoyi.oa.service;
|
||||
|
||||
import com.ruoyi.oa.domain.vo.AmapCityNameVo;
|
||||
|
||||
/**
|
||||
* 高德地图逆地理编码(经纬度 → 城市等)
|
||||
*/
|
||||
public interface IOaAmapGeocodeService {
|
||||
|
||||
/**
|
||||
* 根据经纬度解析城市名称等信息
|
||||
*
|
||||
* @param longitude 经度
|
||||
* @param latitude 纬度
|
||||
* @return 非 null;解析失败时 cityName 等可能为空
|
||||
*/
|
||||
AmapCityNameVo reverseGeocodeCity(Double longitude, Double latitude);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.ruoyi.oa.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.oa.domain.vo.AmapCityNameVo;
|
||||
import com.ruoyi.oa.service.IOaAmapGeocodeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* 高德逆地理编码:restapi.amap.com/v3/geocode/regeo
|
||||
* <p>
|
||||
* 配置项:{@code fad.amap.key}(与 {@code application.yml} 中一致)
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OaAmapGeocodeServiceImpl implements IOaAmapGeocodeService {
|
||||
|
||||
private static final String AMAP_REGEO_URL = "https://restapi.amap.com/v3/geocode/regeo";
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@Value("${fad.amap.key:}")
|
||||
private String amapKey;
|
||||
|
||||
@Override
|
||||
public AmapCityNameVo reverseGeocodeCity(Double longitude, Double latitude) {
|
||||
AmapCityNameVo vo = new AmapCityNameVo();
|
||||
if (longitude == null || latitude == null) {
|
||||
return vo;
|
||||
}
|
||||
if (!StringUtils.hasText(amapKey)) {
|
||||
log.warn("fad.amap.key 未配置,无法调用高德逆地理编码");
|
||||
return vo;
|
||||
}
|
||||
|
||||
try {
|
||||
// 高德要求:location = 经度,纬度
|
||||
String location = longitude + "," + latitude;
|
||||
String url = UriComponentsBuilder.fromHttpUrl(AMAP_REGEO_URL)
|
||||
.queryParam("key", amapKey)
|
||||
.queryParam("location", location)
|
||||
.queryParam("extensions", "base")
|
||||
.queryParam("batch", "false")
|
||||
.queryParam("output", "JSON")
|
||||
.build(true)
|
||||
.toUriString();
|
||||
|
||||
String body = restTemplate.getForObject(url, String.class);
|
||||
if (!StringUtils.hasText(body)) {
|
||||
log.warn("高德逆地理编码响应为空, location={}", location);
|
||||
return vo;
|
||||
}
|
||||
JSONObject response = JSONObject.parseObject(body);
|
||||
if (response == null) {
|
||||
log.warn("高德逆地理编码 JSON 解析失败, location={}", location);
|
||||
return vo;
|
||||
}
|
||||
if (!"1".equals(response.getString("status"))) {
|
||||
String info = response.getString("info");
|
||||
log.warn("高德逆地理编码失败: status={}, info={}, infocode={}",
|
||||
response.getString("status"), info, response.getString("infocode"));
|
||||
return vo;
|
||||
}
|
||||
|
||||
JSONObject regeocode = response.getJSONObject("regeocode");
|
||||
if (regeocode == null) {
|
||||
return vo;
|
||||
}
|
||||
JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
|
||||
if (addressComponent == null) {
|
||||
return vo;
|
||||
}
|
||||
|
||||
String province = nullToEmpty(addressComponent.getString("province"));
|
||||
String district = nullToEmpty(addressComponent.getString("district"));
|
||||
vo.setProvince(province);
|
||||
vo.setDistrict(district);
|
||||
|
||||
String cityName = resolveCityName(addressComponent, province);
|
||||
vo.setCityName(cityName);
|
||||
return vo;
|
||||
} catch (Exception e) {
|
||||
log.warn("高德逆地理编码异常, longitude={}, latitude={}, err={}",
|
||||
longitude, latitude, e.getMessage(), e);
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 城市字段:普通城市为字符串;部分省直辖为 [];直辖市可能用省名表示
|
||||
*/
|
||||
private static String resolveCityName(JSONObject addressComponent, String province) {
|
||||
Object cityRaw = addressComponent.get("city");
|
||||
if (cityRaw instanceof JSONArray) {
|
||||
JSONArray arr = (JSONArray) cityRaw;
|
||||
if (!arr.isEmpty()) {
|
||||
return nullToEmpty(arr.getString(0));
|
||||
}
|
||||
return province;
|
||||
}
|
||||
String city = addressComponent.getString("city");
|
||||
if (StringUtils.hasText(city) && !"[]".equals(city)) {
|
||||
return city.trim();
|
||||
}
|
||||
return province;
|
||||
}
|
||||
|
||||
private static String nullToEmpty(String s) {
|
||||
return s == null ? "" : s.trim();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user