增加地址经纬度以及逆解析地址到经纬度

This commit is contained in:
cb 2025-08-22 16:31:17 +08:00
parent 510e696030
commit c8811b0e60
5 changed files with 375 additions and 161 deletions

View File

@ -11,6 +11,8 @@ import com.ghy.common.utils.StringUtils;
import com.ghy.common.utils.bean.BeanUtils;
import com.ghy.common.utils.poi.ExcelUtil;
import com.ghy.common.utils.LocationUtils;
import com.ghy.common.utils.http.HttpUtils;
import com.alibaba.fastjson.JSONObject;
import com.ghy.goods.domain.*;
import com.ghy.goods.service.*;
import com.ghy.shop.domain.Shop;
@ -399,15 +401,65 @@ public class GoodsController extends BaseController {
}
}
/**
* 获取商品详情
*
* @param requestBody 请求参数
* @return 商品详情信息
*/
@PostMapping("/getDetail")
@ResponseBody
public AjaxResult getDetail(@RequestBody Goods goods) {
public AjaxResult getDetail(@RequestBody JSONObject requestBody) {
try {
Goods result = goodsService.selectById(goods.getGoodsId());
// 从请求体中提取参数
Long goodsId = requestBody.getLong("goodsId");
Double userLatitude = requestBody.getDouble("latitude");
Double userLongitude = requestBody.getDouble("longitude");
String provinceName = requestBody.getString("provinceName");
String cityName = requestBody.getString("cityName");
String countryName = requestBody.getString("countryName");
String streetName = requestBody.getString("streetName");
String address = requestBody.getString("address");
// 获取用户当前位置
Double userLatitude = goods.getLatitude();
Double userLongitude = goods.getLongitude();
if (goodsId == null) {
return AjaxResult.error("商品ID不能为空");
}
Goods result = goodsService.selectById(goodsId);
if (result == null) {
return AjaxResult.error("商品不存在");
}
// 如果用户没有提供经纬度但有详细地址则通过地址获取经纬度
if ((userLatitude == null || userLongitude == null) &&
(provinceName != null || cityName != null ||
countryName != null || streetName != null ||
address != null)) {
try {
// 调用百度地理编码接口获取经纬度
JSONObject geocodeBody = new JSONObject();
geocodeBody.put("provinceName", provinceName);
geocodeBody.put("cityName", cityName);
geocodeBody.put("countryName", countryName);
geocodeBody.put("streetName", streetName);
geocodeBody.put("address", address);
String url = "https://gmhl.gmjlb.com/tool/baidu/geocode";
String resultStr = HttpUtils.sendPost(url, geocodeBody.toJSONString());
JSONObject responseJson = JSONObject.parseObject(resultStr);
if (responseJson.getInteger("code") == 200) {
JSONObject data = responseJson.getJSONObject("data");
userLongitude = data.getDouble("longitude");
userLatitude = data.getDouble("latitude");
logger.info("通过地址获取到用户经纬度: 经度={}, 纬度={}", userLongitude, userLatitude);
} else {
logger.warn("通过地址获取用户经纬度失败: {}", responseJson.getString("msg"));
}
} catch (Exception e) {
logger.error("调用百度地理编码接口异常: {}", e.getMessage(), e);
}
}
// 获取商品店铺信息
Shop goodsShop = null;
@ -506,9 +558,6 @@ public class GoodsController extends BaseController {
// 计算异常时设为null
serviceShop.setDistance(null);
}
} else {
// 用户未提供位置或店铺信息不存在
serviceShop.setDistance(null);
}
result.setServiceShop(serviceShop);
@ -583,6 +632,155 @@ public class GoodsController extends BaseController {
}
}
/**
* 根据商品ID和用户位置获取服务店铺列表
* @return 服务店铺列表按距离排序
*/
@PostMapping("/getShopsByGoodsId")
@ResponseBody
public AjaxResult getShopsByGoodsId(@RequestBody JSONObject requestBody) {
try {
Long goodsId = requestBody.getLong("goodsId");
Double latitude = requestBody.getDouble("latitude");
Double longitude = requestBody.getDouble("longitude");
String provinceName = requestBody.getString("provinceName");
String cityName = requestBody.getString("cityName");
String countryName = requestBody.getString("countryName");
String streetName = requestBody.getString("streetName");
String address = requestBody.getString("address");
if (goodsId == null) {
return AjaxResult.error("商品ID不能为空");
}
// 如果用户没有提供经纬度但有详细地址则通过地址获取经纬度
if ((latitude == null || longitude == null) &&
(provinceName != null || cityName != null || countryName != null ||
streetName != null || address != null)) {
try {
// 调用百度地理编码接口获取经纬度
JSONObject geocodeBody = new JSONObject();
geocodeBody.put("provinceName", provinceName);
geocodeBody.put("cityName", cityName);
geocodeBody.put("countryName", countryName);
geocodeBody.put("streetName", streetName);
geocodeBody.put("address", address);
String url = "https://gmhl.gmjlb.com/tool/baidu/geocode";
String resultStr = HttpUtils.sendPost(url, geocodeBody.toJSONString());
JSONObject responseJson = JSONObject.parseObject(resultStr);
if (responseJson.getInteger("code") == 200) {
JSONObject data = responseJson.getJSONObject("data");
longitude = data.getDouble("longitude");
latitude = data.getDouble("latitude");
logger.info("通过地址获取到用户经纬度: 经度={}, 纬度={}", longitude, latitude);
} else {
logger.warn("通过地址获取用户经纬度失败: {}", responseJson.getString("msg"));
}
} catch (Exception e) {
logger.error("调用百度地理编码接口异常: {}", e.getMessage(), e);
}
}
// 获取商品信息
Goods goods = goodsService.selectById(goodsId);
if (goods == null) {
return AjaxResult.error("商品不存在");
}
List<Shop> serviceShops = new ArrayList<>();
// 通过商品的服务类目获取服务店铺
if (goods.getDeptGoodsCategoryId() != null) {
try {
// 1. 通过商品的类目ID获取类目信息
DeptGoodsCategory deptGoodsCategory = deptGoodsCategoryService.selectOneByGoodsCategoryId(goods.getDeptGoodsCategoryId());
if (deptGoodsCategory != null && deptGoodsCategory.getServiceCategoryId() != null) {
logger.debug("商品的服务类目ID: {}", deptGoodsCategory.getServiceCategoryId());
// 2. 通过服务类目ID查询所有使用该服务类目的商品
Goods queryGoods = new Goods();
queryGoods.setDeptGoodsCategoryId(deptGoodsCategory.getServiceCategoryId());
queryGoods.setStatus(0); // 只查询上架的商品
List<Goods> goodsList = goodsService.selectGoodsList(queryGoods);
// 3. 提取所有店铺ID去重
Set<Long> shopIds = goodsList.stream()
.filter(g -> g.getShopId() != null)
.map(Goods::getShopId)
.collect(Collectors.toSet());
if (!shopIds.isEmpty()) {
// 获取所有店铺信息并计算距离
for (Long shopId : shopIds) {
Shop shop = shopService.getShop(shopId);
if (shop != null) {
// 计算距离
if (LocationUtils.isValidCoordinate(latitude, longitude) &&
LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) {
try {
double distanceInMeters = LocationUtils.getDistanceInMeters(
latitude, longitude,
shop.getLatitude(), shop.getLongitude()
);
shop.setDistance(LocationUtils.formatDistance(distanceInMeters));
} catch (Exception e) {
logger.warn("计算店铺[{}]距离失败: {}", shop.getShopName(), e.getMessage());
shop.setDistance(null);
}
} else {
shop.setDistance(null);
}
serviceShops.add(shop);
}
}
// 按距离排序有距离的排在前面然后按距离升序
serviceShops.sort((s1, s2) -> {
if (s1.getDistance() == null && s2.getDistance() == null) {
return 0;
}
if (s1.getDistance() == null) {
return 1;
}
if (s2.getDistance() == null) {
return -1;
}
// 提取距离数值进行比较
try {
double d1 = Double.parseDouble(s1.getDistance().replaceAll("[^0-9.]", ""));
double d2 = Double.parseDouble(s2.getDistance().replaceAll("[^0-9.]", ""));
return Double.compare(d1, d2);
} catch (Exception e) {
return 0;
}
});
}
} else {
logger.debug("商品类目信息中未配置服务类目ID");
}
} catch (Exception e) {
logger.warn("获取服务店铺信息失败: {}", e.getMessage());
}
}
JSONObject result = new JSONObject();
result.put("goodsId", goodsId);
result.put("goodsName", goods.getGoodsName());
result.put("userLatitude", latitude);
result.put("userLongitude", longitude);
result.put("serviceShops", serviceShops);
result.put("totalCount", serviceShops.size());
return AjaxResult.success(result);
} catch (Exception e) {
logger.error("获取服务店铺列表失败: {}", e.getMessage(), e);
return AjaxResult.error("获取服务店铺列表失败: " + ExceptionUtil.getExceptionMessage(e));
}
}
@Log(title = "商品管理", businessType = BusinessType.EXPORT)
@RequiresPermissions("goods:goods:export")
@PostMapping("/export")
@ -720,155 +918,5 @@ public class GoodsController extends BaseController {
}
}
/**
* 通过配件商品ID获取对应服务类目下的所有店铺信息
*
* @return 店铺信息列表包含距离信息
*/
@PostMapping("/getShopsByGoodsId")
@ResponseBody
public AjaxResult getShopsByGoodsId(@RequestBody Map<String, Object> params) {
try {
Long goodsId = Long.valueOf(params.get("goodsId").toString());
final Double latitude = params.get("latitude") != null ? Double.valueOf(params.get("latitude").toString()) : null;
final Double longitude = params.get("longitude") != null ? Double.valueOf(params.get("longitude").toString()) : null;
// 1. 通过配件商品ID获取商品信息
Goods goods = goodsService.selectById(goodsId);
if (goods == null) {
return null;
}
// 2. 获取商品对应的服务类目ID
Long deptGoodsCategoryId = goods.getDeptGoodsCategoryId();
if (deptGoodsCategoryId == null) {
return null;
}
// 3. 通过类目ID获取当前类目下的所有店铺信息
// 先获取类目信息找到所有使用该类目的商品
DeptGoodsCategory deptGoodsCategory = deptGoodsCategoryService.get(deptGoodsCategoryId);
if (deptGoodsCategory == null) {
return null;
}
// 查询所有使用该服务类目的商品
Goods queryGoods = new Goods();
queryGoods.setDeptGoodsCategoryId(deptGoodsCategoryId);
queryGoods.setStatus(0); // 只查询上架的商品
List<Goods> goodsList = goodsService.selectGoodsList(queryGoods);
// 提取所有店铺ID去重
Set<Long> shopIds = goodsList.stream()
.filter(g -> g.getShopId() != null)
.map(Goods::getShopId)
.collect(Collectors.toSet());
if (shopIds.isEmpty()) {
return AjaxResult.success("该服务类目下暂无店铺信息", new ArrayList<>());
}
// 4. 获取所有店铺信息
List<Shop> shops = new ArrayList<>();
for (Long shopId : shopIds) {
Shop shop = shopService.getShop(shopId);
if (shop != null) {
// 5. 计算距离
if (LocationUtils.isValidCoordinate(latitude, longitude) &&
LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) {
try {
double distanceInMeters = LocationUtils.getDistanceInMeters(
latitude, longitude,
shop.getLatitude(), shop.getLongitude()
);
// 将距离信息添加到店铺对象中可以创建一个新的DTO或者使用Map
Map<String, Object> shopWithDistance = new HashMap<>();
shopWithDistance.put("shop", shop);
shopWithDistance.put("distance", LocationUtils.formatDistance(distanceInMeters));
shopWithDistance.put("distanceInMeters", distanceInMeters);
shops.add(shop);
} catch (Exception e) {
logger.warn("计算店铺[{}]距离失败: {}", shop.getShopName(), e.getMessage());
shops.add(shop);
}
} else {
shops.add(shop);
}
}
}
// 6. 按距离排序如果提供了位置信息
if (LocationUtils.isValidCoordinate(latitude, longitude)) {
shops.sort((shop1, shop2) -> {
try {
if (LocationUtils.isValidCoordinate(shop1.getLatitude(), shop1.getLongitude()) &&
LocationUtils.isValidCoordinate(shop2.getLatitude(), shop2.getLongitude())) {
double dist1 = LocationUtils.getDistanceInMeters(
latitude, longitude,
shop1.getLatitude(), shop1.getLongitude()
);
double dist2 = LocationUtils.getDistanceInMeters(
latitude, longitude,
shop2.getLatitude(), shop2.getLongitude()
);
return Double.compare(dist1, dist2);
}
} catch (Exception e) {
logger.warn("排序时计算距离失败: {}", e.getMessage());
}
return 0;
});
}
// 7. 构建返回结果包含距离信息
List<Map<String, Object>> result = new ArrayList<>();
for (Shop shop : shops) {
Map<String, Object> shopInfo = new HashMap<>();
shopInfo.put("shopId", shop.getShopId());
shopInfo.put("shopName", shop.getShopName());
shopInfo.put("imageUrl", shop.getImageUrl());
shopInfo.put("workerId", shop.getWorkerId());
shopInfo.put("provinceId", shop.getProvinceId());
shopInfo.put("provinceName", shop.getProvinceName());
shopInfo.put("cityId", shop.getCityId());
shopInfo.put("cityName", shop.getCityName());
shopInfo.put("countryId", shop.getCountryId());
shopInfo.put("countryName", shop.getCountryName());
shopInfo.put("streetId", shop.getStreetId());
shopInfo.put("streetName", shop.getStreetName());
shopInfo.put("address", shop.getAddress());
shopInfo.put("phone", shop.getPhone());
shopInfo.put("latitude", shop.getLatitude());
shopInfo.put("longitude", shop.getLongitude());
// 计算并添加距离信息
if (LocationUtils.isValidCoordinate(latitude, longitude) &&
LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) {
try {
double distanceInMeters = LocationUtils.getDistanceInMeters(
latitude, longitude,
shop.getLatitude(), shop.getLongitude()
);
shopInfo.put("distance", LocationUtils.formatDistance(distanceInMeters));
shopInfo.put("distanceInMeters", distanceInMeters);
} catch (Exception e) {
shopInfo.put("distance", null);
shopInfo.put("distanceInMeters", null);
}
} else {
shopInfo.put("distance", null);
shopInfo.put("distanceInMeters", null);
}
result.add(shopInfo);
}
return AjaxResult.success("获取成功", result);
} catch (Exception e) {
logger.error("获取店铺信息失败: {}", e.getMessage(), e);
return AjaxResult.error("获取店铺信息失败: " + e.getMessage());
}
}
}

