20260329:1、规范地图,系统已经融入高德、百度正逆地址编码;2、Modbus设备经纬度用LBS指令读取
This commit is contained in:
parent
775fa83e25
commit
bcc78fe240
@ -2,16 +2,18 @@ package com.fastbee.web.controller.backend;
|
||||
|
||||
import com.fastbee.common.core.domain.AjaxResult;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.http.map.ILocationService;
|
||||
import com.fastbee.http.map.LocationServiceFactory;
|
||||
import com.fastbee.http.model.AddressInfo;
|
||||
import com.fastbee.http.model.Coordinate;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -28,6 +30,9 @@ public class BackendController {
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Resource
|
||||
private LocationServiceFactory locationServiceFactory;
|
||||
|
||||
@ApiOperation("设置缓存值(调试/运维)")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:cache:remove')")
|
||||
@PostMapping("/set")
|
||||
@ -64,4 +69,40 @@ public class BackendController {
|
||||
redisTemplate.delete(key);
|
||||
return AjaxResult.success("ok");
|
||||
}
|
||||
|
||||
// getAddressByCoordinate
|
||||
@ApiOperation("根据坐标获取地址(逆地理编码)")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:cache:remove')")
|
||||
@GetMapping("/getAddressByCoordinate")
|
||||
public AjaxResult getAddressByCoordinate(@RequestParam("coordinate") String coordinate, @RequestParam("provider") String provider) {
|
||||
if (StringUtils.isEmpty(coordinate)) {
|
||||
return AjaxResult.error("coordinate不能为空");
|
||||
}
|
||||
|
||||
String[] split = coordinate.split(",");
|
||||
|
||||
// 工厂模式获取定位服务
|
||||
ILocationService service = locationServiceFactory.getLocationService(provider);
|
||||
Coordinate c = new Coordinate(BigDecimal.valueOf(Double.parseDouble(split[0])), BigDecimal.valueOf(Double.parseDouble(split[1])));
|
||||
AddressInfo addressByCoordinate = service.getAddressByCoordinate(c);
|
||||
|
||||
// TODO:用地图服务根据坐标获取地址
|
||||
return AjaxResult.success(addressByCoordinate);
|
||||
}
|
||||
|
||||
// getCoordinateByAddress
|
||||
@ApiOperation("根据地址获取坐标(正地理编码)")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:cache:remove')")
|
||||
@GetMapping("/getCoordinateByAddress")
|
||||
public AjaxResult getCoordinateByAddress(@RequestParam("address") String address, @RequestParam("provider") String provider) {
|
||||
if (StringUtils.isEmpty(address)) {
|
||||
return AjaxResult.error("address不能为空");
|
||||
}
|
||||
// TODO:用地图服务根据地址获取坐标
|
||||
|
||||
ILocationService service = locationServiceFactory.getLocationService(provider);
|
||||
Coordinate coordinateByAddress = service.getCoordinateByAddress(address);
|
||||
|
||||
return AjaxResult.success(coordinateByAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +154,3 @@ logging:
|
||||
swagger:
|
||||
enabled: true # 是否开启swagger
|
||||
pathMapping: /dev-api # 请求前缀
|
||||
|
||||
# 地图配置
|
||||
map:
|
||||
provider: baidu
|
||||
@ -153,7 +153,3 @@ logging:
|
||||
swagger:
|
||||
enabled: true # 是否开启swagger
|
||||
pathMapping: /prod-api # 请求前缀
|
||||
|
||||
# 地图配置
|
||||
map:
|
||||
provider: baidu
|
||||
@ -102,8 +102,4 @@ logging:
|
||||
# Swagger配置
|
||||
swagger:
|
||||
enabled: true # 是否开启swagger
|
||||
pathMapping: /test-api # 请求前缀
|
||||
|
||||
# 地图配置
|
||||
map:
|
||||
provider: baidu
|
||||
pathMapping: /test-api # 请求前缀
|
||||
@ -217,5 +217,11 @@ liteflow:
|
||||
scriptTypeField: script_type
|
||||
scriptLanguageField: script_language
|
||||
scriptEnableField: enable
|
||||
other:
|
||||
|
||||
|
||||
# 地图配置
|
||||
map:
|
||||
provider: gaode # 提供者
|
||||
baidu_ak: 0SD4KrYeMXVu8Ox2pxQGDOw3NmDYiKIw
|
||||
gaode_ak: 3af85be39e2f28334097781349336157
|
||||
gaode_secret: d0561ad20142e74f8c59edc5deafdbe4
|
||||
@ -371,14 +371,17 @@ public interface FastBeeConstant {
|
||||
String GB28181 = "GB28181";
|
||||
}
|
||||
|
||||
// 地图相关
|
||||
/**
|
||||
* 第三方地图 Web 服务:仅存放根地址与各接口路径,完整 URL = BASE + PATH。
|
||||
*/
|
||||
interface MAP {
|
||||
// 百度 BAIDU
|
||||
String BAIDU_GEOCODING = "http://api.map.baidu.com/geocoding/v3/";
|
||||
// 百度(https://lbs.baidu.com/faq/api?title=webapi/guide/webservice-geocoding)
|
||||
String BAIDU_BASE = "https://api.map.baidu.com";
|
||||
|
||||
// 高德 GAODE
|
||||
// 高德(https://lbs.amap.com/api/webservice/guide/api/georegeo)
|
||||
String GAODE_BASE = "https://restapi.amap.com";
|
||||
|
||||
// 腾讯 TENCENT
|
||||
// 腾讯 TENCENT(预留)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -136,10 +136,10 @@ public class ATCommandParserUtils {
|
||||
if (lbsValue != null && lbsValue.contains(",")) {
|
||||
String[] parts = lbsValue.split(",");
|
||||
try {
|
||||
// 注意:实际顺序可能是 [纬度,经度] 或 [经度,纬度],根据文档通常为经度,纬度
|
||||
// 返回 [经度,纬度]
|
||||
double latitude = Double.parseDouble(parts[0]);
|
||||
double longitude = Double.parseDouble(parts[1]);
|
||||
return new double[]{latitude, longitude};
|
||||
return new double[]{longitude, latitude};
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
@ -292,7 +292,7 @@ public class ATCommandParserUtils {
|
||||
System.out.println("LBS解析结果: " + parseLBSString(lbsHex));
|
||||
double[] lbs = parseLBS(lbsHex);
|
||||
if (lbs != null) {
|
||||
System.out.println("纬度: " + lbs[0] + ", 经度: " + lbs[1]);
|
||||
System.out.println("经度: " + lbs[0] + ", 纬度: " + lbs[1]);
|
||||
}
|
||||
|
||||
// 测试通用解析
|
||||
|
||||
@ -33,6 +33,7 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -312,7 +313,7 @@ public class DeviceDebugLogAspect {
|
||||
return;
|
||||
}
|
||||
|
||||
// 跟新IMEI、ICCID
|
||||
// 更新IMEI、ICCID、longitude、latitude
|
||||
if (StringUtils.isEmpty(device.getImei()) || StringUtils.isEmpty(device.getIccid())) {
|
||||
Map<String, String> atCommandMap = ATCommandParserUtils.parseATResponseFromHex(context.sourceData);
|
||||
Device updateDevice = new Device();
|
||||
@ -322,6 +323,16 @@ public class DeviceDebugLogAspect {
|
||||
if (StringUtils.isEmpty(device.getIccid())) {
|
||||
updateDevice.setIccid(atCommandMap.get("ICCID"));
|
||||
}
|
||||
if (device.getLongitude() == null && device.getLatitude() == null) {
|
||||
String lbsValue = atCommandMap.get("LBS");
|
||||
if (lbsValue != null) {
|
||||
String[] lbsArray = lbsValue.split(",");
|
||||
if (lbsArray.length == 2) {
|
||||
updateDevice.setLongitude(new BigDecimal(lbsArray[1]));
|
||||
updateDevice.setLatitude(new BigDecimal(lbsArray[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
updateDevice.setDeviceId(device.getDeviceId());
|
||||
updateDevice.setUpdateBy("DTU");
|
||||
updateDevice.setUpdateTime(DateUtils.getNowDate());
|
||||
|
||||
@ -118,6 +118,11 @@ public class PowerOnTriggerTaskExecutor {
|
||||
deviceMessage.setMessage("41542b49434349445c725c6e");
|
||||
deviceMessageService.messagePost(deviceMessage);
|
||||
}
|
||||
|
||||
if (device.getLongitude() == null || device.getLatitude() == null) {
|
||||
deviceMessage.setMessage("41542b4c42535c725c6e"); // AT+LBS\r\n
|
||||
deviceMessageService.messagePost(deviceMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -29,7 +29,7 @@ public class ThingsModelPushJob {
|
||||
/**
|
||||
* 每分钟执行一次,检查并触发到期的推送任务
|
||||
*/
|
||||
@Scheduled(fixedDelay = 60_000)
|
||||
@Scheduled(fixedDelay = 300_000)
|
||||
public void execute() {
|
||||
List<ThingsModelPushConfig> configs = pushConfigService.selectAllEnabled();
|
||||
if (CollectionUtils.isEmpty(configs)) {
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
package com.fastbee.http.ip;/**
|
||||
* @description: IP地址相关客户端调用
|
||||
* @author Sumuxi
|
||||
* @date 2026/3/8
|
||||
*/
|
||||
package com.fastbee.http.ip;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.constant.Constants;
|
||||
|
||||
@ -5,6 +5,7 @@ import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.constant.ErrorCodeConstants;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.utils.http.HttpUtils;
|
||||
import com.fastbee.http.model.AddressInfo;
|
||||
import com.fastbee.http.model.Coordinate;
|
||||
import com.fastbee.http.utils.CoordinateTransformUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -16,6 +17,7 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.fastbee.common.exception.ServiceExceptionUtil.exception;
|
||||
@ -30,9 +32,15 @@ import static com.fastbee.common.exception.ServiceExceptionUtil.exception;
|
||||
@Service("baiduLocationService")
|
||||
public class BaiduILocationService implements ILocationService {
|
||||
|
||||
@Value("${other.baidu_ak}")
|
||||
@Value("${map.baidu_ak}")
|
||||
private String baiduAK;
|
||||
|
||||
// 地理编码
|
||||
private static final String BAIDU_PATH_GEOCODING = "/geocoding/v3/";
|
||||
|
||||
// 逆地理编码
|
||||
private static final String BAIDU_PATH_REVERSE_GEOCODING = "/reverse_geocoding/v3/";
|
||||
|
||||
/**
|
||||
* 根据结构化地址获取地理编码(即经纬度)
|
||||
* 地理编码服务提供将结构化地址数据(如:北京市海淀区上地十街十号)转换为对应坐标点(经纬度)功能。
|
||||
@ -43,7 +51,7 @@ public class BaiduILocationService implements ILocationService {
|
||||
public Coordinate getCoordinateByAddress(String address) {
|
||||
|
||||
log.info("结构化地址:{}", address);
|
||||
String baseUrl = FastBeeConstant.MAP.BAIDU_GEOCODING;
|
||||
String baseUrl = FastBeeConstant.MAP.BAIDU_BASE + BAIDU_PATH_GEOCODING;
|
||||
address = StringUtils.isEmpty(address) || Objects.equals("内网IP", address) ? "湖南省长沙市岳麓区" : address;
|
||||
String encodedAddress;
|
||||
try {
|
||||
@ -61,6 +69,55 @@ public class BaiduILocationService implements ILocationService {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 逆地理编码:坐标系为 BD09,与百度地理编码返回及本类 {@link #convertPlatformCoordinateFromWgs84} 一致。
|
||||
* 文档:https://lbs.baidu.com/faq/api?title=webapi/guide/webservice-geocoding
|
||||
*/
|
||||
@Override
|
||||
public AddressInfo getAddressByCoordinate(Coordinate coordinate) {
|
||||
if (coordinate == null || coordinate.getLongitude() == null || coordinate.getLatitude() == null) {
|
||||
log.warn("百度逆地理编码:坐标为空");
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(baiduAK)) {
|
||||
log.warn("百度逆地理编码:未配置 map.baidu_ak");
|
||||
return null;
|
||||
}
|
||||
double lat = coordinate.getLatitude().doubleValue();
|
||||
double lng = coordinate.getLongitude().doubleValue();
|
||||
// location:纬度,经度;coordtype 与坐标系一致
|
||||
String location = String.format(Locale.US, "%.6f,%.6f", lat, lng);
|
||||
String param = "ak=" + baiduAK + "&output=json&coordtype=bd09ll&location=" + location;
|
||||
String response = HttpUtils.sendGet(FastBeeConstant.MAP.BAIDU_BASE + BAIDU_PATH_REVERSE_GEOCODING, param, Constants.UTF8);
|
||||
if (StringUtils.isEmpty(response)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject root = JSONObject.parseObject(response);
|
||||
if (root == null || root.getIntValue("status") != 0) {
|
||||
log.warn("百度逆地理编码失败 status={} message={}", root != null ? root.get("status") : null,
|
||||
root != null ? root.getString("message") : null);
|
||||
return null;
|
||||
}
|
||||
JSONObject result = root.getJSONObject("result");
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
AddressInfo info = new AddressInfo();
|
||||
info.setFormattedAddress(result.getString("formatted_address"));
|
||||
JSONObject ac = result.getJSONObject("addressComponent");
|
||||
if (ac != null) {
|
||||
info.setCountry(ac.getString("country"));
|
||||
info.setProvince(ac.getString("province"));
|
||||
info.setCity(ac.getString("city"));
|
||||
info.setDistrict(ac.getString("district"));
|
||||
info.setTown(ac.getString("town"));
|
||||
info.setStreet(ac.getString("street"));
|
||||
info.setStreetNumber(ac.getString("street_number"));
|
||||
}
|
||||
info.setBuilding(result.getString("business"));
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate convertToWgs84(Coordinate baiduCoordinate) {
|
||||
|
||||
@ -71,4 +128,13 @@ public class BaiduILocationService implements ILocationService {
|
||||
return new Coordinate(BigDecimal.valueOf(doubles[0]), BigDecimal.valueOf(doubles[1]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate convertPlatformCoordinateFromWgs84(Coordinate wgs84Coordinate) {
|
||||
|
||||
//世界坐标系(WGS84)的坐标转换为百度坐标系(BD09)
|
||||
double[] doubles = CoordinateTransformUtils.wgs84ToBd09(wgs84Coordinate.getLongitude().doubleValue(),
|
||||
wgs84Coordinate.getLatitude().doubleValue());
|
||||
return new Coordinate(BigDecimal.valueOf(doubles[0]), BigDecimal.valueOf(doubles[1]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,10 +1,27 @@
|
||||
package com.fastbee.http.map;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.constant.FastBeeConstant;
|
||||
import com.fastbee.common.utils.http.HttpUtils;
|
||||
import com.fastbee.common.utils.sign.Md5Utils;
|
||||
import com.fastbee.http.model.AddressInfo;
|
||||
import com.fastbee.http.model.Coordinate;
|
||||
import com.fastbee.http.utils.CoordinateTransformUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @description: 高德地图API实现类
|
||||
@ -12,15 +29,205 @@ import java.math.BigDecimal;
|
||||
* @author Sumuxi
|
||||
* @date 2026/3/8
|
||||
*/
|
||||
@Slf4j
|
||||
@Service("gaodeLocationService")
|
||||
public class GaodeLocationService implements ILocationService {
|
||||
|
||||
@Value("${map.gaode_ak:3af85be39e2f28334097781349336157}")
|
||||
private String gaodeAK;
|
||||
|
||||
/**
|
||||
* 数字签名私钥(控制台开启「数字签名」后必填)。
|
||||
* 算法参见:https://lbs.amap.com/faq/quota-key/key/41181
|
||||
*/
|
||||
@Value("${map.gaode_secret:d0561ad20142e74f8c59edc5deafdbe4}")
|
||||
private String gaodeSecret;
|
||||
|
||||
/** 地理编码:地址 → 经纬度(GCJ-02) */
|
||||
private static final String GAODE_PATH_GEO = "/v3/geocode/geo";
|
||||
|
||||
/** 逆地理编码:经纬度 → 地址 */
|
||||
private static final String GAODE_PATH_REGEO = "/v3/geocode/regeo";
|
||||
|
||||
/**
|
||||
* 地理编码:结构化地址转经纬度,坐标系为 GCJ-02(火星坐标),与高德 Web 服务一致。
|
||||
* 文档:https://lbs.amap.com/api/webservice/guide/api/georegeo
|
||||
* 数字签名:MD5 使用 UTF-8 下原始参数值拼接(与控制台一致),请求中的 address 需单独 URL 编码,参见
|
||||
* https://lbs.amap.com/faq/quota-key/key/41181
|
||||
*/
|
||||
@Override
|
||||
public Coordinate getCoordinateByAddress(String address) {
|
||||
return null;
|
||||
if (StringUtils.isBlank(address)) {
|
||||
log.warn("高德地理编码:地址为空");
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(gaodeAK)) {
|
||||
log.warn("高德地理编码:未配置 map.gaode_ak");
|
||||
return null;
|
||||
}
|
||||
String raw = address.trim();
|
||||
TreeMap<String, String> forSign = new TreeMap<>();
|
||||
forSign.put("address", raw);
|
||||
forSign.put("output", "json");
|
||||
forSign.put("key", gaodeAK);
|
||||
String sig = null;
|
||||
if (StringUtils.isNotBlank(gaodeSecret)) {
|
||||
sig = Md5Utils.hash(joinKeyValueSorted(forSign) + gaodeSecret);
|
||||
}
|
||||
String encodedAddress;
|
||||
try {
|
||||
encodedAddress = URLEncoder.encode(raw, StandardCharsets.UTF_8.toString());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.error("高德地理编码:地址 UTF-8 编码失败", e);
|
||||
return null;
|
||||
}
|
||||
StringBuilder qb = new StringBuilder();
|
||||
qb.append("address=").append(encodedAddress);
|
||||
qb.append("&key=").append(gaodeAK);
|
||||
qb.append("&output=json");
|
||||
if (sig != null) {
|
||||
qb.append("&sig=").append(sig);
|
||||
}
|
||||
String response = HttpUtils.sendGet(FastBeeConstant.MAP.GAODE_BASE + GAODE_PATH_GEO, qb.toString(), Constants.UTF8);
|
||||
if (StringUtils.isBlank(response)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject root = JSONObject.parseObject(response);
|
||||
if (root == null || !"1".equals(String.valueOf(root.get("status")))) {
|
||||
log.warn("高德地理编码失败 status={} info={}", root != null ? root.get("status") : null,
|
||||
root != null ? root.getString("info") : null);
|
||||
return null;
|
||||
}
|
||||
JSONArray geocodes = root.getJSONArray("geocodes");
|
||||
if (geocodes == null || geocodes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
JSONObject first = geocodes.getJSONObject(0);
|
||||
if (first == null) {
|
||||
return null;
|
||||
}
|
||||
String location = first.getString("location");
|
||||
if (StringUtils.isBlank(location)) {
|
||||
return null;
|
||||
}
|
||||
String[] parts = location.split(",");
|
||||
if (parts.length != 2) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
BigDecimal lng = new BigDecimal(parts[0].trim());
|
||||
BigDecimal lat = new BigDecimal(parts[1].trim());
|
||||
return new Coordinate(lng, lat);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("高德地理编码:无法解析坐标 location={}", location);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 逆地理编码:坐标系为 GCJ-02(火星坐标),与高德 Web 服务一致。
|
||||
* 文档:https://lbs.amap.com/api/webservice/guide/api/georegeo
|
||||
*/
|
||||
@Override
|
||||
public AddressInfo getAddressByCoordinate(Coordinate coordinate) {
|
||||
if (coordinate == null || coordinate.getLongitude() == null || coordinate.getLatitude() == null) {
|
||||
log.warn("高德逆地理编码:坐标为空");
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(gaodeAK)) {
|
||||
log.warn("高德逆地理编码:未配置 map.gaode_key");
|
||||
return null;
|
||||
}
|
||||
double lng = coordinate.getLongitude().doubleValue();
|
||||
double lat = coordinate.getLatitude().doubleValue();
|
||||
// 经度在前、纬度在后,小数点后不超过 6 位
|
||||
String location = String.format(Locale.US, "%.6f,%.6f", lng, lat);
|
||||
Map<String, String> regeoParams = new HashMap<>(4);
|
||||
regeoParams.put("extensions", "base");
|
||||
regeoParams.put("location", location);
|
||||
regeoParams.put("output", "json");
|
||||
String param = buildGaodeWebServiceQuery(regeoParams);
|
||||
String response = HttpUtils.sendGet(FastBeeConstant.MAP.GAODE_BASE + GAODE_PATH_REGEO, param, Constants.UTF8);
|
||||
if (StringUtils.isBlank(response)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject root = JSONObject.parseObject(response);
|
||||
if (root == null || !"1".equals(String.valueOf(root.get("status")))) {
|
||||
log.warn("高德逆地理编码失败 status={} info={}", root != null ? root.get("status") : null,
|
||||
root != null ? root.getString("info") : null);
|
||||
return null;
|
||||
}
|
||||
JSONObject regeocode = root.getJSONObject("regeocode");
|
||||
if (regeocode == null) {
|
||||
return null;
|
||||
}
|
||||
AddressInfo info = new AddressInfo();
|
||||
info.setFormattedAddress(regeocode.getString("formatted_address"));
|
||||
JSONObject ac = regeocode.getJSONObject("addressComponent");
|
||||
if (ac != null) {
|
||||
info.setCountry(ac.getString("country"));
|
||||
info.setProvince(ac.getString("province"));
|
||||
info.setCity(parseGaodeCity(ac));
|
||||
info.setDistrict(ac.getString("district"));
|
||||
info.setTown(ac.getString("township"));
|
||||
JSONObject streetNumber = ac.getJSONObject("streetNumber");
|
||||
if (streetNumber != null) {
|
||||
info.setStreet(streetNumber.getString("street"));
|
||||
info.setStreetNumber(streetNumber.getString("number"));
|
||||
}
|
||||
JSONObject building = ac.getJSONObject("building");
|
||||
if (building != null) {
|
||||
info.setBuilding(building.getString("name"));
|
||||
}
|
||||
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直辖市等场景下 city 可能为空数组,需回退到省名等。
|
||||
*/
|
||||
private static String parseGaodeCity(JSONObject ac) {
|
||||
Object cityVal = ac.get("city");
|
||||
if (cityVal instanceof String) {
|
||||
String s = (String) cityVal;
|
||||
if (StringUtils.isNotBlank(s)) {
|
||||
return s;
|
||||
}
|
||||
} else if (cityVal instanceof JSONArray) {
|
||||
JSONArray arr = (JSONArray) cityVal;
|
||||
if (!arr.isEmpty()) {
|
||||
return arr.getString(0);
|
||||
}
|
||||
}
|
||||
String province = ac.getString("province");
|
||||
return StringUtils.isNotBlank(province) ? province : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装高德 Web 服务 GET 查询串:参数名升序拼接后接私钥做 MD5 得到 sig(与官方数字签名说明一致)。
|
||||
* 未配置 {@code map.gaode_secret} 时不传 sig,适用于未开启数字签名的 Key。
|
||||
*/
|
||||
private String buildGaodeWebServiceQuery(Map<String, String> paramsWithoutKey) {
|
||||
TreeMap<String, String> sorted = new TreeMap<>(paramsWithoutKey);
|
||||
sorted.put("key", gaodeAK);
|
||||
if (StringUtils.isNotBlank(gaodeSecret)) {
|
||||
String signSource = joinKeyValueSorted(sorted) + gaodeSecret;
|
||||
sorted.put("sig", Md5Utils.hash(signSource));
|
||||
}
|
||||
|
||||
return joinKeyValueSorted(sorted);
|
||||
}
|
||||
|
||||
private static String joinKeyValueSorted(TreeMap<String, String> sorted) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, String> e : sorted.entrySet()) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append('&');
|
||||
}
|
||||
sb.append(e.getKey()).append('=').append(e.getValue());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -33,4 +240,14 @@ public class GaodeLocationService implements ILocationService {
|
||||
|
||||
return new Coordinate(BigDecimal.valueOf(doubles[0]), BigDecimal.valueOf(doubles[1]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate convertPlatformCoordinateFromWgs84(Coordinate wgs84Coordinate) {
|
||||
// 将世界坐标 转化为 高德(火星)坐标
|
||||
double[] doubles = CoordinateTransformUtils.wgs84ToGcj02(
|
||||
wgs84Coordinate.getLongitude().doubleValue(),
|
||||
wgs84Coordinate.getLatitude().doubleValue()
|
||||
);
|
||||
return new Coordinate(BigDecimal.valueOf(doubles[0]), BigDecimal.valueOf(doubles[1]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.fastbee.http.map;
|
||||
|
||||
import com.fastbee.http.model.AddressInfo;
|
||||
import com.fastbee.http.model.Coordinate;
|
||||
|
||||
/**
|
||||
@ -18,11 +19,13 @@ public interface ILocationService {
|
||||
Coordinate getCoordinateByAddress(String address);
|
||||
|
||||
/**
|
||||
* 根据经纬度获取详细地址
|
||||
* @param coordinate 经纬度
|
||||
* @return 详细地址信息
|
||||
* 根据经纬度逆地理编码获取结构化地址。
|
||||
* 坐标系因实现而异:{@code gaodeLocationService} 使用 GCJ-02;{@code baiduLocationService} 使用 BD09。
|
||||
*
|
||||
* @param coordinate 经纬度(经度、纬度)
|
||||
* @return 详细地址信息;参数无效、配置缺失或第三方失败时可能为 {@code null}
|
||||
*/
|
||||
//AddressInfo getAddressByCoordinate(Coordinate coordinate);
|
||||
AddressInfo getAddressByCoordinate(Coordinate coordinate);
|
||||
|
||||
|
||||
/**
|
||||
@ -31,4 +34,12 @@ public interface ILocationService {
|
||||
* @return 世界坐标系
|
||||
*/
|
||||
Coordinate convertToWgs84(Coordinate coordinate);
|
||||
|
||||
|
||||
/**
|
||||
* 将世界坐标系(WGS84)的坐标转换为指定平台(高德、百度、腾讯)坐标系。
|
||||
* @param wgs84Coordinate WGS84 坐标
|
||||
* @return 转换后的目标坐标系坐标
|
||||
*/
|
||||
Coordinate convertPlatformCoordinateFromWgs84(Coordinate wgs84Coordinate);
|
||||
}
|
||||
|
||||
@ -26,14 +26,23 @@ public class LocationServiceFactory {
|
||||
|
||||
/**
|
||||
* 获取当前配置的 LocationService 实例
|
||||
* @param providerArr 指定地图提供者,优先级高于配置文件
|
||||
* @return 具体服务实现
|
||||
*/
|
||||
public ILocationService getLocationService() {
|
||||
// if ("gaode".equalsIgnoreCase(provider)) {
|
||||
// return gaodeLocationService;
|
||||
// } else if ("tencent".equalsIgnoreCase(provider)) {
|
||||
// return tencentLocationService;
|
||||
// }
|
||||
public ILocationService getLocationService(String... providerArr) {
|
||||
|
||||
String provider;
|
||||
// 优先参数指定服务商
|
||||
if (providerArr != null && providerArr.length > 0){
|
||||
provider = providerArr[0];
|
||||
} else {
|
||||
provider = this.provider;
|
||||
}
|
||||
if ("gaode".equalsIgnoreCase(provider)) {
|
||||
return gaodeLocationService;
|
||||
} /*else if ("tencent".equalsIgnoreCase(provider)) {
|
||||
return tencentLocationService;
|
||||
}*/
|
||||
// 默认返回百度
|
||||
return baiduLocationService;
|
||||
}
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package com.fastbee.http.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @description: 地址信息
|
||||
* 国家、省份、城市、区县、城镇、乡村、街道、门牌号码、屋邨、大厦
|
||||
*
|
||||
* @author Sumuxi
|
||||
* @date 2026/3/28
|
||||
*/
|
||||
@Data
|
||||
public class AddressInfo {
|
||||
|
||||
/**
|
||||
* 格式化地址
|
||||
*/
|
||||
private String formattedAddress;
|
||||
|
||||
/**
|
||||
* 国家
|
||||
*/
|
||||
private String country;
|
||||
|
||||
/**
|
||||
* 省份
|
||||
*/
|
||||
private String province;
|
||||
|
||||
/**
|
||||
* 城市
|
||||
*/
|
||||
private String city;
|
||||
|
||||
/**
|
||||
* 区/县
|
||||
*/
|
||||
private String district;
|
||||
|
||||
/**
|
||||
* 乡/镇
|
||||
*/
|
||||
private String town;
|
||||
|
||||
/**
|
||||
* 街道
|
||||
*/
|
||||
private String street;
|
||||
|
||||
/**
|
||||
* 门牌号
|
||||
*/
|
||||
private String streetNumber;
|
||||
|
||||
/**
|
||||
* 建筑/楼栋
|
||||
*/
|
||||
private String building;
|
||||
}
|
||||
@ -181,7 +181,7 @@ public class Device extends BaseEntity {
|
||||
/**
|
||||
* 是否自定义位置
|
||||
**/
|
||||
@ApiModelProperty("定位方式(1=ip自动定位,2=设备定位,3=自定义)")
|
||||
@ApiModelProperty("定位方式(1=自动定位[ip地址、modbus LBS],2=设备定位,3=自定义)")
|
||||
private Integer locationWay;
|
||||
|
||||
/**
|
||||
|
||||
@ -27,6 +27,7 @@ import com.fastbee.common.utils.json.JsonUtils;
|
||||
import com.fastbee.http.ip.IPAddressService;
|
||||
import com.fastbee.http.map.ILocationService;
|
||||
import com.fastbee.http.map.LocationServiceFactory;
|
||||
import com.fastbee.http.model.AddressInfo;
|
||||
import com.fastbee.http.model.Coordinate;
|
||||
import com.fastbee.iot.cache.IDeviceCache;
|
||||
import com.fastbee.iot.cache.ITSLCache;
|
||||
@ -1099,6 +1100,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
// 随机经纬度和地址
|
||||
//SysUser user = getLoginUser().getUser();
|
||||
device.setNetworkIp(sysUser.getLoginIp());
|
||||
device.setTransport(product.getTransport()); // 便于Modbus设备LBS定位
|
||||
setLocation(sysUser.getLoginIp(), device);
|
||||
deviceMapper.insertDevice(device);
|
||||
// 处理modbus产品
|
||||
@ -1632,13 +1634,29 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
* @param device 设备信息
|
||||
*/
|
||||
private void setLocation(String ip, Device device) {
|
||||
|
||||
// 工厂模式获取定位服务
|
||||
ILocationService service = locationServiceFactory.getLocationService();
|
||||
|
||||
// Modbus设备采用LBS定位(从设备获取经纬度)
|
||||
if (Objects.equals(1, device.getLocationWay()) && FastBeeConstant.PROTOCOL.ModbusRtu.equals(device.getTransport())) {
|
||||
log.info("Modbus设备采用LBS定位{}", device.getSerialNumber());
|
||||
// 将经纬度转换为地址编码 networkAddress
|
||||
if (StringUtils.isEmpty(device.getNetworkAddress()) && device.getLongitude() != null && device.getLatitude() != null) {
|
||||
Coordinate platformCoordinate = service.convertPlatformCoordinateFromWgs84(new Coordinate(device.getLongitude(), device.getLatitude()));
|
||||
AddressInfo addressInfo = service.getAddressByCoordinate(platformCoordinate);
|
||||
String networkAddress = StringUtils.isEmpty(addressInfo.getBuilding()) ? addressInfo.getFormattedAddress() : addressInfo.getBuilding();
|
||||
device.setNetworkAddress(networkAddress);
|
||||
log.info("{}- 设置地址:{}", device.getSerialNumber(), networkAddress);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 根据IP获取地址
|
||||
String address = ipAddressService.getAddressByIP(ip);
|
||||
device.setNetworkAddress(address);
|
||||
log.info("{}- 设置地址:{}", device.getSerialNumber(), address);
|
||||
// 查询经纬度
|
||||
ILocationService service = locationServiceFactory.getLocationService();
|
||||
Coordinate coordinate = service.getCoordinateByAddress(address);
|
||||
Coordinate wgs84Coordinate = service.convertToWgs84(coordinate);
|
||||
device.setLongitude(wgs84Coordinate.getLongitude());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user