Просмотр исходного кода

开发商户端订单相关接口

wangzhijun 1 неделя назад
Родитель
Сommit
c92a67d93e

+ 84 - 49
nightFragrance-common/src/main/java/com/ylx/common/utils/DistanceUtil.java

@@ -1,71 +1,90 @@
 package com.ylx.common.utils;
 
 import cn.hutool.core.util.ObjectUtil;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 
 /**
- * 计算经纬度之间的直线距离
+ * Haversine公式计算两点经纬度球面直线距离工具类
+ * 所有坐标入参统一使用 BigDecimal,保证数据库高精度存储兼容
  */
-public class DistanceUtil {
+public final class DistanceUtil {
 
-    private static final double EARTH_RADIUS_M = 6_371_000; // 地球半径,单位米
-    private static final double TO_RADIANS = Math.PI / 180.0;
+    // 地球平均半径,单位:米
+    private static final double EARTH_RADIUS_M = 6_371_000D;
+    // 角度转弧度系数
+    private static final double TO_RADIANS = Math.PI / 180.0D;
+    // 米转公里换算值
+    private static final BigDecimal METER_TO_KM = new BigDecimal("1000");
+    // 经纬度极值常量
+    private static final BigDecimal MAX_LAT = new BigDecimal("90");
+    private static final BigDecimal MAX_LON = new BigDecimal("180");
+    private static final BigDecimal ZERO = BigDecimal.ZERO;
 
+    // 私有构造,禁止实例化工具类
+    private DistanceUtil() {
+        throw new AssertionError("工具类不可实例化");
+    }
+
+    // ===================== 对外业务方法 =====================
     /**
-     * 格式化两点间距离
-     *
+     * 获取距离(单位:米,四舍五入整数字符串)
      * @param userLat 用户纬度
      * @param userLon 用户经度
      * @param shopLat 门店纬度
      * @param shopLon 门店经度
-     * @return 距离(米),若坐标无效则返回 "未知"
+     * @return 有效距离返回米数字,坐标为空/0/超出范围 返回 "未知"
      */
-    public static String formatDistance(Double userLat, Double userLon, Double shopLat, Double shopLon) {
-        // 任意坐标空/0 → 未知
-        if (ObjectUtil.hasNull(userLat, userLon, shopLat, shopLon)
-                || userLat == 0 || userLon == 0 || shopLat == 0 || shopLon == 0) {
+    public static String formatDistance(BigDecimal userLat, BigDecimal userLon,
+                                        BigDecimal shopLat, BigDecimal shopLon) {
+        if (!isAllCoordinateValid(userLat, userLon, shopLat, shopLon)) {
             return "未知";
         }
-
-        // 验证坐标范围(可选,增加健壮性)
-        if (!isValidCoordinate(userLat, userLon) || !isValidCoordinate(shopLat, shopLon)) {
-            return "未知"; // 或抛出异常
-        }
-
-        double meter = getDistance(userLat, userLon, shopLat, shopLon);
+        double meter = getDistance(userLat.doubleValue(), userLon.doubleValue(),
+                shopLat.doubleValue(), shopLon.doubleValue());
         return String.valueOf(Math.round(meter));
     }
 
     /**
-     * 格式化两点间距离(单位:公里)
-     *
+     * 获取距离(单位:公里,保留2位小数)
      * @param userLat 用户纬度
      * @param userLon 用户经度
      * @param shopLat 门店纬度
      * @param shopLon 门店经度
-     * @return 距离(公里),若坐标无效则返回 "未知"
+     * @return 公里字符串,坐标非法返回 "未知"
      */
-    public static String formatDistanceInKilometers(Double userLat, Double userLon, Double shopLat, Double shopLon) {
-        if (ObjectUtil.hasNull(userLat, userLon, shopLat, shopLon)
-                || userLat == 0 || userLon == 0 || shopLat == 0 || shopLon == 0) {
-            return "未知";
+    public static BigDecimal formatDistanceInKilometers(BigDecimal userLat, BigDecimal userLon,
+                                                    BigDecimal shopLat, BigDecimal shopLon) {
+        if (!isAllCoordinateValid(userLat, userLon, shopLat, shopLon)) {
+            return null;
         }
-        if (!isValidCoordinate(userLat, userLon) || !isValidCoordinate(shopLat, shopLon)) {
+        double meterVal = getDistance(userLat.doubleValue(), userLon.doubleValue(),
+                shopLat.doubleValue(), shopLon.doubleValue());
+        BigDecimal meter = new BigDecimal(meterVal);
+        return meter.divide(METER_TO_KM, 2, RoundingMode.HALF_UP);
+    }
+
+    /**
+     * 前端友好展示距离
+     * 小于1000米:xx米;大于等于1000米:xx.xx公里;坐标异常:未知
+     */
+    public static String formatDisplay(BigDecimal userLat, BigDecimal userLon,
+                                       BigDecimal shopLat, BigDecimal shopLon) {
+        String meterStr = formatDistance(userLat, userLon, shopLat, shopLon);
+        if ("未知".equals(meterStr)) {
             return "未知";
         }
-        double meter = getDistance(userLat, userLon, shopLat, shopLon);
-        double kilometer = meter / 1000.0;
-        // 四舍五入到两位小数
-        return String.format("%.2f", kilometer);
+        long meter = Long.parseLong(meterStr);
+        if (meter < METER_TO_KM.longValue()) {
+            return meter + "米";
+        }
+        BigDecimal meterNum = new BigDecimal(meter);
+        BigDecimal km = meterNum.divide(METER_TO_KM, 2, RoundingMode.HALF_UP);
+        return km + "公里";
     }
 
     /**
-     * 获取两点间球面距离(米)
-     *
-     * @param lat1 纬度1
-     * @param lon1 经度1
-     * @param lat2 纬度2
-     * @param lon2 经度2
-     * @return 距离(米)
+     * 底层计算:返回两点球面距离(单位米)
      */
     public static double getDistance(double lat1, double lon1, double lat2, double lon2) {
         double lat1Rad = lat1 * TO_RADIANS;
@@ -73,27 +92,43 @@ public class DistanceUtil {
         double lat2Rad = lat2 * TO_RADIANS;
         double lon2Rad = lon2 * TO_RADIANS;
 
-        // 使用 Haversine 公式(更精确,避免 acos 在极近距离下的精度问题)
         double deltaLat = lat2Rad - lat1Rad;
         double deltaLon = lon2Rad - lon1Rad;
 
-        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
-                Math.cos(lat1Rad) * Math.cos(lat2Rad) *
-                        Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
+        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
+                + Math.cos(lat1Rad) * Math.cos(lat2Rad)
+                * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
 
         double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
-
         return EARTH_RADIUS_M * c;
     }
 
+    // ===================== 坐标校验私有方法 =====================
+    /**
+     * 批量校验四个坐标:非空、不等于0、经纬度范围合法
+     */
+    private static boolean isAllCoordinateValid(BigDecimal lat1, BigDecimal lon1,
+                                                BigDecimal lat2, BigDecimal lon2) {
+        // 任意坐标为null
+        if (ObjectUtil.hasNull(lat1, lon1, lat2, lon2)) {
+            return false;
+        }
+        // 任意坐标数值等于0
+        if (lat1.compareTo(ZERO) == 0
+                || lon1.compareTo(ZERO) == 0
+                || lat2.compareTo(ZERO) == 0
+                || lon2.compareTo(ZERO) == 0) {
+            return false;
+        }
+        // 两组坐标分别校验范围
+        return isSingleCoordinateValid(lat1, lon1) && isSingleCoordinateValid(lat2, lon2);
+    }
+
     /**
-     * 验证坐标是否在有效范围内
-     *
-     * @param lat 纬度
-     * @param lon 经度
-     * @return 是否有效
+     * 校验单组经纬度范围:纬度[-90,90] 经度[-180,180]
      */
-    private static boolean isValidCoordinate(double lat, double lon) {
-        return Math.abs(lat) <= 90.0 && Math.abs(lon) <= 180.0;
+    private static boolean isSingleCoordinateValid(BigDecimal lat, BigDecimal lon) {
+        return lat.abs().compareTo(MAX_LAT) <= 0
+                && lon.abs().compareTo(MAX_LON) <= 0;
     }
 }

+ 3 - 2
nightFragrance-massage/src/main/java/com/ylx/fareSetting/domian/dto/FareCalculateDTO.java

@@ -7,6 +7,7 @@ import lombok.Data;
 
 import javax.validation.constraints.NotNull;
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 @Data
@@ -34,10 +35,10 @@ public class FareCalculateDTO implements Serializable {
 
     @ApiModelProperty("用户下单经度")
     @NotNull(message = "用户下单经度不能为空")
-    private Double longitude;
+    private BigDecimal longitude;
 
     @ApiModelProperty("用户下单纬度")
     @NotNull(message = "用户下单纬度不能为空")
-    private Double latitude;
+    private BigDecimal latitude;
 
 }

+ 14 - 20
nightFragrance-massage/src/main/java/com/ylx/fareSetting/service/impl/MaProjectFareSettingServiceImpl.java

@@ -56,20 +56,10 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
         }
 
         // 2. 计算直线距离(公里)
-        String distanceStr = DistanceUtil.formatDistanceInKilometers(
-                dto.getLatitude(),dto.getLongitude(),
-                address.getLatitude(),address.getLongitude()
+        BigDecimal straightLineBigDecimal = DistanceUtil.formatDistanceInKilometers(
+                dto.getLatitude(), dto.getLongitude(),
+                address.getLatitude(), address.getLongitude()
         );
-        double straightLineKm;
-        if ("未知".equals(distanceStr)) {
-            throw new ServiceException("无法获取客户地址信息");
-        }
-        try {
-            straightLineKm = Double.parseDouble(distanceStr);
-        } catch (NumberFormatException e) {
-            log.error("距离字符串解析失败: {}", distanceStr, e);
-            throw new ServiceException("距离数据异常");
-        }
 
         // 3.根据时间段判断是否白天时间段
         LocalDateTime appointmentStartTime = dto.getAppointmentStartTime();
@@ -79,13 +69,15 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
         BigDecimal merchantFreeKm = getMerchantFreeKm(dto.getMerchantId(), dto.getProjectId(), isDay);
 
         // 5. 计算【打车距离】(即计费里程)
-        BigDecimal straightLineBigDecimal = new BigDecimal(straightLineKm).setScale(6, RoundingMode.HALF_UP);
+        if (ObjectUtil.isNull(straightLineBigDecimal)) {
+            throw new ServiceException("距离未知");
+        }
         BigDecimal effectiveDistance = straightLineBigDecimal.subtract(merchantFreeKm);
         if (effectiveDistance.compareTo(BigDecimal.ZERO) < 0) {
             effectiveDistance = BigDecimal.ZERO;
         }
         log.info("直线距离 {} km, 商户免车费距离 {} km -> 打车距离 {} km",
-                straightLineKm, merchantFreeKm, effectiveDistance);
+                straightLineBigDecimal, merchantFreeKm, effectiveDistance);
 
         // 6. 获取城市车费规则(用于最终计费)
         TFareSettingVo cityFare = fareSettingService.getFareSetting(appointmentStartTime, dto.getCityCode());
@@ -194,6 +186,7 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
             return targetTime.equals(startTime);
         }
     }
+
     /**
      * 保存免车费设置信息
      *
@@ -202,7 +195,7 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
      * @return
      */
     @Override
-    public void saveOrUpdateFee(DriverFeeDTO dto){
+    public void saveOrUpdateFee(DriverFeeDTO dto) {
         // 1. 基础校验:检查是否有空值 (对应UI中的“判断必填项是否为空”)
         validateInput(dto);
 
@@ -210,7 +203,7 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
             // --- 模式一:统一设置 ---
             // 逻辑:删除该用户所有具体的项目配置,只保留一条 categoryId=null 的记录
             baseMapper.delete(new LambdaQueryWrapper<MaProjectFareSetting>()
-                                               .eq(MaProjectFareSetting::getMerchantId, dto.getMerchantId()));
+                    .eq(MaProjectFareSetting::getMerchantId, dto.getMerchantId()));
 
             MaProjectFareSetting config = new MaProjectFareSetting();
             config.setMerchantId(dto.getMerchantId());
@@ -223,7 +216,7 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
             // --- 模式二:按项目设置 ---
             // 逻辑:先删除旧数据,再批量插入新数据
             baseMapper.delete(new LambdaQueryWrapper<MaProjectFareSetting>()
-                                               .eq(MaProjectFareSetting::getMerchantId, dto.getMerchantId()));
+                    .eq(MaProjectFareSetting::getMerchantId, dto.getMerchantId()));
 
             List<MaProjectFareSetting> list = new ArrayList<>();
             for (DriverFeeDTO.CategoryFeeItem item : dto.getCategoryConfigs()) {
@@ -243,14 +236,15 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
         }
 
     }
+
     /**
      * 校验输入是否合法 (对应UI中的 Toast 提示逻辑)
      */
     private void validateInput(DriverFeeDTO dto) {
         if (dto.getMode() == 1) {
             if (dto.getUnifiedConfig() == null ||
-                        dto.getUnifiedConfig().getDayFreeKm() == null ||
-                        dto.getUnifiedConfig().getNightFreeKm() == null) {
+                    dto.getUnifiedConfig().getDayFreeKm() == null ||
+                    dto.getUnifiedConfig().getNightFreeKm() == null) {
                 throw new RuntimeException("请输入完整的统一设置里程");
             }
         } else if (dto.getMode() == 2) {

+ 2 - 2
nightFragrance-massage/src/main/java/com/ylx/massage/domain/TAddress.java

@@ -67,10 +67,10 @@ public class TAddress extends Model<TAddress> {
     private String atlasAdd;
     //经度
     @ApiModelProperty("经度")
-    private Double longitude;
+    private BigDecimal longitude;
     //纬度
     @ApiModelProperty("纬度")
-    private Double latitude;
+    private BigDecimal latitude;
     //地址
     @ApiModelProperty("地址")
     private String address;

+ 2 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/TGeoFenceService.java

@@ -51,4 +51,6 @@ public interface TGeoFenceService extends IService<TGeoFence> {
      * @return 是否删除成功
      */
     Boolean deleteGeoFence(TGeoFence geoFence);
+
+    List<TGeoFence> selectValidFences(String cityCode);
 }

+ 1 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/MaTechnicianServiceImpl.java

@@ -1273,7 +1273,7 @@ public class MaTechnicianServiceImpl extends ServiceImpl<MaTechnicianMapper, MaT
         }
 
         // 3. 计算当前用户距离商户距离
-        String distanceStr = DistanceUtil.formatDistanceInKilometers(
+        BigDecimal distanceStr = DistanceUtil.formatDistanceInKilometers(
                 dto.getLatitude(), dto.getLongitude(),
                 address.getLatitude(), address.getLongitude()
         );

+ 10 - 1
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TGeoFenceServiceImpl.java

@@ -107,6 +107,14 @@ public class TGeoFenceServiceImpl extends ServiceImpl<TGeoFenceMapper, TGeoFence
         return true;
     }
 
+    @Override
+    public List<TGeoFence> selectValidFences(String cityCode) {
+        LambdaQueryWrapper<TGeoFence> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StringUtils.isNotBlank(cityCode), TGeoFence::getCityCode, cityCode)
+                .eq(TGeoFence::getIsDelete, NOT_DELETE);
+        return this.list(queryWrapper);
+    }
+
     private void validateUpdateParam(TGeoFence geoFence) {
         if (geoFence == null) {
             throw new ServiceException("参数不能为空");
@@ -158,6 +166,7 @@ public class TGeoFenceServiceImpl extends ServiceImpl<TGeoFenceMapper, TGeoFence
 
     /**
      * 构建查询条件。
+     *
      * @param geoFence
      * @return LambdaQueryWrapper<TGeoFence>
      */
@@ -175,7 +184,7 @@ public class TGeoFenceServiceImpl extends ServiceImpl<TGeoFenceMapper, TGeoFence
      * 验证地理坐标栏中心点坐标是否有效。
      *
      * @param longitude 经度
-     * @param latitude 纬度
+     * @param latitude  纬度
      */
     private void validateCoordinate(BigDecimal longitude, BigDecimal latitude) {
         if (longitude == null) {

+ 3 - 2
nightFragrance-massage/src/main/java/com/ylx/merchant/domain/dto/MerchantDetailDTO.java

@@ -6,6 +6,7 @@ import lombok.Data;
 
 import javax.validation.constraints.NotNull;
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 @Data
 @ApiModel("客户端商户详情DTO")
@@ -18,10 +19,10 @@ public class MerchantDetailDTO implements Serializable {
 
     @ApiModelProperty("用户经度")
     @NotNull(message = "用户经度不能为空")
-    private Double longitude;
+    private BigDecimal longitude;
 
     @ApiModelProperty("用户纬度")
     @NotNull(message = "用户纬度不能为空")
-    private Double latitude;
+    private BigDecimal latitude;
 
 }

+ 2 - 1
nightFragrance-massage/src/main/java/com/ylx/merchant/domain/vo/MerchantDetailVO.java

@@ -6,6 +6,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -48,7 +49,7 @@ public class MerchantDetailVO implements Serializable {
     private String brief;
 
     @ApiModelProperty("距离:千米")
-    private String distance;
+    private BigDecimal distance;
 
     @ApiModelProperty("商户评分")
     private Double score;

+ 6 - 2
nightFragrance-massage/src/main/java/com/ylx/order/domain/dto/OrderDateQueryDTO.java

@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import java.math.BigDecimal;
 import java.time.LocalDate;
 
 /**
@@ -54,8 +55,11 @@ public class OrderDateQueryDTO extends Page<OrderDateQueryDTO> {
     private Long merchantId;
 
     @ApiModelProperty("经度")
-    private Double longitude;
+    private BigDecimal longitude;
 
     @ApiModelProperty("纬度")
-    private Double latitude;
+    private BigDecimal latitude;
+
+    @ApiModelProperty("城市编码")
+    private String cityCode;
 }

+ 4 - 1
nightFragrance-massage/src/main/java/com/ylx/order/domain/vo/merchant/OrderPageVO.java

@@ -55,7 +55,7 @@ public class OrderPageVO<T> implements IAfterSaleDisplay {
     private String contactAddressInfo;
 
     @ApiModelProperty("距离")
-    private Double distanceKm;
+    private BigDecimal distanceKm;
 
     @ApiModelProperty("用户下单时纬度")
     private BigDecimal userLatitude;
@@ -63,6 +63,9 @@ public class OrderPageVO<T> implements IAfterSaleDisplay {
     @ApiModelProperty("用户下单时经度")
     private BigDecimal userLongitude;
 
+    @ApiModelProperty("围栏介绍")
+    private String fenceIntro;
+
     @ApiModelProperty("风险提示")
     private Boolean isHighRiskArea;
 

+ 88 - 26
nightFragrance-massage/src/main/java/com/ylx/order/service/impl/TOrderServiceImpl.java

@@ -12,25 +12,16 @@ import com.ylx.common.core.domain.R;
 import com.ylx.common.core.domain.entity.SysDictData;
 import com.ylx.common.core.domain.model.WxLoginUser;
 import com.ylx.common.exception.ServiceException;
-import com.ylx.common.utils.DateUtils;
-import com.ylx.common.utils.DictUtils;
-import com.ylx.common.utils.SecurityUtils;
-import com.ylx.common.utils.StringUtils;
+import com.ylx.common.utils.*;
 import com.ylx.common.weixinPay.enums.WxPayTypeEnum;
 import com.ylx.common.weixinPay.service.WxPayV3Service;
-import com.ylx.massage.domain.MaProject;
-import com.ylx.massage.domain.MaTechnician;
-import com.ylx.massage.domain.TAddress;
-import com.ylx.massage.domain.TWxUser;
+import com.ylx.massage.domain.*;
 import com.ylx.massage.domain.vo.HomeBlock;
 import com.ylx.massage.domain.vo.OrderVerificationVo;
 import com.ylx.massage.domain.vo.TechnicianAvailabilityVo;
 import com.ylx.massage.mapper.MaProjectMapper;
 import com.ylx.massage.mapper.MaTechnicianMapper;
-import com.ylx.massage.service.CouponService;
-import com.ylx.massage.service.IMaTechnicianService;
-import com.ylx.massage.service.TAddressService;
-import com.ylx.massage.service.TWxUserService;
+import com.ylx.massage.service.*;
 import com.ylx.massage.utils.OrderNumberGenerator;
 import com.ylx.message.enums.TriggerEventEnum;
 import com.ylx.message.service.IMessageService;
@@ -120,6 +111,8 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
     private IMessageService messageService;
 
     private static final int NOT_DELETE = 0;
+    @Resource
+    private TGeoFenceService geoFenceService;
 
     @Override
     public TOrder addOrder(TOrder order) {
@@ -554,16 +547,16 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
         order.setPaymentMethod(dto.getPaymentMethod());
 
         // 经纬度安全赋值
-        order.setUserLatitude(new BigDecimal(address.getLatitude()));
-        order.setUserLongitude(new BigDecimal(address.getLongitude()));
+        order.setUserLatitude(address.getLatitude());
+        order.setUserLongitude(address.getLongitude());
 
         merchantAddressList.stream().filter(a -> a.getType() == 1).findFirst().ifPresent(addr -> {
-            order.setMerchantLongitude(new BigDecimal(addr.getLongitude()));
-            order.setMerchantLatitude(new BigDecimal(addr.getLatitude()));
+            order.setMerchantLongitude(addr.getLongitude());
+            order.setMerchantLatitude(addr.getLatitude());
         });
         merchantAddressList.stream().filter(a -> a.getType() == 2).findFirst().ifPresent(addr -> {
-            order.setVirtualLongitude(new BigDecimal(addr.getLongitude()));
-            order.setVirtualLatitude(new BigDecimal(addr.getLatitude()));
+            order.setVirtualLongitude(addr.getLongitude());
+            order.setVirtualLatitude(addr.getLatitude());
         });
 
         // 设置支付状态
@@ -905,19 +898,88 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
                 .eq(TAddress::getIsDefault, 1)
                 .eq(TAddress::getIsDelete, NOT_DELETE));
         if (CollUtil.isEmpty(merchantAddressList)) {
-            throw new ServiceException("商户地址不存在");
+            throw new ServiceException("商户地址不存在,请先完善商户地址");
         }
 
-        TAddress address = CollUtil.getFirst(merchantAddressList);
-        dto.setLongitude(address.getLongitude());
-        dto.setLatitude(address.getLatitude());
+        TAddress merchantAddress = CollUtil.getFirst(merchantAddressList);
+        BigDecimal merchantLat = merchantAddress.getLatitude();
+        BigDecimal merchantLon = merchantAddress.getLongitude();
+
+        // 商户坐标为空拦截
+        if (ObjectUtil.hasNull(merchantLat, merchantLon)) {
+            log.warn("商户id:{} 默认地址经纬度为空,无法计算围栏距离", userId);
+            dto.setLatitude(null);
+            dto.setLongitude(null);
+        } else {
+            dto.setLatitude(merchantLat);
+            dto.setLongitude(merchantLon);
+        }
 
         page = this.baseMapper.queryMerchantOrderList(page, dto);
+        List<OrderPageVO> records = page.getRecords();
+        if (CollUtil.isEmpty(records)) {
+            return page;
+        }
+
+        // 4. 获取所有有效的地理围栏
+        List<TGeoFence> validFenceList = geoFenceService.selectValidFences(dto.getCityCode());
+        if (CollUtil.isEmpty(validFenceList)) {
+            log.info("城市编码{}无有效地理围栏,全部订单非高风险", dto.getCityCode());
+            for (OrderPageVO vo : records) {
+                vo.setIsHighRiskArea(false);
+            }
+            return page;
+        }
+
+        // 5. 遍历订单,在内存中进行空间计算
+        for (OrderPageVO orderVO : records) {
+            BigDecimal userLat = orderVO.getUserLatitude();
+            BigDecimal userLon = orderVO.getUserLongitude();
+            // 用户坐标为空,跳过计算
+            if (ObjectUtil.hasNull(userLat, userLon)) {
+                orderVO.setIsHighRiskArea(false);
+                continue;
+            }
 
-        if (ObjectUtil.isNotNull(page) && CollUtil.isNotEmpty(page.getRecords())) {
-            List<OrderPageVO> records = page.getRecords();
-            for (OrderPageVO record : records) {
-                this.fillCurrentAfterSaleInfo(record, record.getId());
+            // 存储最小距离、对应围栏、是否命中风险圈
+            BigDecimal minDistanceKm = null;
+            TGeoFence nearestFence = null;
+
+            for (TGeoFence fence : validFenceList) {
+                BigDecimal fenceLat = fence.getLatitude();
+                BigDecimal fenceLon = fence.getLongitude();
+                BigDecimal radiusKm = fence.getRadiusKm();
+                // 围栏坐标/半径非法则跳过当前围栏
+                if (ObjectUtil.hasNull(fenceLat, fenceLon, radiusKm)
+                        || radiusKm.compareTo(BigDecimal.ZERO) <= 0) {
+                    continue;
+                }
+
+                // 【修复】传参顺序:用户纬度、用户经度,围栏纬度、围栏经度
+                BigDecimal distKm = DistanceUtil.formatDistanceInKilometers(userLat, userLon, fenceLat, fenceLon);
+                // 距离非法跳过
+                if (ObjectUtil.isNull(distKm)) {
+                    continue;
+                }
+
+                // 更新全局最小距离围栏
+                if (ObjectUtil.isNull(minDistanceKm) || distKm.compareTo(minDistanceKm) < 0) {
+                    minDistanceKm = distKm;
+                    nearestFence = fence;
+                }
+            }
+
+            // 赋值VO
+            if (ObjectUtil.isNull(minDistanceKm) || ObjectUtil.isNull(nearestFence)) {
+                orderVO.setIsHighRiskArea(false);
+                orderVO.setDistanceKm(null);
+                orderVO.setFenceIntro(null);
+            } else {
+                orderVO.setDistanceKm(minDistanceKm);
+                // 判断是否在围栏半径内
+                int compare = minDistanceKm.compareTo(nearestFence.getRadiusKm());
+                orderVO.setIsHighRiskArea(compare <= 0);
+                orderVO.setFenceIntro(nearestFence.getFenceIntro());
             }
         }