View File

@ -17,6 +17,9 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* 百度地图逆解析
* @author clunt
@ -105,6 +108,56 @@ public class BaiduController extends BaseController {
}
}
/**
* 百度地图正向地理编码地址 -> 经纬度
* 接收JSON{provinceName, cityName, countryName, streetName, address}
*/
@PostMapping("/geocode")
@ResponseBody
public AjaxResult geocode(@RequestBody JSONObject jsonObject) {
try {
String provinceName = jsonObject.getString("provinceName");
String cityName = jsonObject.getString("cityName");
String countryName = jsonObject.getString("countryName");
String streetName = jsonObject.getString("streetName");
String detailAddress = jsonObject.getString("address");
StringBuilder full = new StringBuilder();
if (provinceName != null) { full.append(provinceName); }
if (cityName != null) { full.append(cityName); }
if (countryName != null) { full.append(countryName); }
if (streetName != null) { full.append(streetName); }
if (detailAddress != null) { full.append(detailAddress); }
String address = full.toString();
if (address == null || address.trim().isEmpty()) {
return AjaxResult.error("地址不能为空");
}
String encoded = URLEncoder.encode(address, StandardCharsets.UTF_8.name());
// 使用百度正向地理编码API
String url = "https://api.map.baidu.com/geocoding/v3/?output=json&ak="
+ baiduConfig.getAk() + "&address=" + encoded;
String result = HttpUtils.sendGet(url);
result = result.replaceAll("\n", "").replaceAll("\t", "");
JSONObject resultJson = JSONObject.parseObject(result);
if ("0".equals(resultJson.getString("status"))) {
JSONObject location = resultJson.getJSONObject("result").getJSONObject("location");
JSONObject data = new JSONObject();
data.put("longitude", location.getBigDecimal("lng"));
data.put("latitude", location.getBigDecimal("lat"));
data.put("fullAddress", address);
return AjaxResult.success(data);
} else {
return AjaxResult.error("百度地理编码失败:" + resultJson.getString("msg"));
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
return AjaxResult.error(ExceptionUtil.getExceptionMessage(e));
}
}
public static void main(String[] args) {
String result = " {\"status\":0,\"result\":{\"location\":{\"lng\":113.29950224957551,\"lat\":23.019282373814027},\"formatted_address\":\"广东省广州市番禺区安平路\",\"business\":\"大石\",\"addressComponent\":{\"country\":\"中国\",\"country_code\":0,\"country_code_iso\":\"CHN\",\"country_code_iso2\":\"CN\",\"province\":\"广东省\",\"city\":\"广州市\",\"city_level\":2,\"district\":\"番禺区\",\"town\":\"\",\"town_code\":\"\",\"distance\":\"\",\"direction\":\"\",\"adcode\":\"440113\",\"street\":\"安平路\",\"street_number\":\"\"},\"pois\":[],\"roads\":[],\"poiRegions\":[],\"sematic_description\":\"\",\"cityCode\":257}}\n";

View File

@ -4,6 +4,7 @@ import com.ghy.common.annotation.Excel;
import com.ghy.common.core.domain.BaseEntity;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
@ -64,6 +65,16 @@ public class CustomerAddress extends BaseEntity {
private Boolean needNameFlag = false;
/**
* 经度
*/
private BigDecimal longitude;
/**
* 纬度
*/
private BigDecimal latitude;
/*是否删除id*/
private Long isDelete;
}

View File

@ -10,7 +10,11 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONObject;
import com.ghy.common.utils.http.HttpUtils;
@Slf4j
@Service
@ -21,6 +25,7 @@ public class CustomerAddressServiceImpl implements CustomerAddressService {
@Resource
private ISysAreaService iSysAreaService;
private static final Logger logger = LoggerFactory.getLogger(CustomerAddressServiceImpl.class);
@Override
@ -67,11 +72,15 @@ public class CustomerAddressServiceImpl implements CustomerAddressService {
@Override
public int insertCustomerAddress(CustomerAddress customerAddress) {
// 在插入前自动获取经纬度
setGeocodingForAddress(customerAddress);
return customerAddressMapper.insertCustomerAddress(customerAddress);
}
@Override
public int updateCustomerAddress(CustomerAddress customerAddress) {
// 在更新前自动获取经纬度
setGeocodingForAddress(customerAddress);
return customerAddressMapper.updateCustomerAddress(customerAddress);
}
@ -79,4 +88,89 @@ public class CustomerAddressServiceImpl implements CustomerAddressService {
public CustomerAddress selectByCustomerAndAddress(Long customerId, Long provinceId, Long cityId, Long countryId, String address) {
return customerAddressMapper.selectByCustomerAndAddress(customerId, provinceId, cityId, countryId, address);
}
/**
* 为地址设置经纬度信息
*
* @param customerAddress 客户地址对象
*/
private void setGeocodingForAddress(CustomerAddress customerAddress) {
try {
// 如果已经有经纬度信息则跳过
if (customerAddress.getLongitude() != null && customerAddress.getLatitude() != null) {
logger.info("地址已有经纬度信息,跳过获取");
return;
}
// 获取地址名称信息
String provinceName = null;
String cityName = null;
String countryName = null;
String streetName = null;
if (customerAddress.getProvinceId() != null) {
try {
provinceName = iSysAreaService.selectById(customerAddress.getProvinceId()).getAreaName();
} catch (Exception e) {
logger.warn("获取省份名称失败: {}", e.getMessage());
}
}
if (customerAddress.getCityId() != null) {
try {
cityName = iSysAreaService.selectById(customerAddress.getCityId()).getAreaName();
} catch (Exception e) {
logger.warn("获取城市名称失败: {}", e.getMessage());
}
}
if (customerAddress.getCountryId() != null) {
try {
countryName = iSysAreaService.selectById(customerAddress.getCountryId()).getAreaName();
} catch (Exception e) {
logger.warn("获取区县名称失败: {}", e.getMessage());
}
}
if (customerAddress.getStreetId() != null) {
try {
streetName = iSysAreaService.selectById(customerAddress.getStreetId()).getAreaName();
} catch (Exception e) {
logger.warn("获取街道名称失败: {}", e.getMessage());
}
}
// 调用百度地理编码接口获取经纬度
try {
JSONObject requestBody = new JSONObject();
requestBody.put("provinceName", provinceName);
requestBody.put("cityName", cityName);
requestBody.put("countryName", countryName);
requestBody.put("streetName", streetName);
requestBody.put("address", customerAddress.getAddress());
// 调用百度地理编码接口
String url = "http://localhost:19001/tool/baidu/geocode";
String result = HttpUtils.sendPost(url, requestBody.toJSONString());
JSONObject responseJson = JSONObject.parseObject(result);
if (responseJson.getInteger("code") == 200) {
JSONObject data = responseJson.getJSONObject("data");
BigDecimal longitude = data.getBigDecimal("longitude");
BigDecimal latitude = data.getBigDecimal("latitude");
customerAddress.setLongitude(longitude);
customerAddress.setLatitude(latitude);
logger.info("成功获取地址经纬度: 经度={}, 纬度={}", longitude, latitude);
} else {
logger.warn("百度地理编码接口调用失败: {}", responseJson.getString("msg"));
}
} catch (Exception e) {
logger.error("调用百度地理编码接口异常: {}", e.getMessage(), e);
}
} catch (Exception e) {
logger.error("获取地址经纬度失败: {}", e.getMessage(), e);
}
}
}

View File

@ -23,11 +23,13 @@
<result property="cityName" column="city_name" />
<result property="countryName" column="country_name" />
<result property="streetName" column="street_name" />
<result property="longitude" column="longitude" />
<result property="latitude" column="latitude" />
</resultMap>
<sql id="selectCustomerAddress">
SELECT customer_address_id, customer_id, name, phone, province_id, city_id, country_id, street_id, status,
address, create_by, create_time, remark, is_default
address, create_by, create_time, remark, is_default, longitude, latitude
FROM customer_address
</sql>
@ -114,6 +116,8 @@
<if test="isDefault != null">is_default,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="remark != null and remark != ''">remark,</if>
<if test="longitude != null">longitude,</if>
<if test="latitude != null">latitude,</if>
deleted,
create_time
)values(
@ -129,6 +133,8 @@
<if test="isDefault != null">#{isDefault},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="remark != null and remark != ''">#{remark},</if>
<if test="longitude != null">#{longitude},</if>
<if test="latitude != null">#{latitude},</if>
0,
sysdate()
)
@ -147,6 +153,8 @@
<if test="isDefault != null">is_default = #{isDefault},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="longitude != null">longitude = #{longitude},</if>
<if test="latitude != null">latitude = #{latitude},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
update_time = sysdate()
</set>