소스 검색

Merge remote-tracking branch 'origin/dev' into dev

jinshihui 4 일 전
부모
커밋
be72a6c636
17개의 변경된 파일795개의 추가작업 그리고 208개의 파일을 삭제
  1. 2 1
      nightFragrance-admin/src/main/resources/application-dev.yml
  2. 2 1
      nightFragrance-admin/src/main/resources/application-pro.yml
  3. 2 1
      nightFragrance-admin/src/main/resources/application-test.yml
  4. 19 0
      nightFragrance-common/src/main/java/com/ylx/common/utils/DictUtils.java
  5. 72 11
      nightFragrance-common/src/main/java/com/ylx/common/utils/DistanceUtil.java
  6. 8 6
      nightFragrance-massage/src/main/java/com/ylx/fareSetting/domian/dto/FareCalculateDTO.java
  7. 13 9
      nightFragrance-massage/src/main/java/com/ylx/fareSetting/domian/vo/FareCalculateResultVO.java
  8. 160 71
      nightFragrance-massage/src/main/java/com/ylx/fareSetting/service/impl/MaProjectFareSettingServiceImpl.java
  9. 22 103
      nightFragrance-massage/src/main/java/com/ylx/massage/domain/TAddress.java
  10. 13 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/TAddressService.java
  11. 144 0
      nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TAddressServiceImpl.java
  12. 113 5
      nightFragrance-massage/src/main/java/com/ylx/useradress/domain/controller/UserAddressController.java
  13. 86 0
      nightFragrance-massage/src/main/java/com/ylx/useradress/domain/dto/UserAddressAddDto.java
  14. 31 0
      nightFragrance-massage/src/main/java/com/ylx/useradress/domain/dto/UserAddressDeleteDto.java
  15. 79 0
      nightFragrance-massage/src/main/java/com/ylx/useradress/domain/dto/UserAddressUpdateDto.java
  16. 25 0
      nightFragrance-massage/src/main/java/com/ylx/useradress/domain/vo/UserAddressVo.java
  17. 4 0
      nightFragrance-massage/src/main/resources/mapper/massage/TAddressMapper.xml

+ 2 - 1
nightFragrance-admin/src/main/resources/application-dev.yml

@@ -30,7 +30,8 @@ server:
       max: 800
       # Tomcat启动初始化的线程数,默认值10
       min-spare: 100
-
+# 本地环境登录验证码关闭,不做校验
+codeSwitch: false
 # 日志配置
 logging:
   level:

+ 2 - 1
nightFragrance-admin/src/main/resources/application-pro.yml

@@ -32,7 +32,8 @@ server:
       max: 800
       # Tomcat启动初始化的线程数,默认值10
       min-spare: 100
-
+# 本地环境登录验证码关闭,不做校验
+codeSwitch: false
 # 日志配置
 logging:
   level:

+ 2 - 1
nightFragrance-admin/src/main/resources/application-test.yml

@@ -33,7 +33,8 @@ server:
       max: 800
       # Tomcat启动初始化的线程数,默认值10
       min-spare: 100
-
+# 本地环境登录验证码关闭,不做校验
+codeSwitch: false
 # 日志配置
 logging:
   level:

+ 19 - 0
nightFragrance-common/src/main/java/com/ylx/common/utils/DictUtils.java

@@ -1,7 +1,10 @@
 package com.ylx.common.utils;
 
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.List;
+import java.util.stream.Collectors;
+
 import com.alibaba.fastjson2.JSONArray;
 import com.ylx.common.constant.CacheConstants;
 import com.ylx.common.core.domain.entity.SysDictData;
@@ -183,4 +186,20 @@ public class DictUtils
     {
         return CacheConstants.SYS_DICT_KEY + configKey;
     }
+
+    /**
+     * 获取按 dict_sort 字段正序排列的字典数据列表
+     *
+     * @param dictType 字典类型
+     * @return 排序后的字典数据列表(按 dict_sort 升序),若无数据则返回 null
+     */
+    public static List<SysDictData> getSortedDictCache(String dictType) {
+        List<SysDictData> datas = getDictCache(dictType);
+        if (datas != null && !datas.isEmpty()) {
+            return datas.stream()
+                    .sorted(Comparator.comparingLong(SysDictData::getDictSort))
+                    .collect(Collectors.toList());
+        }
+        return datas;
+    }
 }

+ 72 - 11
nightFragrance-common/src/main/java/com/ylx/common/utils/DistanceUtil.java

