Преглед изворни кода

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

jinwenhai пре 5 дана
родитељ
комит
8a5da11b50

+ 38 - 0
nightFragrance-common/src/main/java/com/ylx/common/utils/DistanceUtil.java

@@ -0,0 +1,38 @@
+package com.ylx.common.utils;
+
+import cn.hutool.core.util.ObjectUtil;
+
+/**
+ * 计算经纬度之间的直线距离
+ */
+public class DistanceUtil {
+
+    /**
+     * @param userLat 用户纬度
+     * @param userLon 用户经度
+     * @param shopLat 门店纬度
+     * @param shopLon 门店经度
+     * @return 返回距离单位为m
+     */
+    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) {
+            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;
+    }
+
+}

+ 21 - 0
nightFragrance-massage/src/main/java/com/ylx/fareSetting/controller/MaProjectFareSettingController.java

@@ -1,13 +1,34 @@
 package com.ylx.fareSetting.controller;
 
+import com.ylx.common.core.domain.R;
+import com.ylx.fareSetting.domian.dto.FareCalculateDTO;
+import com.ylx.fareSetting.domian.vo.FareCalculateResultVO;
+import com.ylx.fareSetting.service.IMaProjectFareSettingService;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+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 javax.annotation.Resource;
+
 @RestController
 @RequestMapping("/project/fare/setting")
 @Api(tags = {"项目车费标准设置"})
 @Slf4j
 public class MaProjectFareSettingController {
+
+    @Resource
+    private IMaProjectFareSettingService maProjectFareSettingService;
+
+
+    @PostMapping("/calculate")
+    @ApiOperation("下单前计算打车费")
+    public R<FareCalculateResultVO> calculate(@Validated @RequestBody FareCalculateDTO dto) {
+        FareCalculateResultVO result = maProjectFareSettingService.calculateFare(dto);
+        return R.ok(result);
+    }
 }

+ 39 - 0
nightFragrance-massage/src/main/java/com/ylx/fareSetting/domian/dto/FareCalculateDTO.java

@@ -0,0 +1,39 @@
+package com.ylx.fareSetting.domian.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("车费计算请求DTO")
+public class FareCalculateDTO implements Serializable {
+    private static final long serialVersionUID = 1632111945634156891L;
+
+
+    @ApiModelProperty(value = "商户ID", required = true)
+    @NotNull(message = "商户ID不能为空")
+    private Long merchantId;
+
+    @ApiModelProperty(value = "项目/服务ID", required = true)
+    @NotNull(message = "项目ID不能为空")
+    private Long projectId;
+
+    @ApiModelProperty(value = "预约开始时间", required = true, example = "2024-01-07 15:30:00")
+    @NotNull(message = "预约时间不能为空")
+    private LocalDateTime appointmentStartTime;
+
+    @ApiModelProperty(value = "城市编码", required = true)
+    @NotNull(message = "城市编码")
+    private String cityCode;
+
+    @ApiModelProperty(value = "用户下单经度", required = true)
+    private Double longitude;
+
+    @ApiModelProperty(value = "用户下单纬度", required = true)
+    private Double latitude;
+
+}

+ 29 - 0
nightFragrance-massage/src/main/java/com/ylx/fareSetting/domian/vo/FareCalculateResultVO.java

@@ -0,0 +1,29 @@
+package com.ylx.fareSetting.domian.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import java.math.BigDecimal;
+
+@Data
+@ApiModel("车费计算结果")
+public class FareCalculateResultVO {
+
+    @ApiModelProperty("实际导航距离(公里)")
+    private BigDecimal actualDistanceKm;
+
+    @ApiModelProperty("免费里程额度(公里)")
+    private BigDecimal freeDistanceKm;
+
+    @ApiModelProperty("超出免费里程的距离(公里)")
+    private BigDecimal exceedDistanceKm;
+
+    @ApiModelProperty("预估打车费(元)")
+    private BigDecimal estimatedFare;
+
+    @ApiModelProperty("是否免车费")
+    private Boolean isFree;
+
+    @ApiModelProperty("提示信息,例如:在5公里范围内免车费")
+    private String message;
+}

+ 3 - 0
nightFragrance-massage/src/main/java/com/ylx/fareSetting/service/IMaProjectFareSettingService.java

@@ -2,6 +2,9 @@ package com.ylx.fareSetting.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ylx.fareSetting.domian.MaProjectFareSetting;
+import com.ylx.fareSetting.domian.dto.FareCalculateDTO;
+import com.ylx.fareSetting.domian.vo.FareCalculateResultVO;
 
 public interface IMaProjectFareSettingService extends IService<MaProjectFareSetting> {
+    FareCalculateResultVO calculateFare(FareCalculateDTO dto);
 }

+ 129 - 0
nightFragrance-massage/src/main/java/com/ylx/fareSetting/service/impl/MaProjectFareSettingServiceImpl.java

@@ -1,11 +1,140 @@
 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.exception.ServiceException;
 import com.ylx.fareSetting.domian.MaProjectFareSetting;
+import com.ylx.fareSetting.domian.dto.FareCalculateDTO;
+import com.ylx.fareSetting.domian.vo.FareCalculateResultVO;
 import com.ylx.fareSetting.mapper.MaProjectFareSettingMapper;
 import com.ylx.fareSetting.service.IMaProjectFareSettingService;
+import com.ylx.massage.domain.TAddress;
+import com.ylx.massage.domain.vo.TFareSettingVo;
+import com.ylx.massage.service.TAddressService;
+import com.ylx.massage.service.TFareSettingService;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalTime;
+import java.util.List;
+import java.util.Optional;
+
+
+@Slf4j
 @Service
 public class MaProjectFareSettingServiceImpl extends ServiceImpl<MaProjectFareSettingMapper, MaProjectFareSetting> implements IMaProjectFareSettingService {
+
+    @Resource
+    private TFareSettingService fareSettingService;
+    @Resource
+    private TAddressService addressService;
+
+    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));
+        if (ObjectUtil.isNull(address)) {
+            throw new ServiceException("无法获取商户的默认地址");
+        }
+
+        // 1. 获取商户车费配置
+        LambdaQueryWrapper<MaProjectFareSetting> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(MaProjectFareSetting::getMerchantId, dto.getMerchantId())
+                .eq(MaProjectFareSetting::getIsDelete, 0);
+        List<MaProjectFareSetting> list = 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);
+
+            } else {
+                // 【情况B】集合数量大于1 -> 不是统一配置(独立项目配置)
+                // 需要根据传入的 projectId 从列表中筛选出对应的项目配置
+                log.info("商户[{}]存在多条配置,根据projectId[{}]进行匹配", dto.getMerchantId(), dto.getProjectId());
+
+                // 使用 Stream API 过滤出当前项目对应的配置
+                Optional<MaProjectFareSetting> projectConfigOpt = list.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));
+//        }
+
+        return result;
+    }
 }