diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/ShopController.java b/ghy-admin/src/main/java/com/ghy/web/controller/ShopController.java index 842c294d..d2d99191 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/ShopController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/ShopController.java @@ -11,6 +11,9 @@ import java.util.List; import java.util.HashMap; import java.util.Map; import org.springframework.web.client.RestTemplate; +import com.ghy.common.utils.LocationUtils; +import java.util.stream.Collectors; +import java.util.Comparator; /** * 店铺管理接口 @@ -125,23 +128,23 @@ public class ShopController extends BaseController { return AjaxResult.success(shops); } - /** - * 通过地址获取经纬度坐标 - */ - @PostMapping("/getCoordinates") - public AjaxResult getCoordinatesByAddress(@RequestBody Shop shop) { - try { - Map coordinates = getCoordinatesByAddress(shop); - if (coordinates != null) { - return AjaxResult.success("获取坐标成功", coordinates); - } else { - return AjaxResult.error("无法获取该地址的坐标信息"); - } - } catch (Exception e) { - logger.error("获取坐标失败: {}", e.getMessage(), e); - return AjaxResult.error("获取坐标失败: " + e.getMessage()); - } - } +// /** +// * 通过地址获取经纬度坐标 +// */ +// @PostMapping("/getCoordinates") +// public AjaxResult getCoordinatesByAddress(@RequestBody Shop shop) { +// try { +// Map coordinates = getCoordinatesByAddress(shop); +// if (coordinates != null) { +// return AjaxResult.success("获取坐标成功", coordinates); +// } else { +// return AjaxResult.error("无法获取该地址的坐标信息"); +// } +// } catch (Exception e) { +// logger.error("获取坐标失败: {}", e.getMessage(), e); +// return AjaxResult.error("获取坐标失败: " + e.getMessage()); +// } +// } /** * 通过经纬度逆解析获取地址信息并保存到店铺 @@ -329,4 +332,160 @@ public class ShopController extends BaseController { return AjaxResult.error("查询失败: " + e.getMessage()); } } + + /** + * 查询附近的店铺 + */ + @PostMapping("/nearby") + public AjaxResult getNearbyShops(@RequestBody Map params) { + try { + Double latitude = Double.valueOf(params.get("latitude").toString()); + Double longitude = Double.valueOf(params.get("longitude").toString()); + Double radiusKm = params.containsKey("radiusKm") ? + Double.valueOf(params.get("radiusKm").toString()) : 5.0; // 默认5公里 + + // 验证坐标有效性 + if (!LocationUtils.isValidCoordinate(latitude, longitude)) { + return AjaxResult.error("无效的坐标参数"); + } + + // 获取所有店铺 + List allShops = shopService.listShops(); + + // 筛选出有坐标的店铺并计算距离 + List> nearbyShops = allShops.stream() + .filter(shop -> LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) + .map(shop -> { + double distance = LocationUtils.getDistanceInKilometers( + latitude, longitude, shop.getLatitude(), shop.getLongitude()); + + Map shopWithDistance = new HashMap<>(); + shopWithDistance.put("shop", shop); + shopWithDistance.put("distance", distance); + shopWithDistance.put("distanceText", LocationUtils.formatDistance(distance * 1000)); + + // 计算方向 + double bearing = LocationUtils.getBearing(latitude, longitude, + shop.getLatitude(), shop.getLongitude()); + shopWithDistance.put("direction", LocationUtils.getDirectionDescription(bearing)); + shopWithDistance.put("bearing", bearing); + + return shopWithDistance; + }) + .filter(shopMap -> (Double) shopMap.get("distance") <= radiusKm) + .sorted(Comparator.comparingDouble(shopMap -> (Double) shopMap.get("distance"))) + .collect(Collectors.toList()); + + Map result = new HashMap<>(); + result.put("centerLatitude", latitude); + result.put("centerLongitude", longitude); + result.put("radiusKm", radiusKm); + result.put("totalCount", nearbyShops.size()); + result.put("shops", nearbyShops); + + return AjaxResult.success("查询成功", result); + + } catch (Exception e) { + logger.error("查询附近店铺失败: {}", e.getMessage(), e); + return AjaxResult.error("查询失败: " + e.getMessage()); + } + } + + /** + * 计算两个店铺之间的距离 + */ + @PostMapping("/distance") + public AjaxResult getShopsDistance(@RequestBody Map params) { + try { + Long shopId1 = Long.valueOf(params.get("shopId1").toString()); + Long shopId2 = Long.valueOf(params.get("shopId2").toString()); + + Shop shop1 = shopService.getShop(shopId1); + Shop shop2 = shopService.getShop(shopId2); + + if (shop1 == null || shop2 == null) { + return AjaxResult.error("店铺不存在"); + } + + if (!LocationUtils.isValidCoordinate(shop1.getLatitude(), shop1.getLongitude()) || + !LocationUtils.isValidCoordinate(shop2.getLatitude(), shop2.getLongitude())) { + return AjaxResult.error("店铺坐标信息不完整"); + } + + double distanceKm = LocationUtils.getDistanceInKilometers( + shop1.getLatitude(), shop1.getLongitude(), + shop2.getLatitude(), shop2.getLongitude()); + + double bearing = LocationUtils.getBearing( + shop1.getLatitude(), shop1.getLongitude(), + shop2.getLatitude(), shop2.getLongitude()); + + Map result = new HashMap<>(); + result.put("shop1", shop1); + result.put("shop2", shop2); + result.put("distanceKm", distanceKm); + result.put("distanceText", LocationUtils.formatDistance(distanceKm * 1000)); + result.put("direction", LocationUtils.getDirectionDescription(bearing)); + result.put("bearing", bearing); + + return AjaxResult.success("计算成功", result); + + } catch (Exception e) { + logger.error("计算店铺距离失败: {}", e.getMessage(), e); + return AjaxResult.error("计算失败: " + e.getMessage()); + } + } + +// /** +// * 获取店铺覆盖范围内的其他店铺 +// */ +// @PostMapping("/coverage") +// public AjaxResult getShopsCoverage(@RequestBody Map params) { +// try { +// Long shopId = Long.valueOf(params.get("shopId").toString()); +// Double radiusKm = params.containsKey("radiusKm") ? +// Double.valueOf(params.get("radiusKm").toString()) : 10.0; // 默认10公里 +// +// Shop centerShop = shopService.getShop(shopId); +// if (centerShop == null) { +// return AjaxResult.error("店铺不存在"); +// } +// +// if (!LocationUtils.isValidCoordinate(centerShop.getLatitude(), centerShop.getLongitude())) { +// return AjaxResult.error("中心店铺坐标信息不完整"); +// } +// +// // 获取附近的店铺 +// Map nearbyParams = new HashMap<>(); +// nearbyParams.put("latitude", centerShop.getLatitude()); +// nearbyParams.put("longitude", centerShop.getLongitude()); +// nearbyParams.put("radiusKm", radiusKm); +// +// AjaxResult nearbyResult = getNearbyShops(nearbyParams); +// if (nearbyResult.isSuccess()) { +// Map data = (Map) nearbyResult.get("data"); +// List> shops = (List>) data.get("shops"); +// +// // 排除自己 +// shops = shops.stream() +// .filter(shopMap -> { +// Shop shop = (Shop) shopMap.get("shop"); +// return !shop.getShopId().equals(shopId); +// }) +// .collect(Collectors.toList()); +// +// data.put("centerShop", centerShop); +// data.put("shops", shops); +// data.put("totalCount", shops.size()); +// +// return AjaxResult.success("查询成功", data); +// } else { +// return nearbyResult; +// } +// +// } catch (Exception e) { +// logger.error("查询店铺覆盖范围失败: {}", e.getMessage(), e); +// return AjaxResult.error("查询失败: " + e.getMessage()); +// } +// } } \ No newline at end of file diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/goods/GoodsController.java b/ghy-admin/src/main/java/com/ghy/web/controller/goods/GoodsController.java index f45d6d48..f5d1d241 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/goods/GoodsController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/goods/GoodsController.java @@ -10,8 +10,11 @@ import com.ghy.common.utils.ShiroUtils; 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.goods.domain.*; import com.ghy.goods.service.*; +import com.ghy.shop.domain.Shop; +import com.ghy.shop.service.ShopService; import com.ghy.system.domain.SysArea; import com.ghy.system.service.ISysAreaService; import io.swagger.annotations.ApiModel; @@ -51,6 +54,8 @@ public class GoodsController extends BaseController { private IInsuranceManagerService insuranceManagerService; @Resource private IDeptCategoryInsuranceRelationService deptCategoryInsuranceRelationService; + @Resource + private ShopService shopService; @RequiresPermissions("goods:goods:view") @GetMapping() @@ -168,6 +173,11 @@ public class GoodsController extends BaseController { if (type==null){ goods.setType(1); } + + // 获取用户当前位置 + Double userLatitude = goods.getLatitude(); + Double userLongitude = goods.getLongitude(); + // 判断类目id是否为第三级,不是的话需要找到所有符合条件的第三级类目id作为新的条件 if (goods.getDeptGoodsCategoryId() != null) { logger.info("入参:" + goods.getDeptGoodsCategoryId()); @@ -210,6 +220,10 @@ public class GoodsController extends BaseController { List list = goodsService.selectGoodsList(goods); logger.info("传入的类目id汇总{},获取到的所有商品{}",goods.getDeptGoodsCategoryIds(),list); + + // 用于缓存店铺信息,避免重复查询 + Map shopCache = new HashMap<>(); + list.forEach(one -> { // 补全商品服务区域 List goodsAreas = goodsAreaService.selectByGoodsId(one.getGoodsId()); @@ -227,9 +241,88 @@ public class GoodsController extends BaseController { one.setParGoodsCategoryId(parGoodsCategory.getGoodsCategoryId()); one.setParGoodsCategoryName(parGoodsCategory.getGoodsCategoryName()); } + + // 计算距离逻辑 + if (LocationUtils.isValidCoordinate(userLatitude, userLongitude) && one.getShopId() != null) { + try { + // 从缓存获取店铺信息,避免重复查询 + Shop shop = shopCache.computeIfAbsent(one.getShopId(), + shopId -> shopService.getShop(shopId)); + + if (shop != null && LocationUtils.isValidCoordinate(shop.getLatitude(), shop.getLongitude())) { + // 计算距离(米) + double distanceInMeters = LocationUtils.getDistanceInMeters( + userLatitude, userLongitude, + shop.getLatitude(), shop.getLongitude() + ); + + // 格式化距离并设置到商品对象 + one.setDistance(LocationUtils.formatDistance(distanceInMeters)); + + logger.debug("商品[{}]距离用户: {}", one.getGoodsName(), one.getDistance()); + } else { + // 店铺坐标不完整 + one.setDistance(null); + } + } catch (Exception e) { + logger.warn("计算商品[{}]距离失败: {}", one.getGoodsName(), e.getMessage()); + // 计算异常时设为null + one.setDistance(null); + } + } else { + // 用户未提供位置或商品无店铺信息 + one.setDistance(null); + } }); + + // 如果用户提供了位置信息,按距离排序(距离为null的排在最后) + if (LocationUtils.isValidCoordinate(userLatitude, userLongitude)) { + list.sort((goods1, goods2) -> { + String distance1 = goods1.getDistance(); + String distance2 = goods2.getDistance(); + + // 距离为null的排在最后 + if (distance1 == null && distance2 == null) return 0; + if (distance1 == null) return 1; + if (distance2 == null) return -1; + + // 解析距离数值进行比较 + try { + double dist1 = parseDistanceToMeters(distance1); + double dist2 = parseDistanceToMeters(distance2); + return Double.compare(dist1, dist2); + } catch (Exception e) { + // 解析失败时保持原顺序 + return 0; + } + }); + + logger.info("已按距离排序,用户位置: 纬度={}, 经度={}", userLatitude, userLongitude); + } + return getDataTable(list); } + + /** + * 解析距离字符串为米数(用于排序) + */ + private double parseDistanceToMeters(String distanceText) { + if (distanceText == null || distanceText.trim().isEmpty()) { + return Double.MAX_VALUE; + } + + try { + if (distanceText.endsWith("米")) { + return Double.parseDouble(distanceText.replace("米", "")); + } else if (distanceText.endsWith("公里")) { + return Double.parseDouble(distanceText.replace("公里", "")) * 1000; + } + } catch (NumberFormatException e) { + logger.warn("无法解析距离字符串: {}", distanceText); + } + + return Double.MAX_VALUE; + } @PostMapping("/hot/list") @ResponseBody diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java index ccc3270a..bf4a90b8 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java @@ -1760,25 +1760,94 @@ public class OrderMasterController extends BaseController { * */ @PostMapping("/console/cancel") @ResponseBody + @Transactional(rollbackFor = Exception.class) public AjaxResult consoleCancel(Long id) { if(id == null){ return AjaxResult.error("订单编码id不能为空!"); } + // 判断该主单状态 OrderMaster orderMaster = orderMasterService.selectById(id); if(!orderMaster.getOrderStatus().equals(OrderStatus.PLAIN.code()) && !orderMaster.getOrderStatus().equals(OrderStatus.RECEIVE.code())){ return AjaxResult.error("该订单处于无法退单状态!"); } + // 含有子单 List orderDetails = orderDetailService.selectByOrderMasterId(id); if(CollectionUtils.isNotEmpty(orderDetails)){ return AjaxResult.error("该订单已经派发过子单,无法退单状态!"); } - // 清空id - orderMasterService.removeWorker(orderMaster.getId()); - // 更新状态待接单 - orderMasterService.updateStatus(orderMaster.getId(), OrderStatus.RECEIVE.code()); - return AjaxResult.success(); + + try { + // 判断是否为服务订单且需要退还服务金额 + if (isServiceOrder(orderMaster) && + orderMaster.getGoodsOrderMasterId() != null && + orderMaster.getServerGoodsMoney() != null && + orderMaster.getServerGoodsMoney().compareTo(BigDecimal.ZERO) > 0) { + + logger.info("检测到服务订单退单,开始退还服务金额:订单[{}],金额[{}],目标商品订单[{}]", + orderMaster.getId(), orderMaster.getServerGoodsMoney(), orderMaster.getGoodsOrderMasterId()); + + // 退还服务金额到商品订单 + refundServerMoneyToGoodsOrder(orderMaster.getGoodsOrderMasterId(), orderMaster.getServerGoodsMoney()); + } + + // 清空id + orderMasterService.removeWorker(orderMaster.getId()); + // 更新状态待接单 + orderMasterService.updateStatus(orderMaster.getId(), OrderStatus.RECEIVE.code()); + + return AjaxResult.success("退单成功"); + + } catch (Exception e) { + logger.error("退单失败:订单ID={}, 错误信息={}", id, e.getMessage(), e); + return AjaxResult.error("退单失败:" + e.getMessage()); + } + } + + /** + * 判断是否为服务订单 + */ + private boolean isServiceOrder(OrderMaster orderMaster) { + return orderMaster.getOrderType() != null && orderMaster.getOrderType() == 0; + } + + /** + * 服务订单退单时,退还服务金额给商品订单 + * + * @param goodsOrderMasterId 商品订单主单ID + * @param serverGoodsMoney 需要退还的服务金额 + */ + private void refundServerMoneyToGoodsOrder(Long goodsOrderMasterId, BigDecimal serverGoodsMoney) { + try { + // 1. 查询商品订单 + OrderMaster goodsOrder = orderMasterService.selectById(goodsOrderMasterId); + if (goodsOrder == null) { + logger.error("商品订单不存在,无法退还服务金额,goodsOrderMasterId={}", goodsOrderMasterId); + throw new RuntimeException("商品订单不存在"); + } + + // 2. 查询商品订单的财务记录 + FinancialMaster goodsFinancial = financialMasterService.selectByOrderMasterId(goodsOrderMasterId); + if (goodsFinancial == null) { + logger.error("商品订单财务记录不存在,无法退还服务金额,goodsOrderMasterId={}", goodsOrderMasterId); + throw new RuntimeException("商品订单财务记录不存在"); + } + + // 3. 更新商品订单的可用金额(增加退还的服务金额) + BigDecimal currentPayMoney = goodsFinancial.getPayMoney(); + BigDecimal newPayMoney = currentPayMoney.add(serverGoodsMoney); + + goodsFinancial.setPayMoney(newPayMoney); + financialMasterService.updateFinancialMaster(goodsFinancial); + + logger.info("服务订单退单成功,已退还{}元服务金额到商品订单[{}]", serverGoodsMoney, goodsOrderMasterId); + + } catch (Exception e) { + logger.error("退还服务金额失败:goodsOrderMasterId={}, serverGoodsMoney={}, error={}", + goodsOrderMasterId, serverGoodsMoney, e.getMessage(), e); + throw new RuntimeException("退还服务金额失败:" + e.getMessage()); + } } /** diff --git a/ghy-goods/src/main/java/com/ghy/goods/domain/Goods.java b/ghy-goods/src/main/java/com/ghy/goods/domain/Goods.java index 7ec83810..b8ea0395 100644 --- a/ghy-goods/src/main/java/com/ghy/goods/domain/Goods.java +++ b/ghy-goods/src/main/java/com/ghy/goods/domain/Goods.java @@ -100,4 +100,14 @@ public class Goods extends BaseEntity { private Integer type; private List insuranceManagers; + + private String distance; + + /** 纬度 */ + private Double latitude; + + /** 经度 */ + private Double longitude; + + }