| New file |
| | |
| | | package com.oying.modules.sh.utils; |
| | | |
| | | import com.oying.utils.BigDecimalUtils; |
| | | import com.oying.utils.StringUtils; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * @author xin |
| | | * @description 运费计算器 |
| | | * @date 2025/9/27 21:08 |
| | | */ |
| | | public class ShippingFeeCalculator { |
| | | |
| | | // 商品类型枚举 |
| | | public enum ProductType { |
| | | FOOD, // 食品小吃、饮料、商超 |
| | | SPECIAL // 鲜花、蛋糕、电商、高端餐饮 |
| | | } |
| | | |
| | | // 城市等级枚举 |
| | | public enum CityLevel { |
| | | C, D, E |
| | | } |
| | | |
| | | // 基础运费配置 |
| | | private static class BaseFeeConfig { |
| | | private final double foodFee; |
| | | private final double specialFee; |
| | | |
| | | public BaseFeeConfig(double foodFee, double specialFee) { |
| | | this.foodFee = foodFee; |
| | | this.specialFee = specialFee; |
| | | } |
| | | |
| | | public double getFee(ProductType type) { |
| | | return type == ProductType.FOOD ? foodFee : specialFee; |
| | | } |
| | | } |
| | | |
| | | // 各等级对应的基础运费 |
| | | private static final Map<CityLevel, BaseFeeConfig> LEVEL_FEES = new HashMap<>(); |
| | | |
| | | static { |
| | | LEVEL_FEES.put(CityLevel.C, new BaseFeeConfig(5.2, 7.2)); |
| | | LEVEL_FEES.put(CityLevel.D, new BaseFeeConfig(4.7, 6.7)); |
| | | LEVEL_FEES.put(CityLevel.E, new BaseFeeConfig(4.5, 6.0)); |
| | | } |
| | | |
| | | // 城市到等级的映射 |
| | | private static final Map<String, CityLevel> CITY_LEVELS = new HashMap<>(); |
| | | |
| | | static { |
| | | // C等级城市 |
| | | CITY_LEVELS.put("西安市", CityLevel.C); |
| | | CITY_LEVELS.put("福州市", CityLevel.C); |
| | | CITY_LEVELS.put("上海市", CityLevel.C); |
| | | CITY_LEVELS.put("哈尔滨", CityLevel.C); |
| | | CITY_LEVELS.put("杭州市", CityLevel.C); |
| | | CITY_LEVELS.put("北京市", CityLevel.C); |
| | | CITY_LEVELS.put("长春市", CityLevel.C); |
| | | CITY_LEVELS.put("珠海市", CityLevel.C); |
| | | CITY_LEVELS.put("重庆市", CityLevel.C); |
| | | CITY_LEVELS.put("舟山市", CityLevel.C); |
| | | |
| | | // D等级城市 |
| | | CITY_LEVELS.put("郑州市", CityLevel.D); |
| | | CITY_LEVELS.put("大连市", CityLevel.D); |
| | | CITY_LEVELS.put("天津市", CityLevel.D); |
| | | CITY_LEVELS.put("长沙市", CityLevel.D); |
| | | CITY_LEVELS.put("中山市", CityLevel.D); |
| | | CITY_LEVELS.put("东莞市", CityLevel.D); |
| | | CITY_LEVELS.put("广州市", CityLevel.D); |
| | | CITY_LEVELS.put("贵阳市", CityLevel.D); |
| | | CITY_LEVELS.put("兰州市", CityLevel.D); |
| | | CITY_LEVELS.put("温州市", CityLevel.D); |
| | | CITY_LEVELS.put("厦门市", CityLevel.D); |
| | | CITY_LEVELS.put("惠州市", CityLevel.D); |
| | | CITY_LEVELS.put("深圳市", CityLevel.D); |
| | | CITY_LEVELS.put("青岛市", CityLevel.D); |
| | | |
| | | // E等级城市 |
| | | CITY_LEVELS.put("邯郸市", CityLevel.E); |
| | | CITY_LEVELS.put("宿迁市", CityLevel.E); |
| | | CITY_LEVELS.put("湖州市", CityLevel.E); |
| | | CITY_LEVELS.put("绍兴市", CityLevel.E); |
| | | CITY_LEVELS.put("金华市", CityLevel.E); |
| | | CITY_LEVELS.put("衢州市", CityLevel.E); |
| | | CITY_LEVELS.put("台州市", CityLevel.E); |
| | | CITY_LEVELS.put("丽水市", CityLevel.E); |
| | | CITY_LEVELS.put("阜阳市", CityLevel.E); |
| | | CITY_LEVELS.put("三明市", CityLevel.E); |
| | | CITY_LEVELS.put("南平市", CityLevel.E); |
| | | CITY_LEVELS.put("龙岩市", CityLevel.E); |
| | | CITY_LEVELS.put("宁德市", CityLevel.E); |
| | | CITY_LEVELS.put("淄博市", CityLevel.E); |
| | | CITY_LEVELS.put("济宁市", CityLevel.E); |
| | | CITY_LEVELS.put("威海市", CityLevel.E); |
| | | CITY_LEVELS.put("日照市", CityLevel.E); |
| | | CITY_LEVELS.put("十堰市", CityLevel.E); |
| | | CITY_LEVELS.put("衡阳市", CityLevel.E); |
| | | CITY_LEVELS.put("泸州市", CityLevel.E); |
| | | CITY_LEVELS.put("绵阳市", CityLevel.E); |
| | | CITY_LEVELS.put("泰安市", CityLevel.E); |
| | | CITY_LEVELS.put("廊坊市", CityLevel.E); |
| | | } |
| | | |
| | | // 加价规则常量 |
| | | private static final double BASE_DISTANCE = 1.0; // 起步距离1公里 |
| | | private static final double BASE_WEIGHT = 5.0; // 起步重量5公斤 |
| | | private static final double DISTANCE_RATE = 1.0; // 距离加价:每公里1元 |
| | | private static final double WEIGHT_RATE = 2.0; // 重量加价:每公斤2元 |
| | | |
| | | /** |
| | | * 计算订单运费 |
| | | * |
| | | * @param city 城市名称 |
| | | * @param productType 商品类型 |
| | | * @param distance 配送距离(公里) |
| | | * @param weight 商品重量(公斤) |
| | | * @param isPeakHour 是否午高峰时段 |
| | | * @return 总运费 |
| | | */ |
| | | public static double calculateShippingFee(String city, ProductType productType, |
| | | double distance, double weight, boolean isPeakHour) { |
| | | if (StringUtils.isEmpty(city)) { |
| | | city = "重庆市"; |
| | | } |
| | | // 获取城市等级 |
| | | CityLevel level = CITY_LEVELS.get(city); |
| | | |
| | | // 基础运费 |
| | | double baseFee = LEVEL_FEES.get(level).getFee(productType); |
| | | |
| | | // 距离加价(超过1公里部分,每公里加1元) |
| | | double distanceFee = 0; |
| | | if (distance > BASE_DISTANCE) { |
| | | double extraDistance = Math.min(distance - BASE_DISTANCE, 2.0); // 1-3公里内加价 |
| | | distanceFee = Math.ceil(extraDistance) * DISTANCE_RATE; |
| | | } |
| | | |
| | | // 重量加价(超过5公斤部分,每公斤加2元) |
| | | double weightFee = 0; |
| | | if (weight > BASE_WEIGHT) { |
| | | double extraWeight = weight - BASE_WEIGHT; |
| | | weightFee = Math.ceil(extraWeight) * WEIGHT_RATE; |
| | | } |
| | | |
| | | // 时段加价(午高峰时段,具体加价金额需补充) |
| | | double peakFee = isPeakHour ? getPeakHourFee() : 0; |
| | | |
| | | return baseFee + distanceFee + weightFee + peakFee; |
| | | } |
| | | |
| | | /** |
| | | * 获取午高峰时段加价费用(需要根据具体规则补充) |
| | | */ |
| | | private static double getPeakHourFee() { |
| | | // 根据实际午高峰加价规则返回具体金额 |
| | | return 0.0; // 暂设为0,需补充具体规则 |
| | | } |
| | | |
| | | /** |
| | | * 获取城市运费等级信息 |
| | | */ |
| | | public static String getCityLevelInfo(String city) { |
| | | if (StringUtils.isEmpty(city)) { |
| | | city = "重庆市"; |
| | | } |
| | | CityLevel level = CITY_LEVELS.get(city); |
| | | |
| | | BaseFeeConfig config = LEVEL_FEES.get(level); |
| | | return String.format("城市: %s, 等级: %s, 食品类起步费: %.1f元, 特殊类起步费: %.1f元", |
| | | city, level, config.foodFee, config.specialFee); |
| | | } |
| | | |
| | | /** |
| | | * 重量换算kg |
| | | * |
| | | * @param grams 重量 单位:g |
| | | * @return double |
| | | */ |
| | | public static double safeConvert(Integer grams) { |
| | | if (grams == null || grams < 0) { |
| | | return 0.0; // 返回默认值 |
| | | } |
| | | return grams / 1000.0; |
| | | } |
| | | |
| | | // 测试示例 |
| | | public static void main(String[] args) { |
| | | // 测试不同城市的运费计算 |
| | | System.out.println("=== 运费计算测试 ==="); |
| | | double distance = 9.00; |
| | | double width = safeConvert(1500); |
| | | String[] testCities = {null, "", "淄博市", "天津市"}; |
| | | for (String city : testCities) { |
| | | System.out.println(getCityLevelInfo(city)); |
| | | // 计算示例订单 |
| | | double fee1 = calculateShippingFee(city, ProductType.FOOD, distance, width, false); |
| | | double fee2 = calculateShippingFee(city, ProductType.SPECIAL, distance, width, true); |
| | | System.out.printf(" 食品订单(" + distance + "km, " + width + "kg): %.1f元\n", fee1); |
| | | System.out.printf(" 特殊订单(" + distance + "km, " + width + "kg, 高峰): %.1f元\n", fee2); |
| | | System.out.println(); |
| | | } |
| | | } |
| | | } |