增加详细地址计算与店铺的距离
This commit is contained in:
parent
0a37c352fb
commit
353e302df9
|
|
@ -34,3 +34,4 @@ build/
|
||||||
.vscode/
|
.vscode/
|
||||||
/logs/
|
/logs/
|
||||||
/ghy-admin/src/test/
|
/ghy-admin/src/test/
|
||||||
|
ghy-shop/ADDRESS_QUERY_EXAMPLE.md
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,12 @@ public class ShopController extends BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过店铺ID查询详细信息并计算距离
|
* 通过店铺ID查询详细信息并计算距离
|
||||||
|
* 最终效果:
|
||||||
|
* 1. 传入的详细地址 → 转换为经纬度
|
||||||
|
* 2. 店铺有经纬度 → 直接使用
|
||||||
|
* 3. 店铺没有经纬度 → 通过详细地址生成经纬度
|
||||||
|
* 4. 最终通过经纬度计算距离 → 放在店铺的距离字段上
|
||||||
|
*
|
||||||
* @param query 距离查询请求实体
|
* @param query 距离查询请求实体
|
||||||
* @return 包含距离信息的完整店铺详情
|
* @return 包含距离信息的完整店铺详情
|
||||||
*/
|
*/
|
||||||
|
|
@ -450,14 +456,25 @@ public class ShopController extends BaseController {
|
||||||
return AjaxResult.error("店铺ID不能为空");
|
return AjaxResult.error("店铺ID不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.getLatitude() == null || query.getLongitude() == null) {
|
// 验证是否有足够的信息来计算距离或获取位置
|
||||||
return AjaxResult.error("经纬度参数不能为空");
|
if (!query.hasEnoughInfo()) {
|
||||||
|
return AjaxResult.error("请提供经纬度或地址信息");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LocationUtils.isValidCoordinate(query.getLatitude(), query.getLongitude())) {
|
// 如果提供了经纬度,验证其有效性
|
||||||
|
if (query.hasCoordinateInfo() && !LocationUtils.isValidCoordinate(query.getLatitude(), query.getLongitude())) {
|
||||||
return AjaxResult.error("无效的坐标参数");
|
return AjaxResult.error("无效的坐标参数");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证地址参数(如果提供了地址信息,则验证其完整性)
|
||||||
|
if (query.hasAddressInfo()) {
|
||||||
|
String fullAddress = query.getFullAddress();
|
||||||
|
if (fullAddress.trim().length() < 5) {
|
||||||
|
return AjaxResult.error("地址信息不完整,请提供更详细的地址");
|
||||||
|
}
|
||||||
|
logger.info("使用传入的地址信息: {}", fullAddress);
|
||||||
|
}
|
||||||
|
|
||||||
// 调用服务层方法获取店铺详情和距离
|
// 调用服务层方法获取店铺详情和距离
|
||||||
Shop shop = shopService.getShopWithDistance(query);
|
Shop shop = shopService.getShopWithDistance(query);
|
||||||
|
|
||||||
|
|
@ -466,8 +483,13 @@ public class ShopController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查距离是否有效
|
// 检查距离是否有效
|
||||||
if (shop.getDistance() == null || shop.getDistance().equals("-1")) {
|
if (shop.getDistance() == null || shop.getDistance().equals("无法计算距离")) {
|
||||||
return AjaxResult.error("无法计算店铺距离,请检查店铺地址信息");
|
if (query.hasCoordinateInfo()) {
|
||||||
|
return AjaxResult.error("无法计算店铺距离,请检查店铺地址信息或提供更详细的地址参数");
|
||||||
|
} else {
|
||||||
|
// 如果没有经纬度,只获取位置信息
|
||||||
|
logger.info("仅获取店铺位置信息,无法计算距离");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建返回结果
|
// 构建返回结果
|
||||||
|
|
@ -490,13 +512,6 @@ public class ShopController extends BaseController {
|
||||||
result.put("longitude", shop.getLongitude());
|
result.put("longitude", shop.getLongitude());
|
||||||
result.put("distance", shop.getDistance());
|
result.put("distance", shop.getDistance());
|
||||||
|
|
||||||
// 添加额外的距离信息
|
|
||||||
Map<String, Object> distanceInfo = new HashMap<>();
|
|
||||||
distanceInfo.put("formattedDistance", shop.getDistance());
|
|
||||||
distanceInfo.put("currentLatitude", query.getLatitude());
|
|
||||||
distanceInfo.put("currentLongitude", query.getLongitude());
|
|
||||||
|
|
||||||
result.put("distanceInfo", distanceInfo);
|
|
||||||
|
|
||||||
return AjaxResult.success("查询成功", result);
|
return AjaxResult.success("查询成功", result);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,20 @@ import java.io.Serializable;
|
||||||
@Data
|
@Data
|
||||||
public class ShopDistanceQuery implements Serializable {
|
public class ShopDistanceQuery implements Serializable {
|
||||||
|
|
||||||
private Long shopId; // 店铺ID
|
private Long shopId; // 店铺ID (必填)
|
||||||
private Double latitude; // 当前纬度
|
private Double latitude; // 当前纬度 (可选,如果有地址信息)
|
||||||
private Double longitude; // 当前经度
|
private Double longitude; // 当前经度 (可选,如果有地址信息)
|
||||||
|
|
||||||
|
// 地址相关参数(用于查询经纬度)
|
||||||
|
private Long provinceId; // 省份ID
|
||||||
|
private String provinceName; // 省份名称
|
||||||
|
private Long cityId; // 城市ID
|
||||||
|
private String cityName; // 城市名称
|
||||||
|
private Long countryId; // 区县ID
|
||||||
|
private String countryName; // 区县名称
|
||||||
|
private Long streetId; // 街道ID
|
||||||
|
private String streetName; // 街道名称
|
||||||
|
private String address; // 详细地址
|
||||||
|
|
||||||
// 可选参数,用于扩展
|
// 可选参数,用于扩展
|
||||||
private String unit; // 距离单位:km(公里)、m(米)
|
private String unit; // 距离单位:km(公里)、m(米)
|
||||||
|
|
@ -24,4 +35,42 @@ public class ShopDistanceQuery implements Serializable {
|
||||||
this.latitude = latitude;
|
this.latitude = latitude;
|
||||||
this.longitude = longitude;
|
this.longitude = longitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整地址字符串
|
||||||
|
*/
|
||||||
|
public String getFullAddress() {
|
||||||
|
StringBuilder fullAddress = new StringBuilder();
|
||||||
|
if (provinceName != null) fullAddress.append(provinceName);
|
||||||
|
if (cityName != null) fullAddress.append(cityName);
|
||||||
|
if (countryName != null) fullAddress.append(countryName);
|
||||||
|
if (streetName != null) fullAddress.append(streetName);
|
||||||
|
if (address != null) fullAddress.append(address);
|
||||||
|
return fullAddress.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有地址信息
|
||||||
|
*/
|
||||||
|
public boolean hasAddressInfo() {
|
||||||
|
return (provinceName != null && !provinceName.trim().isEmpty()) ||
|
||||||
|
(cityName != null && !cityName.trim().isEmpty()) ||
|
||||||
|
(countryName != null && !countryName.trim().isEmpty()) ||
|
||||||
|
(streetName != null && !streetName.trim().isEmpty()) ||
|
||||||
|
(address != null && !address.trim().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有坐标信息
|
||||||
|
*/
|
||||||
|
public boolean hasCoordinateInfo() {
|
||||||
|
return latitude != null && longitude != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有足够的信息来计算距离
|
||||||
|
*/
|
||||||
|
public boolean hasEnoughInfo() {
|
||||||
|
return hasAddressInfo() || hasCoordinateInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,13 @@ import org.springframework.stereotype.Service;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ShopServiceImpl implements ShopService {
|
public class ShopServiceImpl implements ShopService {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ShopServiceImpl.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ShopMapper shopMapper;
|
private ShopMapper shopMapper;
|
||||||
|
|
||||||
|
|
@ -56,15 +60,13 @@ public class ShopServiceImpl implements ShopService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算距离
|
// 计算距离
|
||||||
double distance = calculateDistance(shop, query.getLatitude(), query.getLongitude());
|
double distance = calculateDistance(shop, query);
|
||||||
|
|
||||||
// 设置距离信息
|
// 设置距离信息
|
||||||
shop.setDistance(LocationUtils.formatDistance(distance * 1000)); // 转换为米并格式化
|
if (distance >= 0) {
|
||||||
|
shop.setDistance(LocationUtils.formatDistance(distance * 1000)); // 转换为米并格式化
|
||||||
// 如果请求更新店铺位置信息,则更新店铺的经纬度
|
} else {
|
||||||
if (query.getUpdateShopLocation() != null && query.getUpdateShopLocation() &&
|
shop.setDistance("无法计算距离");
|
||||||
shop.getLatitude() != null && shop.getLongitude() != null) {
|
|
||||||
shopMapper.update(shop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shop;
|
return shop;
|
||||||
|
|
@ -72,19 +74,50 @@ public class ShopServiceImpl implements ShopService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算店铺与指定坐标的距离
|
* 计算店铺与指定坐标的距离
|
||||||
* 如果店铺有经纬度,直接计算;如果没有,则通过地址解析获取经纬度后计算
|
* 实现逻辑:
|
||||||
|
* 1. 传入的详细地址 → 转换为经纬度(作为用户当前位置)
|
||||||
|
* 2. 店铺有经纬度 → 直接使用
|
||||||
|
* 3. 店铺没有经纬度 → 通过详细地址生成经纬度
|
||||||
|
* 4. 最终计算用户位置与店铺位置的距离
|
||||||
*/
|
*/
|
||||||
private double calculateDistance(Shop shop, Double latitude, Double longitude) {
|
private double calculateDistance(Shop shop, ShopDistanceQuery query) {
|
||||||
// 如果店铺有经纬度,直接计算距离
|
Double userLat = null;
|
||||||
if (shop.getLatitude() != null && shop.getLongitude() != null) {
|
Double userLng = null;
|
||||||
return LocationUtils.getDistanceInKilometers(
|
Double shopLat = null;
|
||||||
latitude, longitude,
|
Double shopLng = null;
|
||||||
shop.getLatitude(), shop.getLongitude()
|
|
||||||
);
|
// 1. 获取用户当前位置(优先使用传入的经纬度,其次使用传入地址转换的经纬度)
|
||||||
|
if (query.hasCoordinateInfo()) {
|
||||||
|
// 直接使用传入的经纬度作为用户位置
|
||||||
|
userLat = query.getLatitude();
|
||||||
|
userLng = query.getLongitude();
|
||||||
|
logger.info("使用传入的经纬度作为用户位置: 经度={}, 纬度={}", userLng, userLat);
|
||||||
|
} else if (query.hasAddressInfo()) {
|
||||||
|
// 通过传入的地址信息生成用户位置的经纬度
|
||||||
|
try {
|
||||||
|
String fullAddress = query.getFullAddress();
|
||||||
|
if (!fullAddress.isEmpty()) {
|
||||||
|
logger.info("通过传入的地址信息生成用户位置经纬度: {}", fullAddress);
|
||||||
|
|
||||||
|
Map<String, Double> coordinates = getCoordinatesByAddress(fullAddress);
|
||||||
|
if (coordinates != null) {
|
||||||
|
userLat = coordinates.get("latitude");
|
||||||
|
userLng = coordinates.get("longitude");
|
||||||
|
logger.info("通过传入地址成功生成用户位置经纬度: 经度={}, 纬度={}", userLng, userLat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("通过传入地址生成用户位置经纬度失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果店铺没有经纬度但有地址,尝试通过地址解析获取经纬度
|
// 2. 获取店铺位置(优先使用店铺已有的经纬度,其次使用店铺地址生成的经纬度)
|
||||||
if (shop.getAddress() != null && !shop.getAddress().trim().isEmpty()) {
|
if (shop.getLatitude() != null && shop.getLongitude() != null) {
|
||||||
|
shopLat = shop.getLatitude();
|
||||||
|
shopLng = shop.getLongitude();
|
||||||
|
logger.info("使用店铺已有的经纬度: 经度={}, 纬度={}", shopLng, shopLat);
|
||||||
|
} else if (shop.getAddress() != null && !shop.getAddress().trim().isEmpty()) {
|
||||||
|
// 通过店铺的地址信息生成经纬度
|
||||||
try {
|
try {
|
||||||
// 构建完整地址
|
// 构建完整地址
|
||||||
StringBuilder fullAddress = new StringBuilder();
|
StringBuilder fullAddress = new StringBuilder();
|
||||||
|
|
@ -96,69 +129,89 @@ public class ShopServiceImpl implements ShopService {
|
||||||
|
|
||||||
String address = fullAddress.toString();
|
String address = fullAddress.toString();
|
||||||
if (!address.isEmpty()) {
|
if (!address.isEmpty()) {
|
||||||
// 这里应该调用地图API获取地址的经纬度
|
logger.info("通过店铺地址信息生成经纬度: {}", address);
|
||||||
// 暂时使用示例坐标(实际使用时请替换为真实的API调用)
|
|
||||||
Map<String, Double> coordinates = getCoordinatesByAddress(address);
|
Map<String, Double> coordinates = getCoordinatesByAddress(address);
|
||||||
if (coordinates != null) {
|
if (coordinates != null) {
|
||||||
double shopLat = coordinates.get("latitude");
|
shopLat = coordinates.get("latitude");
|
||||||
double shopLng = coordinates.get("longitude");
|
shopLng = coordinates.get("longitude");
|
||||||
|
logger.info("通过店铺地址成功生成经纬度: 经度={}, 纬度={}", shopLng, shopLat);
|
||||||
// 计算距离
|
|
||||||
double distance = LocationUtils.getDistanceInKilometers(
|
|
||||||
latitude, longitude, shopLat, shopLng
|
|
||||||
);
|
|
||||||
|
|
||||||
// 更新店铺的经纬度信息
|
// 更新店铺的经纬度信息
|
||||||
shop.setLatitude(shopLat);
|
shop.setLatitude(shopLat);
|
||||||
shop.setLongitude(shopLng);
|
shop.setLongitude(shopLng);
|
||||||
|
|
||||||
return distance;
|
// 如果请求更新店铺位置信息,则保存到数据库
|
||||||
|
if (query.getUpdateShopLocation() != null && query.getUpdateShopLocation()) {
|
||||||
|
shopMapper.update(shop);
|
||||||
|
logger.info("已更新并保存店铺[{}]的经纬度: 经度={}, 纬度={}",
|
||||||
|
shop.getShopName(), shopLng, shopLat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 地址解析失败,记录日志但不影响主流程
|
logger.warn("通过店铺地址生成经纬度失败: {}", e.getMessage());
|
||||||
System.err.println("地址解析失败: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无法获取距离,返回-1表示无效
|
// 3. 计算用户位置与店铺位置的距离
|
||||||
return -1;
|
if (userLat != null && userLng != null && shopLat != null && shopLng != null) {
|
||||||
|
// 有用户位置和店铺位置,计算距离
|
||||||
|
double distance = LocationUtils.getDistanceInKilometers(userLat, userLng, shopLat, shopLng);
|
||||||
|
logger.info("成功计算用户位置与店铺位置的距离: {} 公里", distance);
|
||||||
|
return distance;
|
||||||
|
} else if (shopLat != null && shopLng != null) {
|
||||||
|
// 只有店铺位置,没有用户位置,无法计算距离
|
||||||
|
logger.info("已获取店铺位置,但未获取到用户位置,无法计算距离");
|
||||||
|
return 0; // 返回0表示成功获取店铺位置但无法计算距离
|
||||||
|
} else {
|
||||||
|
// 无法获取店铺位置,返回-1表示无效
|
||||||
|
logger.warn("无法获取店铺[{}]的经纬度信息,无法计算距离", shop.getShopName());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过地址获取经纬度坐标
|
* 通过地址获取经纬度坐标
|
||||||
* 这是一个示例实现,实际使用时请替换为真实的地图API调用
|
* 直接调用百度地图正向地理编码API
|
||||||
*/
|
*/
|
||||||
private Map<String, Double> getCoordinatesByAddress(String address) {
|
private Map<String, Double> getCoordinatesByAddress(String address) {
|
||||||
try {
|
try {
|
||||||
// TODO: 这里应该调用真实的地图API
|
if (address == null || address.trim().isEmpty()) {
|
||||||
// 示例:百度地图正向地理编码API
|
logger.warn("地址为空,无法获取坐标");
|
||||||
// String url = "https://api.map.baidu.com/geocoding/v3/?address=" +
|
return null;
|
||||||
// java.net.URLEncoder.encode(address, "UTF-8") +
|
}
|
||||||
// "&output=json&ak=YOUR_BAIDU_API_KEY";
|
|
||||||
//
|
|
||||||
// String result = restTemplate.getForObject(url, String.class);
|
|
||||||
// JSONObject jsonResult = JSONObject.parseObject(result);
|
|
||||||
//
|
|
||||||
// if ("0".equals(jsonResult.getString("status"))) {
|
|
||||||
// JSONObject location = jsonResult.getJSONObject("result").getJSONObject("location");
|
|
||||||
// Double lng = location.getDouble("lng");
|
|
||||||
// Double lat = location.getDouble("lat");
|
|
||||||
//
|
|
||||||
// Map<String, Double> coordinates = new HashMap<>();
|
|
||||||
// coordinates.put("longitude", lng);
|
|
||||||
// coordinates.put("latitude", lat);
|
|
||||||
// return coordinates;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 临时返回示例坐标(实际使用时请替换为真实的API调用)
|
logger.info("开始调用百度地图API解析地址: {}", address);
|
||||||
Map<String, Double> coordinates = new HashMap<>();
|
|
||||||
coordinates.put("latitude", 39.915 + Math.random() * 0.1); // 示例纬度
|
// 调用百度地图正向地理编码API
|
||||||
coordinates.put("longitude", 116.404 + Math.random() * 0.1); // 示例经度
|
String encoded = java.net.URLEncoder.encode(address, "UTF-8");
|
||||||
|
String url = "https://api.map.baidu.com/geocoding/v3/?output=json&ak=ZQTgMW7W0GTuE7Ripb0HDp5TqRaOI6PZ&address=" + encoded;
|
||||||
|
|
||||||
|
// 使用HttpUtils发送GET请求
|
||||||
|
String result = com.ghy.common.utils.http.HttpUtils.sendGet(url);
|
||||||
|
result = result.replaceAll("\n", "").replaceAll("\t", "");
|
||||||
|
|
||||||
|
// 解析返回结果
|
||||||
|
com.alibaba.fastjson.JSONObject resultJson = com.alibaba.fastjson.JSONObject.parseObject(result);
|
||||||
|
if ("0".equals(resultJson.getString("status"))) {
|
||||||
|
com.alibaba.fastjson.JSONObject location = resultJson.getJSONObject("result").getJSONObject("location");
|
||||||
|
Double lng = location.getDouble("lng");
|
||||||
|
Double lat = location.getDouble("lat");
|
||||||
|
|
||||||
|
Map<String, Double> coordinates = new HashMap<>();
|
||||||
|
coordinates.put("longitude", lng);
|
||||||
|
coordinates.put("latitude", lat);
|
||||||
|
|
||||||
|
logger.info("百度地图API解析成功: {} -> 经度={}, 纬度={}", address, lng, lat);
|
||||||
|
return coordinates;
|
||||||
|
} else {
|
||||||
|
logger.error("百度地图API解析失败: {} - {}", address, resultJson.getString("msg"));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return coordinates;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("地址解析失败: " + e.getMessage());
|
logger.error("调用百度地图API失败: {} - {}", address, e.getMessage(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue