增加详细地址计算与店铺的距离

This commit is contained in:
cb 2025-08-23 16:45:00 +08:00
parent 0a37c352fb
commit 353e302df9
4 changed files with 192 additions and 74 deletions

3
.gitignore vendored
View File

@ -33,4 +33,5 @@ build/
### VS Code ###
.vscode/
/logs/
/ghy-admin/src/test/
/ghy-admin/src/test/
ghy-shop/ADDRESS_QUERY_EXAMPLE.md

View File

@ -439,6 +439,12 @@ public class ShopController extends BaseController {
/**
* 通过店铺ID查询详细信息并计算距离
* 最终效果
* 1. 传入的详细地址 转换为经纬度
* 2. 店铺有经纬度 直接使用
* 3. 店铺没有经纬度 通过详细地址生成经纬度
* 4. 最终通过经纬度计算距离 放在店铺的距离字段上
*
* @param query 距离查询请求实体
* @return 包含距离信息的完整店铺详情
*/
@ -450,14 +456,25 @@ public class ShopController extends BaseController {
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("无效的坐标参数");
}
// 验证地址参数如果提供了地址信息则验证其完整性
if (query.hasAddressInfo()) {
String fullAddress = query.getFullAddress();
if (fullAddress.trim().length() < 5) {
return AjaxResult.error("地址信息不完整,请提供更详细的地址");
}
logger.info("使用传入的地址信息: {}", fullAddress);
}
// 调用服务层方法获取店铺详情和距离
Shop shop = shopService.getShopWithDistance(query);
@ -466,8 +483,13 @@ public class ShopController extends BaseController {
}
// 检查距离是否有效
if (shop.getDistance() == null || shop.getDistance().equals("-1")) {
return AjaxResult.error("无法计算店铺距离,请检查店铺地址信息");
if (shop.getDistance() == null || shop.getDistance().equals("无法计算距离")) {
if (query.hasCoordinateInfo()) {
return AjaxResult.error("无法计算店铺距离,请检查店铺地址信息或提供更详细的地址参数");
} else {
// 如果没有经纬度只获取位置信息
logger.info("仅获取店铺位置信息,无法计算距离");
}
}
// 构建返回结果
@ -490,14 +512,7 @@ public class ShopController extends BaseController {
result.put("longitude", shop.getLongitude());
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);
} catch (Exception e) {

View File

@ -9,9 +9,20 @@ import java.io.Serializable;
@Data
public class ShopDistanceQuery implements Serializable {
private Long shopId; // 店铺ID
private Double latitude; // 当前纬度
private Double longitude; // 当前经度
private Long shopId; // 店铺ID (必填)
private Double latitude; // 当前纬度 (可选如果有地址信息)
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()
@ -24,4 +35,42 @@ public class ShopDistanceQuery implements Serializable {
this.latitude = latitude;
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();
}
}

View File

@ -11,9 +11,13 @@ import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class ShopServiceImpl implements ShopService {
private static final Logger logger = LoggerFactory.getLogger(ShopServiceImpl.class);
@Autowired
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 (query.getUpdateShopLocation() != null && query.getUpdateShopLocation() &&
shop.getLatitude() != null && shop.getLongitude() != null) {
shopMapper.update(shop);
if (distance >= 0) {
shop.setDistance(LocationUtils.formatDistance(distance * 1000)); // 转换为米并格式化
} else {
shop.setDistance("无法计算距离");
}
return shop;
@ -72,19 +74,50 @@ public class ShopServiceImpl implements ShopService {
/**
* 计算店铺与指定坐标的距离
* 如果店铺有经纬度直接计算如果没有则通过地址解析获取经纬度后计算
* 实现逻辑
* 1. 传入的详细地址 转换为经纬度作为用户当前位置
* 2. 店铺有经纬度 直接使用
* 3. 店铺没有经纬度 通过详细地址生成经纬度
* 4. 最终计算用户位置与店铺位置的距离
*/
private double calculateDistance(Shop shop, Double latitude, Double longitude) {
// 如果店铺有经纬度直接计算距离
if (shop.getLatitude() != null && shop.getLongitude() != null) {
return LocationUtils.getDistanceInKilometers(
latitude, longitude,
shop.getLatitude(), shop.getLongitude()
);
private double calculateDistance(Shop shop, ShopDistanceQuery query) {
Double userLat = null;
Double userLng = null;
Double shopLat = null;
Double shopLng = null;
// 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());
}
}
// 如果店铺没有经纬度但有地址尝试通过地址解析获取经纬度
if (shop.getAddress() != null && !shop.getAddress().trim().isEmpty()) {
// 2. 获取店铺位置优先使用店铺已有的经纬度其次使用店铺地址生成的经纬度
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 {
// 构建完整地址
StringBuilder fullAddress = new StringBuilder();
@ -96,69 +129,89 @@ public class ShopServiceImpl implements ShopService {
String address = fullAddress.toString();
if (!address.isEmpty()) {
// 这里应该调用地图API获取地址的经纬度
// 暂时使用示例坐标实际使用时请替换为真实的API调用
logger.info("通过店铺地址信息生成经纬度: {}", address);
Map<String, Double> coordinates = getCoordinatesByAddress(address);
if (coordinates != null) {
double shopLat = coordinates.get("latitude");
double shopLng = coordinates.get("longitude");
// 计算距离
double distance = LocationUtils.getDistanceInKilometers(
latitude, longitude, shopLat, shopLng
);
shopLat = coordinates.get("latitude");
shopLng = coordinates.get("longitude");
logger.info("通过店铺地址成功生成经纬度: 经度={}, 纬度={}", shopLng, shopLat);
// 更新店铺的经纬度信息
shop.setLatitude(shopLat);
shop.setLongitude(shopLng);
return distance;
// 如果请求更新店铺位置信息则保存到数据库
if (query.getUpdateShopLocation() != null && query.getUpdateShopLocation()) {
shopMapper.update(shop);
logger.info("已更新并保存店铺[{}]的经纬度: 经度={}, 纬度={}",
shop.getShopName(), shopLng, shopLat);
}
}
}
} catch (Exception e) {
// 地址解析失败记录日志但不影响主流程
System.err.println("地址解析失败: " + e.getMessage());
logger.warn("通过店铺地址生成经纬度失败: {}", e.getMessage());
}
}
// 无法获取距离返回-1表示无效
return -1;
// 3. 计算用户位置与店铺位置的距离
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) {
try {
// TODO: 这里应该调用真实的地图API
// 示例百度地图正向地理编码API
// String url = "https://api.map.baidu.com/geocoding/v3/?address=" +
// 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;
// }
if (address == null || address.trim().isEmpty()) {
logger.warn("地址为空,无法获取坐标");
return null;
}
// 临时返回示例坐标实际使用时请替换为真实的API调用
Map<String, Double> coordinates = new HashMap<>();
coordinates.put("latitude", 39.915 + Math.random() * 0.1); // 示例纬度
coordinates.put("longitude", 116.404 + Math.random() * 0.1); // 示例经度
logger.info("开始调用百度地图API解析地址: {}", address);
// 调用百度地图正向地理编码API
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) {
System.err.println("地址解析失败: " + e.getMessage());
logger.error("调用百度地图API失败: {} - {}", address, e.getMessage(), e);
return null;
}
}