@@ -7,12 +7,17 @@ import cn.hutool.core.util.ObjectUtil;
  */
 public class DistanceUtil {
 
+    private static final double EARTH_RADIUS_M = 6_371_000; // 地球半径,单位米
+    private static final double TO_RADIANS = Math.PI / 180.0;
+
     /**
+     * 格式化两点间距离
+     *
      * @param userLat 用户纬度
      * @param userLon 用户经度
      * @param shopLat 门店纬度
      * @param shopLon 门店经度
-     * @return 返回距离单位为m
+     * @return 距离(米),若坐标无效则返回 "未知"
      */
     public static String formatDistance(Double userLat, Double userLon, Double shopLat, Double shopLon) {
         // 任意坐标空/0 → 未知
@@ -20,19 +25,75 @@ public class DistanceUtil {
                 || userLat == 0 || userLon == 0 || shopLat == 0 || shopLon == 0) {
             return "未知";
         }
+
+        // 验证坐标范围(可选,增加健壮性)
+        if (!isValidCoordinate(userLat, userLon) || !isValidCoordinate(shopLat, shopLon)) {
+            return "未知"; // 或抛出异常
+        }
+
         double meter = getDistance(userLat, userLon, shopLat, shopLon);
         return String.valueOf(Math.round(meter));
     }
 
-    // 球面距离:lat1,lon1,lat2,lon2 → 米
-    private static double getDistance(double lat1, double lon1, double lat2, double lon2) {
-        double radLat1 = Math.toRadians(lat1);
-        double radLon1 = Math.toRadians(lon1);
-        double radLat2 = Math.toRadians(lat2);
-        double radLon2 = Math.toRadians(lon2);
-        double r = 6371;
-        double km = r * Math.acos(Math.cos(radLat1) * Math.cos(radLat2) * Math.cos(radLon2 - radLon1) + Math.sin(radLat1) * Math.sin(radLat2));
-        return km * 1000;
+    /**
+     * 格式化两点间距离(单位:公里)
+     *
+     * @param userLat 用户纬度
+     * @param userLon 用户经度
+     * @param shopLat 门店纬度
+     * @param shopLon 门店经度
+     * @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 "未知";
+        }
+        if (!isValidCoordinate(userLat, userLon) || !isValidCoordinate(shopLat, shopLon)) {
+            return "未知";
+        }
+        double meter = getDistance(userLat, userLon, shopLat, shopLon);
+        double kilometer = meter / 1000.0;
+        // 四舍五入到两位小数
+        return String.format("%.2f", kilometer);
+    }
+
+    /**
+     * 获取两点间球面距离(米)
+     *
+     * @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;
+        double lon1Rad = lon1 * TO_RADIANS;
+        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 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+
+        return EARTH_RADIUS_M * c;
     }
 
-}
+    /**
+     * 验证坐标是否在有效范围内
+     *
+     * @param lat 纬度
+     * @param lon 经度
+     * @return 是否有效
+     */
+    private static boolean isValidCoordinate(double lat, double lon) {
+        return Math.abs(lat) <= 90.0 && Math.abs(lon) <= 180.0;
+    }
+}

+ 8 - 6
nightFragrance-massage/src/main/java/com/ylx/fareSetting/domian/dto/FareCalculateDTO.java

@@ -14,26 +14,28 @@ public class FareCalculateDTO implements Serializable {
     private static final long serialVersionUID = 1632111945634156891L;
 
 
-    @ApiModelProperty(value = "商户ID", required = true)
+    @ApiModelProperty("商户ID")
     @NotNull(message = "商户ID不能为空")
     private Long merchantId;
 
-    @ApiModelProperty(value = "项目/服务ID", required = true)
+    @ApiModelProperty("项目/服务ID")
     @NotNull(message = "项目ID不能为空")
     private Long projectId;
 
-    @ApiModelProperty(value = "预约开始时间", required = true, example = "2024-01-07 15:30:00")
+    @ApiModelProperty(value = "预约开始时间", example = "2024-01-07 15:30:00")
     @NotNull(message = "预约时间不能为空")
     private LocalDateTime appointmentStartTime;
 
-    @ApiModelProperty(value = "城市编码", required = true)
+    @ApiModelProperty("城市编码")
     @NotNull(message = "城市编码")
     private String cityCode;
 
-    @ApiModelProperty(value = "用户下单经度", required = true)
+    @ApiModelProperty("用户下单经度")
+    @NotNull(message = "用户下单经度不能为空")
     private Double longitude;
 
-    @ApiModelProperty(value = "用户下单纬度", required = true)
+    @ApiModelProperty("用户下单纬度")
+    @NotNull(message = "用户下单纬度不能为空")
     private Double latitude;
 
 }

+ 13 - 9
nightFragrance-massage/src/main/java/com/ylx/fareSetting/domian/vo/FareCalculateResultVO.java

@@ -9,21 +9,25 @@ import java.math.BigDecimal;
 @ApiModel("车费计算结果")
 public class FareCalculateResultVO {
 
-    @ApiModelProperty("实际导航距离(公里)")
+    @ApiModelProperty("预估打车费(元)")
+    private BigDecimal estimatedFare;
+
+    @ApiModelProperty("全程距离(公里)")
     private BigDecimal actualDistanceKm;
 
-    @ApiModelProperty("免费里程额度(公里)")
-    private BigDecimal freeDistanceKm;
+    @ApiModelProperty("免费公里数")
+    private BigDecimal freeKm;
 
-    @ApiModelProperty("超出免费里程的距离(公里)")
-    private BigDecimal exceedDistanceKm;
+    @ApiModelProperty("起步价")
+    private BigDecimal baseFare;
 
-    @ApiModelProperty("预估打车费(元)")
-    private BigDecimal estimatedFare;
+    @ApiModelProperty("起步距离(公里)")
+    private BigDecimal baseDistance;
+
+    @ApiModelProperty("超出起步价后每公里费用")
+    private BigDecimal additionalFarePer;
 
     @ApiModelProperty("是否免车费")
     private Boolean isFree;
 
-    @ApiModelProperty("提示信息,例如:在5公里范围内免车费")
-    private String message;
 }

+ 160 - 71
nightFragrance-massage/src/main/java/com/ylx/fareSetting/service/impl/MaProjectFareSettingServiceImpl.java

@@ -1,12 +1,13 @@
 package com.ylx.fareSetting.service.impl;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.CoordinateUtil;
-import cn.hutool.core.util.NumberUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ylx.common.core.domain.entity.SysDictData;
 import com.ylx.common.exception.ServiceException;
+import com.ylx.common.utils.DictUtils;
+import com.ylx.common.utils.DistanceUtil;
 import com.ylx.fareSetting.domian.MaProjectFareSetting;
 import com.ylx.fareSetting.domian.dto.FareCalculateDTO;
 import com.ylx.fareSetting.domian.vo.FareCalculateResultVO;
@@ -21,6 +22,8 @@ import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.util.List;
 import java.util.Optional;
@@ -35,106 +38,192 @@ public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSe
     @Resource
     private TAddressService addressService;
 
+    @Override
     public FareCalculateResultVO calculateFare(FareCalculateDTO dto) {
         FareCalculateResultVO result = new FareCalculateResultVO();
 
         // 1.获取商户的默认地址
-        TAddress address = this.addressService.getOne(new LambdaQueryWrapper<TAddress>().eq(TAddress::getMerchantId, dto.getMerchantId()).eq(TAddress::getType, 1));
+        TAddress address = this.addressService.getOne(new LambdaQueryWrapper<TAddress>()
+                .eq(TAddress::getMerchantId, dto.getMerchantId())
+                .eq(TAddress::getType, 1));
         if (ObjectUtil.isNull(address)) {
             throw new ServiceException("无法获取商户的默认地址");
         }
 
-        // 1. 获取商户车费配置
+        // 2. 计算直线距离(公里)
+        String distanceStr = DistanceUtil.formatDistanceInKilometers(
+                dto.getLongitude(), dto.getLatitude(),
+                address.getLongitude(), address.getLatitude()
+        );
+        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();
+        boolean isDay = isDayTimePeriod(appointmentStartTime);
+
+        // 4. 获取商户车费配置
         LambdaQueryWrapper<MaProjectFareSetting> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(MaProjectFareSetting::getMerchantId, dto.getMerchantId())
                 .eq(MaProjectFareSetting::getIsDelete, 0);
-        List<MaProjectFareSetting> list = this.baseMapper.selectList(wrapper);
+        List<MaProjectFareSetting> configList = this.baseMapper.selectList(wrapper);
 
         MaProjectFareSetting finalConfig = null;
 
-        if (CollUtil.isNotEmpty(list)) {
-            if (list.size() == 1 && ObjectUtil.equals(1, list.get(0).getIsUnified())) {
-                // 【情况A】集合数量为1 -> 是统一配置
-                // 直接取唯一的一条作为最终配置
-                log.info("商户[{}]仅有一条配置,判定为统一设置模式", dto.getMerchantId());
-                finalConfig = list.get(0);
+        if (CollUtil.isNotEmpty(configList)) {
+            // 先查找是否存在统一配置 (isUnified = 1)
+            Optional<MaProjectFareSetting> unifiedOpt = configList.stream()
+                    .filter(c -> ObjectUtil.equals(1, c.getIsUnified()))
+                    .findFirst();
 
+            if (unifiedOpt.isPresent()) {
+                finalConfig = unifiedOpt.get();
             } else {
-                // 【情况B】集合数量大于1 -> 不是统一配置(独立项目配置)
-                // 需要根据传入的 projectId 从列表中筛选出对应的项目配置
-                log.info("商户[{}]存在多条配置,根据projectId[{}]进行匹配", dto.getMerchantId(), dto.getProjectId());
+                // 查找匹配的项目配置
+                log.info("商户[{}]不存在统一配置,根据projectId[{}]进行匹配", dto.getMerchantId(), dto.getProjectId());
 
-                // 使用 Stream API 过滤出当前项目对应的配置
-                Optional<MaProjectFareSetting> projectConfigOpt = list.stream()
+                Optional<MaProjectFareSetting> projectConfigOpt = configList.stream()
                         .filter(item -> item.getProjectId() != null && item.getProjectId().equals(dto.getProjectId()))
                         .findFirst();
 
                 if (projectConfigOpt.isPresent()) {
+                    // 找到了对应项目的配置
                     finalConfig = projectConfigOpt.get();
-                } else {
-                    // 可选:如果没找到对应项目的配置,是否回退到第一条?或者报错?
-                    // 这里假设必须精确匹配,否则抛异常或给默认值
-                    throw new ServiceException("未找到该项目[" + dto.getProjectId() + "]的专属车费配置");
                 }
             }
-        } else {
-            // 【情况C】商户无配置 -> 获取后台城市车费设置
-            log.info("商户[{}]无配置,尝试获取城市[{}]的默认配置", dto.getMerchantId(), dto.getCityCode());
-            // TODO: 调用获取城市配置的 Service/Method
-            // finalConfig = cityFareService.getDefaultByCity(dto.getCityCode());
-            TFareSettingVo fareSetting = fareSettingService.getFareSetting("", dto.getCityCode());
         }
 
-        // 此时 finalConfig 应该已经有值了,继续后续的距离计算逻辑...
         if (ObjectUtil.isNull(finalConfig)) {
             throw new ServiceException("无法获取有效的车费计算规则");
         }
 
-        // 2. 判断时间段 (白天 vs 夜间)
-        // 假设白天是 7:30 - 19:30,其余为夜间 (具体逻辑根据你的业务定)
-        LocalTime time = dto.getAppointmentStartTime().toLocalTime();
-        boolean isDayTime = !time.isBefore(LocalTime.of(7, 30)) && time.isBefore(LocalTime.of(19, 30));
-
-        BigDecimal freeKm = isDayTime ? finalConfig.getDayFreeKm() : finalConfig.getNightFreeKm();
-        result.setFreeDistanceKm(freeKm);
-
-
-        // 3. 计算实际距离 (调用地图API)
-//
-//        // 参数顺序:纬度(lat), 经度(lon)
-//
-////        double distance = CoordinateUtil(37.870000, 112.550000, 37.880000, 112.560000);
-//
-//// 返回值单位是 米 (m),转换为公里
-//        double distanceKm = NumberUtil.div(distance, 1000, 2); // 保留2位小数
-//
-//        double distanceInMeters = this.baseMapper.calculateDistance(
-//                dto.getLatitude(), dto.getLongitude(),
-//                targetLat, targetLon
-//        );
-//
-//        BigDecimal actualKm = new BigDecimal(distanceInMeters).divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP);
-//        result.setActualDistanceKm(actualKm);
-//
-//        // 4. 计算费用
-//        BigDecimal freeKm = result.getFreeDistanceKm();
-//        BigDecimal exceedKm = actualKm.subtract(freeKm);
-//
-//        // 如果小于0,说明在免费范围内
-//        if (exceedKm.compareTo(BigDecimal.ZERO) <= 0) {
-//            result.setExceedDistanceKm(BigDecimal.ZERO);
-//            result.setEstimatedFare(BigDecimal.ZERO);
-//            result.setIsFree(true);
-//            result.setMessage(String.format("距离 %.2f km,在 %.2f km 免费范围内", actualKm, freeKm));
-//        } else {
-//            result.setExceedDistanceKm(exceedKm);
-//            // 费用 = 超出距离 * 单价
-//            BigDecimal fare = exceedKm.multiply(UNIT_PRICE).setScale(2, RoundingMode.HALF_UP);
-//            result.setEstimatedFare(fare);
-//            result.setIsFree(false);
-//            result.setMessage(String.format("超出免费里程 %.2f km,需支付车费", exceedKm));
-//        }
+        // 商户配置的免车费距离(用于扣减)
+        BigDecimal merchantFreeKm = isDay ? finalConfig.getDayFreeKm() : finalConfig.getNightFreeKm();
+
+        if (ObjectUtil.isNull(merchantFreeKm) || merchantFreeKm.compareTo(BigDecimal.ZERO) <= 0) {
+            merchantFreeKm = BigDecimal.ZERO;
+            log.info("商户[{}]配置的免车费距离为 null 或 <= 0, 视为 0", dto.getMerchantId());
+        } else {
+            log.info("商户[{}]使用配置ID={}, 免费公里数: {}", dto.getMerchantId(), finalConfig.getId(), merchantFreeKm);
+        }
+
+        // 4. 计算【打车距离】(即计费里程)
+        BigDecimal straightLineBigDecimal = new BigDecimal(straightLineKm).setScale(6, RoundingMode.HALF_UP);
+        BigDecimal effectiveDistance = straightLineBigDecimal.subtract(merchantFreeKm);
+        if (effectiveDistance.compareTo(BigDecimal.ZERO) < 0) {
+            effectiveDistance = BigDecimal.ZERO;
+        }
+        log.info("直线距离 {} km, 商户免车费距离 {} km -> 打车距离 {} km",
+                straightLineKm, merchantFreeKm, effectiveDistance);
+
+        // 5. 获取城市车费规则(用于最终计费)
+        TFareSettingVo cityFare = fareSettingService.getFareSetting(appointmentStartTime.toString(), dto.getCityCode());
+        if (ObjectUtil.isNull(cityFare)) {
+            throw new ServiceException("未找到城市[" + dto.getCityCode() + "]的车费配置");
+        }
+
+        // 6. 使用城市规则计算费用
+        BigDecimal baseFare = cityFare.getBaseFare();          // 起步价
+        BigDecimal baseDistance = cityFare.getBaseDistance();  // 起步距离(公里)
+        BigDecimal additionalFarePer = cityFare.getAdditionalFarePer(); // 超出后每公里价格
+
+        BigDecimal estimatedFare;
+        boolean isFree;
+
+        // 如果打车距离为 0,表示全程被商户免车费覆盖,直接免费
+        if (effectiveDistance.compareTo(BigDecimal.ZERO) <= 0) {
+            estimatedFare = BigDecimal.ZERO;
+            isFree = true;
+        } else {
+            // 打车距离 > 0,才进入城市计费逻辑
+            BigDecimal exceedDistance = effectiveDistance.subtract(baseDistance);
+            if (exceedDistance.compareTo(BigDecimal.ZERO) <= 0) {
+                // 在起步距离内(但打车距离 > 0)
+                estimatedFare = baseFare;
+                isFree = false;
+            } else {
+                // 超出起步距离
+                BigDecimal extraFare = exceedDistance.multiply(additionalFarePer).setScale(2, RoundingMode.HALF_UP);
+                estimatedFare = baseFare.add(extraFare).setScale(2, RoundingMode.HALF_UP);
+                isFree = false;
+            }
+        }
+
+        // 7. 设置结果
+        result.setActualDistanceKm(straightLineBigDecimal.setScale(2, RoundingMode.HALF_UP)); // 原始直线距离(展示用)
+        result.setEstimatedFare(estimatedFare);
+        result.setIsFree(isFree);
 
         return result;
     }
+
+    /**
+     * 判断预约时间是否属于白天时间段
+     *
+     * @param appointmentStartTime 预约开始时间
+     * @return true表示是白天时间段,false表示不是白天时间段(可能是夜间或其他时间段)
+     */
+    public boolean isDayTimePeriod(LocalDateTime appointmentStartTime) {
+        // 提取预约时间的小时和分钟,仅用于时间段比较
+        LocalTime appointmentTime = appointmentStartTime.toLocalTime();
+
+        // 查询白天时间段配置
+        List<SysDictData> dayTimeRanges = DictUtils.getSortedDictCache("day_time");
+
+        if (CollUtil.isNotEmpty(dayTimeRanges) && dayTimeRanges.size() >= 2) {
+            // 获取开始时间和结束时间
+            LocalTime dayStartTime = parseTime(CollUtil.getFirst(dayTimeRanges).getDictValue()); // 如 7:30
+            LocalTime dayEndTime = parseTime(CollUtil.getLast(dayTimeRanges).getDictValue());   // 如 19:30
+
+            // 判断是否在白天时间段范围内
+            return isTimeInRange(appointmentTime, dayStartTime, dayEndTime);
+        }
+
+        // 如果没有找到白天时间段配置,默认返回false
+        return false;
+    }
+
+    /**
+     * 解析时间字符串为LocalTime
+     */
+    private LocalTime parseTime(String timeStr) {
+        // 处理可能的格式差异,比如"7:30"转为"07:30"
+        if (!timeStr.contains(":")) {
+            throw new IllegalArgumentException("无效的时间格式: " + timeStr);
+        }
+
+        String[] parts = timeStr.split(":");
+        if (parts.length == 2) {
+            int hour = Integer.parseInt(parts[0]);
+            int minute = Integer.parseInt(parts[1]);
+            return LocalTime.of(hour, minute);
+        } else {
+            throw new IllegalArgumentException("无效的时间格式: " + timeStr);
+        }
+    }
+
+    /**
+     * 判断时间是否在时间段范围内(不跨越午夜)
+     */
+    private boolean isTimeInRange(LocalTime targetTime, LocalTime startTime, LocalTime endTime) {
+        // 如果开始时间小于结束时间(同一天内)
+        if (startTime.isBefore(endTime)) {
+            return (targetTime.equals(startTime) || targetTime.isAfter(startTime)) &&
+                    (targetTime.equals(endTime) || targetTime.isBefore(endTime));
+        } else if (startTime.isAfter(endTime)) {
+            // 如果开始时间大于结束时间(跨越午夜),这应该是夜间时间段
+            return false;
+        } else {
+            // 如果开始时间等于结束时间
+            return targetTime.equals(startTime);
+        }
+    }
 }

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

@@ -11,6 +11,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.Date;
 
 /**
@@ -26,7 +27,11 @@ public class TAddress extends Model<TAddress> {
     //主键
     @ApiModelProperty("主键")
     private String id;
-
+    /**
+     * TwxUser的主键用户id
+     */
+    @ApiModelProperty("TwxUser的主键用户id")
+    private String userId;
     /**
      * 商户id
      */
@@ -66,12 +71,17 @@ public class TAddress extends Model<TAddress> {
     //纬度
     @ApiModelProperty("纬度")
     private Double latitude;
-    //详细地址
-    @ApiModelProperty("详细地址")
+    //地址
+    @ApiModelProperty("地址")
     private String address;
 
-    @ApiModelProperty("地址name")
-    private String name;
+    //详细地址(地址+门牌号)
+    @ApiModelProperty("详细地址")
+    private String detailAddress;
+
+    //门牌号
+    @ApiModelProperty("门牌号")
+    private String roomNumber;
 
     /**
      * 用户类型 1:用户 2:技师
@@ -80,10 +90,15 @@ public class TAddress extends Model<TAddress> {
     private Integer userType;
 
     /**
-     * 地址类型 0:普通地址 1:默认地址 2:虚拟地址
+     * 地址类型 1:真实地址 2:虚拟地址
      */
-    @ApiModelProperty("地址类型 0:普通地址 1:默认地址 2:虚拟地址")
+    @ApiModelProperty("地址类型 1:真实地址 2:虚拟地址")
     private Integer type;
+    /**
+     * 地址是否默认 1:默认 0:非默认
+     */
+    @ApiModelProperty("地址是否默认 1:默认 0:非默认")
+    private Integer isDefault;
 
     /**
      * 创建时间
@@ -105,102 +120,6 @@ public class TAddress extends Model<TAddress> {
     private Integer isDelete;
 
 
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public String getOpenid() {
-        return openid;
-    }
-
-    public void setOpenid(String openid) {
-        this.openid = openid;
-    }
-
-    public String getPhone() {
-        return phone;
-    }
-
-    public void setPhone(String phone) {
-        this.phone = phone;
-    }
-
-    public String getUserName() {
-        return userName;
-    }
-
-    public void setUserName(String userName) {
-        this.userName = userName;
-    }
-
-    public String getAtlasAdd() {
-        return atlasAdd;
-    }
-
-    public void setAtlasAdd(String atlasAdd) {
-        this.atlasAdd = atlasAdd;
-    }
-
-    public Double getLongitude() {
-        return longitude;
-    }
-
-    public void setLongitude(Double longitude) {
-        this.longitude = longitude;
-    }
-
-    public Double getLatitude() {
-        return latitude;
-    }
-
-    public void setLatitude(Double latitude) {
-        this.latitude = latitude;
-    }
-
-    public Integer getType() {
-        return type;
-    }
-
-    public void setType(Integer type) {
-        this.type = type;
-    }
-
-    public String getAddress() {
-        return address;
-    }
-
-    public void setAddress(String address) {
-        this.address = address;
-    }
-
-    public Date getCreateTime() {
-        return createTime;
-    }
-
-    public void setCreateTime(Date createTime) {
-        this.createTime = createTime;
-    }
-
-    public Date getUpdateTime() {
-        return updateTime;
-    }
-
-    public void setUpdateTime(Date updateTime) {
-        this.updateTime = updateTime;
-    }
-
-    public Integer getIsDelete() {
-        return isDelete;
-    }
-
-    public void setIsDelete(Integer isDelete) {
-        this.isDelete = isDelete;
-    }
-
     /**
      * 获取主键值
      *

+ 13 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/TAddressService.java

@@ -2,7 +2,10 @@ package com.ylx.massage.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ylx.massage.domain.TAddress;
+import com.ylx.useradress.domain.dto.UserAddressAddDto;
+import com.ylx.useradress.domain.dto.UserAddressDeleteDto;
 import com.ylx.useradress.domain.dto.UserAddressDto;
+import com.ylx.useradress.domain.dto.UserAddressUpdateDto;
 import com.ylx.useradress.domain.vo.UserAddressVo;
 
 import java.util.List;
@@ -41,5 +44,15 @@ public interface TAddressService extends IService<TAddress> {
     Object insertVirtualAddress(TAddress tAddress);
 
     Object updateAddress(TAddress tAddress);
+
+    /**
+     * 新增地址
+     * @param dto
+     */
+    void addUserAddress(UserAddressAddDto dto);
+
+    void updateUserAddress(UserAddressUpdateDto dto);
+
+    void deleteUserAddress(UserAddressDeleteDto dto);
 }
 

+ 144 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TAddressServiceImpl.java

@@ -1,10 +1,15 @@
 package com.ylx.massage.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ylx.common.utils.StringUtils;
+import com.ylx.useradress.domain.dto.UserAddressAddDto;
+import com.ylx.useradress.domain.dto.UserAddressDeleteDto;
 import com.ylx.useradress.domain.dto.UserAddressDto;
+import com.ylx.useradress.domain.dto.UserAddressUpdateDto;
 import com.ylx.useradress.domain.vo.UserAddressVo;
 import com.ylx.massage.mapper.TAddressMapper;
 import com.ylx.massage.domain.TAddress;
@@ -27,6 +32,18 @@ import java.util.stream.Collectors;
 @Service("tAddressService")
 public class TAddressServiceImpl extends ServiceImpl<TAddressMapper, TAddress> implements TAddressService {
 
+    //0=未删除
+    private static final Integer NO_DELETE = 0;
+    //1=用户
+    private static final Integer USER_TYPE = 1;
+    //1=真实地址
+    private static final Integer TYPE = 1;
+    //1=默认地址
+    private static final Integer IS_DEFAULT = 1;
+    //0=不是默认地址
+    private static final Integer NO_DEFAULT = 0;
+
+
     @Override
     public TAddress getByOpenId(String openId) {
         LambdaQueryWrapper<TAddress> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
@@ -138,5 +155,132 @@ public class TAddressServiceImpl extends ServiceImpl<TAddressMapper, TAddress> i
         }
         return this.updateById(tAddress);
     }
+
+    /**
+     * 用户端新增地址
+     * @param dto
+     */
+    @Override
+    public void addUserAddress(UserAddressAddDto dto) {
+
+
+        // 1. DTO -> 实体 转换
+        TAddress address = new TAddress();
+        BeanUtil.copyProperties(dto, address);
+
+        // 2. 补充实体中缺失但需要入库的字段
+        address.setIsDelete(NO_DELETE);
+        address.setUserType(USER_TYPE);
+        address.setType(TYPE);
+        address.setDetailAddress(address.getAddress() + " " + address.getRoomNumber());
+
+
+        // 3. 处理默认地址逻辑
+        // 如果当前新增的地址是默认地址,需要先将该用户下的其他地址设为非默认
+        if (IS_DEFAULT.equals(dto.getIsDefault())) {
+            LambdaQueryWrapper<TAddress> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(TAddress::getUserId, dto.getUserId())
+                    .eq(TAddress::getIsDefault, IS_DEFAULT);
+            TAddress defaultExists = this.baseMapper.selectOne(wrapper);
+            if (defaultExists != null) {
+                defaultExists.setIsDefault(NO_DEFAULT);
+                this.updateById(defaultExists);
+            }
+        }
+
+        // 5. 执行入库
+        this.baseMapper.insert(address);
+    }
+
+    /**
+     * 用户端更新地址
+     * @param dto
+     */
+    @Override
+    public void updateUserAddress(UserAddressUpdateDto dto) {
+        // 1. 参数校验:地址ID不能为空
+        if (StrUtil.isBlank(dto.getId())) {
+            throw new RuntimeException("地址ID不能为空");
+        }
+
+        // 2. 查询原有地址记录
+        TAddress existingAddress = this.baseMapper.selectById(dto.getId());
+        if (existingAddress == null) {
+            throw new RuntimeException("地址不存在");
+        }
+
+        // 3. 权限校验:只能修改自己的地址
+        if (!existingAddress.getUserId().equals(dto.getUserId())) {
+            throw new RuntimeException("无权修改该地址");
+        }
+        // 4. 将DTO中非空字段拷贝到实体(忽略为null的字段)
+        BeanUtil.copyProperties(dto, existingAddress, "id", "userId", "openid", "merchantId",
+                "userType", "type", "createTime", "isDelete");
+
+        // 6. 拼接详细地址(address + 空格 + roomNumber)
+        String detailAddress = "";
+        if (StrUtil.isNotBlank(existingAddress.getAddress())
+                && StrUtil.isNotBlank(existingAddress.getRoomNumber())) {
+            detailAddress = existingAddress.getAddress() + " " + existingAddress.getRoomNumber();
+        } else if (StrUtil.isNotBlank(existingAddress.getAddress())) {
+            detailAddress = existingAddress.getAddress();
+        } else if (StrUtil.isNotBlank(existingAddress.getRoomNumber())) {
+            detailAddress = existingAddress.getRoomNumber();
+        }
+        existingAddress.setDetailAddress(detailAddress);
+
+        // 8. 执行更新(根据ID更新)
+        int rows = this.baseMapper.updateById(existingAddress);
+        if (rows != 1) {
+            throw new RuntimeException("更新地址失败");
+        }
+    }
+
+    /**
+     * 用户端删除地址
+     * @param dto
+     */
+    @Override
+    public void deleteUserAddress(UserAddressDeleteDto dto) {
+        String addressId = dto.getId();
+
+        // 1. 查询地址是否存在且未被删除
+        TAddress address = this.baseMapper.selectOne(
+                new LambdaQueryWrapper<TAddress>()
+                        .eq(TAddress::getId, addressId)
+                        .eq(TAddress::getIsDelete, 0)
+        );
+        if (address == null) {
+            throw new RuntimeException("地址不存在或已被删除");
+        }
+
+
+        // 2. 权限校验:只能删除自己的地址
+        if (!address.getUserId().equals(dto.getUserId())) {
+            throw new RuntimeException("无权删除该地址");
+        }
+
+        // 3. 执行逻辑删除(MyBatis-Plus 会自动将 isDelete 设为 1,并更新 updateTime)
+        int rows = this.baseMapper.deleteById(addressId);
+        if (rows != 1) {
+            throw new RuntimeException("删除地址失败");
+        }
+
+        // 4. 【可选】如果删除的是默认地址,将用户的其他最新地址设为默认
+        if (IS_DEFAULT.equals(address.getIsDefault())) {
+            // 查询该用户下未被删除的其他地址,按创建时间倒序取第一条
+            LambdaQueryWrapper<TAddress> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(TAddress::getUserId, dto.getUserId())
+                    .eq(TAddress::getIsDelete, 0)
+                    .orderByDesc(TAddress::getCreateTime)
+                    .last("limit 1");
+            TAddress newDefault = this.baseMapper.selectOne(wrapper);
+            if (newDefault != null) {
+                newDefault.setIsDefault(IS_DEFAULT);
+                this.baseMapper.updateById(newDefault);
+            }
+        }
+    }
+
 }
 

+ 113 - 5
nightFragrance-massage/src/main/java/com/ylx/useradress/domain/controller/UserAddressController.java

@@ -1,20 +1,32 @@
 package com.ylx.useradress.domain.controller;
 
+import com.alibaba.fastjson.JSON;
 import com.ylx.common.core.domain.R;
+import com.ylx.common.core.domain.model.aliyun.SMSVerificationCode;
+import com.ylx.common.core.domain.model.aliyun.SendSmsComponents;
+import com.ylx.common.core.domain.model.aliyun.SendSmsEnum;
+import com.ylx.common.utils.StringUtils;
+import com.ylx.massage.domain.vo.Result;
 import com.ylx.massage.service.TAddressService;
+import com.ylx.useradress.domain.dto.UserAddressAddDto;
+import com.ylx.useradress.domain.dto.UserAddressDeleteDto;
 import com.ylx.useradress.domain.dto.UserAddressDto;
+import com.ylx.useradress.domain.dto.UserAddressUpdateDto;
 import com.ylx.useradress.domain.vo.UserAddressVo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
 import java.util.List;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
 
 /**
  * 类描述:
@@ -25,12 +37,19 @@ import java.util.List;
  */
 @RestController
 @RequestMapping("/user/address")
-@Api(tags = {"项目车费标准设置"})
+@Api(tags = {"用户端地址管理"})
 @Slf4j
 public class UserAddressController {
+    public static final String PHONE_USER_ADDRESS_CODE_KEY = "user:address:phon:";
+    @Value("${codeSwitch}")
+    private Boolean codeSwitch;
     @Resource
     private TAddressService tAddressService;
 
+    @Autowired
+    private StringRedisTemplate redisTemplate;
+    @Autowired
+    private SendSmsComponents sendSms;
     /**
      * 用户地址列表查询
      *
@@ -41,4 +60,93 @@ public class UserAddressController {
     public R<List<UserAddressVo>> getUserAddress(@Validated @RequestBody UserAddressDto dto) {
         return R.ok(tAddressService.getByOpenIdList(dto));
     }
+
+    /**
+     * 用户地址新增
+     *
+     * @return
+     */
+    @ApiOperation(value="新增用户地址", notes="新增用户地址")
+    @PostMapping(value = "/addUserAddress")
+    public R<?> addUserAddress(@Validated @RequestBody UserAddressAddDto dto) {
+        if (codeSwitch) {
+            // 短信验证
+            String msg = redisTemplate.opsForValue().get(PHONE_USER_ADDRESS_CODE_KEY + dto.getPhone());
+            if (StringUtils.isEmpty(msg)) {
+                return R.fail("验证码已失效");
+            }
+
+            if (!dto.getPhoneMsg().equals(msg)) {
+                return R.fail("短信验证码不正确");
+            }
+        }
+        tAddressService.addUserAddress(dto);
+        return R.ok();
+    }
+
+    /**
+     * 用户地址编辑
+     *
+     * @return
+     */
+    @ApiOperation(value="用户地址编辑", notes="用户地址编辑")
+    @PostMapping(value = "/updateUserAddress")
+    public R<?> updateUserAddress(@Validated @RequestBody UserAddressUpdateDto dto) {
+        if (codeSwitch) {
+            // 短信验证
+            String msg = redisTemplate.opsForValue().get(PHONE_USER_ADDRESS_CODE_KEY + dto.getPhone());
+            if (StringUtils.isEmpty(msg)) {
+                return R.fail("验证码已失效");
+            }
+
+            if (!dto.getPhoneMsg().equals(msg)) {
+                return R.fail("短信验证码不正确");
+            }
+        }
+        tAddressService.updateUserAddress(dto);
+        return R.ok();
+    }
+
+    /**
+     * 用户地址删除
+     *
+     * @return
+     */
+    @ApiOperation(value="用户地址删除", notes="用户地址删除")
+    @PostMapping(value = "/deleteUserAddress")
+    public R<?> deleteUserAddress(@Validated @RequestBody UserAddressDeleteDto dto) {
+        tAddressService.deleteUserAddress(dto);
+        return R.ok();
+    }
+
+    /**
+     * 用户地址新增或者编辑时获取验证码
+     * @param phone
+     * @param request
+     * @return
+     */
+    @GetMapping("/sendMsg")
+    @ApiOperation(value = "短信发送", notes = "短信发送")
+    public Result sendMsg(@RequestParam String phone, HttpServletRequest request) {
+        if (StringUtils.isEmpty(phone)) {
+            return Result.error("手机号不能为空");
+        }
+        Random rand = new Random();
+        // randNumber 将被赋值为一个 MIN 和 MAX 范围内的随机数
+        int randNumber = rand.nextInt(9999 - 1000 + 1) + 1000;
+        // 保存验证码到redis
+        redisTemplate.opsForValue()
+                .set(PHONE_USER_ADDRESS_CODE_KEY + phone, String.valueOf(randNumber), 5L
+                        , TimeUnit.MINUTES);
+        try {
+            SMSVerificationCode smsVerificationCode = new SMSVerificationCode(String.valueOf(randNumber));
+            String jsonString = JSON.toJSONString(smsVerificationCode);
+            sendSms.sendSms(phone, SendSmsEnum.SMS_220650024, jsonString);
+            return Result.ok("发送成功");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return Result.ok("发送失败");
+    }
+
 }

+ 86 - 0
nightFragrance-massage/src/main/java/com/ylx/useradress/domain/dto/UserAddressAddDto.java

@@ -0,0 +1,86 @@
+package com.ylx.useradress.domain.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 类描述:用户地址管理新增dto
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/6 9:12
+ */
+@Data
+public class UserAddressAddDto {
+    /**
+     * TwxUser的主键用户id
+     */
+    @NotBlank(message = "用户id不能为空")
+    @ApiModelProperty("TwxUser的主键用户id")
+    private String userId;
+    /**
+     * 性别(0女1男)
+     */
+    @NotNull(message = "性别不能为空")
+    @TableField("sex")
+    private Integer sex;
+    /**
+     * 用户openId
+     */
+    @NotBlank(message = "用户openId不能为空")
+    @ApiModelProperty("用户openId")
+    private String openid;
+
+    /**
+     * 城市编码
+     */
+    @NotBlank(message = "城市编码不能为空")
+    @ApiModelProperty("城市编码")
+    private String cityCode;
+    //电话
+    @NotBlank(message = "电话不能为空")
+    @ApiModelProperty("电话")
+    private String phone;
+    /**
+     * 验证码
+     */
+    @NotBlank(message = "验证码不能为空")
+    @ApiModelProperty(value = "验证码")
+    private String phoneMsg;
+    /**
+     * 前端直接传入输入的姓氏
+     */
+    @NotBlank(message = "姓氏不能为空")
+    @ApiModelProperty("姓氏")
+    private String userName;
+
+    //经度
+    @NotNull(message = "经度不能为空")
+    @ApiModelProperty("经度")
+    private Double longitude;
+    //纬度
+    @NotNull(message = "纬度不能为空")
+    @ApiModelProperty("纬度")
+    private Double latitude;
+
+    //地址
+    @NotBlank(message = "地址不能为空")
+    @ApiModelProperty("地址")
+    private String address;
+
+    //门牌号
+    @NotBlank(message = "门牌号不能为空")
+    @ApiModelProperty("门牌号")
+    private String roomNumber;
+
+    /**
+     * 地址是否默认 1:默认 0:非默认
+     */
+    @NotNull(message = "地址是否默认不能为空")
+    @ApiModelProperty("地址是否默认 1:默认 0:非默认")
+    private Integer isDefault;
+}

+ 31 - 0
nightFragrance-massage/src/main/java/com/ylx/useradress/domain/dto/UserAddressDeleteDto.java

@@ -0,0 +1,31 @@
+package com.ylx.useradress.domain.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 类描述:用户地址管理删除dto
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/6 9:12
+ */
+@Data
+public class UserAddressDeleteDto {
+    //地址id
+    @NotBlank(message = "地址id不能为空")
+    @ApiModelProperty("地址id")
+    private String id;
+    /**
+     * TwxUser的主键用户id
+     */
+    @NotBlank(message = "用户id不能为空")
+    @ApiModelProperty("TwxUser的主键用户id")
+    private String userId;
+
+
+}

+ 79 - 0
nightFragrance-massage/src/main/java/com/ylx/useradress/domain/dto/UserAddressUpdateDto.java

@@ -0,0 +1,79 @@
+package com.ylx.useradress.domain.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 类描述:用户地址管理编辑dto
+ *
+ * @author Administrator
+ * @version 1.0
+ * @date 2026/6/6 9:12
+ */
+@Data
+public class UserAddressUpdateDto {
+    //地址id
+    @NotBlank(message = "地址id不能为空")
+    @ApiModelProperty("地址id")
+    private String id;
+    /**
+     * TwxUser的主键用户id
+     */
+    @NotBlank(message = "用户id不能为空")
+    @ApiModelProperty("TwxUser的主键用户id")
+    private String userId;
+    /**
+     * 性别(0女1男)
+     */
+    @NotNull(message = "性别不能为空")
+    @TableField("sex")
+    private Integer sex;
+
+    /**
+     * 城市编码
+     */
+    @NotBlank(message = "城市编码不能为空")
+    @ApiModelProperty("城市编码")
+    private String cityCode;
+    //电话
+    @NotBlank(message = "电话不能为空")
+    @ApiModelProperty("电话")
+    private String phone;
+    /**
+     * 验证码
+     */
+    @NotBlank(message = "验证码不能为空")
+    @ApiModelProperty(value = "验证码")
+    private String phoneMsg;
+
+    /**
+     * 前端直接传入输入的姓氏
+     */
+    @NotBlank(message = "姓氏不能为空")
+    @ApiModelProperty("姓氏")
+    private String userName;
+
+    //经度
+    @NotNull(message = "经度不能为空")
+    @ApiModelProperty("经度")
+    private Double longitude;
+    //纬度
+    @NotNull(message = "纬度不能为空")
+    @ApiModelProperty("纬度")
+    private Double latitude;
+
+    //地址
+    @NotBlank(message = "地址不能为空")
+    @ApiModelProperty("地址")
+    private String address;
+
+    //门牌号
+    @NotBlank(message = "门牌号不能为空")
+    @ApiModelProperty("门牌号")
+    private String roomNumber;
+
+}

+ 25 - 0
nightFragrance-massage/src/main/java/com/ylx/useradress/domain/vo/UserAddressVo.java

@@ -1,6 +1,8 @@
 package com.ylx.useradress.domain.vo;
 
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
 
 
 /**
@@ -10,12 +12,22 @@ import io.swagger.annotations.ApiModelProperty;
  * @version 1.0
  * @date 2026/6/5 17:16
  */
+@Data
 public class UserAddressVo {
 
+    //地址id
+    @ApiModelProperty("地址id")
+    private String id;
     //电话
     @ApiModelProperty("电话")
     private String phone;
 
+    /**
+     * TwxUser的主键用户id
+     */
+    @ApiModelProperty("TwxUser的主键用户id")
+    private String userId;
+
     /**
      * 姓名
      */
@@ -32,9 +44,22 @@ public class UserAddressVo {
     private Double latitude;
     //详细地址
     @ApiModelProperty("详细地址")
+    private String detailAddress;
+    //地址
+    @ApiModelProperty("地址")
     private String address;
 
+    //门牌号
+    @ApiModelProperty("门牌号")
+    private String roomNumber;
+
     @ApiModelProperty("地址name")
     private String name;
 
+    /**
+     * 地址是否默认 1:默认 0:非默认
+     */
+    @ApiModelProperty("地址是否默认 1:默认 0:非默认")
+    private Integer isDefault;
+
 }

+ 4 - 0
nightFragrance-massage/src/main/resources/mapper/massage/TAddressMapper.xml

@@ -5,6 +5,7 @@
     <resultMap type="com.ylx.massage.domain.TAddress" id="TAddressMap">
         <result property="id" column="id" jdbcType="VARCHAR"/>
         <result property="openid" column="openid" jdbcType="VARCHAR"/>
+        <result property="userId" column="user_id" jdbcType="VARCHAR"/>
         <result property="cityCode" column="city_code" jdbcType="VARCHAR"/>
         <result property="phone" column="phone" jdbcType="VARCHAR"/>
         <result property="userName" column="user_name" jdbcType="VARCHAR"/>
@@ -12,8 +13,11 @@
         <result property="longitude" column="longitude" jdbcType="NUMERIC"/>
         <result property="latitude" column="latitude" jdbcType="NUMERIC"/>
         <result property="type" column="type" jdbcType="INTEGER"/>
+        <result property="isDefault" column="type" jdbcType="INTEGER"/>
         <result property="sex" column="sex" jdbcType="INTEGER"/>
         <result property="address" column="address" jdbcType="VARCHAR"/>
+        <result property="detailAddress" column="detail_address" jdbcType="VARCHAR"/>
+        <result property="roomNumber" column="room_number" jdbcType="VARCHAR"/>
         <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
         <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
         <result property="isDelete" column="is_delete" jdbcType="INTEGER"/>