package com.oying.modules.sh.utils;
|
|
import java.time.LocalTime;
|
import java.util.HashMap;
|
import java.util.Map;
|
|
public class ShippingFeeCalculator {
|
|
// 城市等级映射
|
private static final Map<String, String> CITY_LEVEL_MAP = new HashMap<>();
|
|
// 等级基础运费映射 (普通品类, 特殊品类)
|
private static final Map<String, Double[]> LEVEL_BASE_FEE_MAP = new HashMap<>();
|
|
// 高峰时段及加价规则
|
private static final Object[][] PEAK_HOURS_RULES = {
|
{LocalTime.of(0, 0), LocalTime.of(6, 0), 4.0}, // 00:00-06:00 加价4元
|
{LocalTime.of(11, 0), LocalTime.of(13, 0), 2.0}, // 11:00-13:00 加价2元
|
{LocalTime.of(21, 0), LocalTime.of(23, 59), 3.0} // 21:00-23:59 加价3元
|
};
|
|
// 特殊品类
|
private static final String[] SPECIAL_CATEGORIES = {
|
"鲜花", "蛋糕", "电商", "高端餐饮"
|
};
|
|
static {
|
// 初始化城市等级映射
|
initializeCityLevelMap();
|
|
// 初始化等级基础运费
|
initializeLevelBaseFee();
|
}
|
|
private static void initializeCityLevelMap() {
|
// C等级城市
|
String[] levelCCities = {"西安", "福州", "上海", "哈尔滨", "杭州", "北京", "长春", "珠海", "重庆", "舟山"};
|
for (String city : levelCCities) {
|
CITY_LEVEL_MAP.put(city, "C");
|
}
|
|
// D等级城市
|
String[] levelDCities = {"郑州", "大连", "天津", "长沙", "中山", "东莞", "广州", "贵阳", "兰州", "温州",
|
"厦门", "惠州", "深圳"};
|
for (String city : levelDCities) {
|
CITY_LEVEL_MAP.put(city, "D");
|
}
|
|
// E等级城市
|
String[] levelECities = {"青岛", "邯郸", "宿迁", "湖州", "绍兴", "金华", "衢州", "台州", "丽水", "阜阳",
|
"三明", "南平", "龙岩", "宁德", "淄博", "济宁", "威海", "十堰", "泸州", "绵阳",
|
"德阳", "汉阳", "襄阳", "武汉", "常德", "沈阳"};
|
for (String city : levelECities) {
|
CITY_LEVEL_MAP.put(city, "E");
|
}
|
}
|
|
private static void initializeLevelBaseFee() {
|
// 等级C: 普通品类5.2元,特殊品类6.7元
|
LEVEL_BASE_FEE_MAP.put("C", new Double[]{5.8, 7.9});
|
// 等级D: 普通品类4.7元,特殊品类6.7元
|
LEVEL_BASE_FEE_MAP.put("D", new Double[]{5.2, 7.3});
|
// 等级E: 普通品类4.5元,特殊品类6.0元
|
LEVEL_BASE_FEE_MAP.put("E", new Double[]{5.0, 6.6});
|
}
|
|
/**
|
* 计算运费
|
*
|
* @param city 城市名称
|
* @param category 商品品类
|
* @param weight 重量(公斤)
|
* @param distance 距离(公里)
|
* @param orderTime 下单时间
|
* @param isSpecialConditions 是否特殊条件(恶劣天气、运力紧张、特殊日期)
|
* @return 总运费
|
*/
|
public static double calculateShippingFee(String city, String category,
|
double weight, double distance,
|
LocalTime orderTime, boolean isSpecialConditions) {
|
double totalFee = 0.0;
|
|
// 1. 基础运费
|
totalFee += calculateBaseFee(city, category);
|
|
// 2. 重量加价
|
totalFee += calculateWeightSurcharge(weight);
|
|
// 3. 距离加价
|
totalFee += calculateDistanceSurcharge(distance);
|
|
// 4. 时段加价
|
totalFee += calculateTimeSurcharge(orderTime);
|
|
// 5. 特殊条件加价(需要外部传入具体加价金额,这里返回基础值)
|
if (isSpecialConditions) {
|
totalFee += 1.0; // 默认加价1元,实际应根据具体规则调整
|
}
|
|
return totalFee;
|
}
|
|
/**
|
* 计算基础运费
|
*/
|
private static double calculateBaseFee(String city, String category) {
|
String cityLevel = CITY_LEVEL_MAP.getOrDefault(city, "E"); // 默认E等级
|
Double[] fees = LEVEL_BASE_FEE_MAP.get(cityLevel);
|
|
if (fees == null) {
|
fees = LEVEL_BASE_FEE_MAP.get("E"); // 默认使用E等级
|
}
|
|
// 判断是否为特殊品类
|
boolean isSpecialCategory = false;
|
for (String specialCat : SPECIAL_CATEGORIES) {
|
if (category.contains(specialCat)) {
|
isSpecialCategory = true;
|
break;
|
}
|
}
|
|
return isSpecialCategory ? fees[1] : fees[0];
|
}
|
|
/**
|
* 计算重量加价
|
*/
|
private static double calculateWeightSurcharge(double weight) {
|
final double BASE_WEIGHT = 5.0; // 起步重量5公斤
|
final double SURCHARGE_PER_KG = 2.0; // 每公斤加价2元
|
|
if (weight <= BASE_WEIGHT) {
|
return 0.0;
|
}
|
|
double excessWeight = weight - BASE_WEIGHT;
|
return Math.ceil(excessWeight) * SURCHARGE_PER_KG;
|
}
|
|
/**
|
* 计算距离加价
|
*/
|
private static double calculateDistanceSurcharge(double distance) {
|
double surcharge = 0.0;
|
|
// 分段计算加价
|
if (distance > 7.0) {
|
surcharge += (distance - 7.0) * 4.0; // 超过7km部分
|
distance = 7.0;
|
}
|
if (distance > 5.0) {
|
surcharge += (distance - 5.0) * 3.0; // 5-7km部分
|
distance = 5.0;
|
}
|
if (distance > 3.0) {
|
surcharge += (distance - 3.0) * 2.0; // 3-5km部分
|
distance = 3.0;
|
}
|
if (distance > 1.0) {
|
surcharge += (distance - 1.0); // 1-3km部分
|
// 0-1km部分不加价
|
}
|
|
return surcharge;
|
}
|
|
/**
|
* 计算时段加价
|
*/
|
private static double calculateTimeSurcharge(LocalTime orderTime) {
|
for (Object[] rule : PEAK_HOURS_RULES) {
|
LocalTime startTime = (LocalTime) rule[0];
|
LocalTime endTime = (LocalTime) rule[1];
|
double surcharge = (Double) rule[2];
|
|
if (!orderTime.isBefore(startTime) && !orderTime.isAfter(endTime)) {
|
return surcharge;
|
}
|
}
|
return 0.0;
|
}
|
|
/**
|
* 使用示例 - 优化版
|
*/
|
public static void main(String[] args) {
|
System.out.println(
|
"等级C: 普通品类5.8元,特殊品类7.9元\n" +
|
"等级D: 普通品类5.2元,特殊品类7.3元\n" +
|
"等级E: 普通品类5.0元,特殊品类6.6元"
|
);
|
// 测试用例数组
|
Object[][] testCases = {
|
// 城市, 品类, 重量(kg), 距离(km), 时间, 特殊条件, 预期运费描述
|
{"上海", "食品小吃", 7.0, 3.5, LocalTime.of(14, 30), false, "普通商品,超重,中距离,非高峰"},
|
{"北京", "蛋糕", 10.0, 8.0, LocalTime.of(12, 30), false, "特殊商品,超重,长距离,高峰"},
|
{"青岛", "饮料", 3.0, 2.0, LocalTime.of(10, 0), false, "普通商品,未超重,中距离,非高峰"},
|
{"深圳", "高端餐饮", 5.0, 0.5, LocalTime.of(5, 30), true, "特殊商品,临界重量,短距离,高峰+特殊"},
|
{"武汉", "普通商品", 15.0, 6.0, LocalTime.of(20, 0), false, "普通商品,超重,中距离,高峰"},
|
{"西安", "电商", 2.0, 10.0, LocalTime.of(15, 0), true, "特殊商品,未超重,长距离,特殊"},
|
{"广州", "普通商品", 5.0, 1.0, LocalTime.of(12, 0), false, "普通商品,临界重量,临界距离,高峰"},
|
{"沈阳", "蛋糕", 8.0, 4.0, LocalTime.of(22, 30), true, "特殊商品,超重,中距离,高峰+特殊"},
|
{"杭州", "鲜花", 4.0, 7.0, LocalTime.of(9, 0), false, "特殊商品,未超重,临界距离,非高峰"},
|
{"重庆", "普通商品", 6.0, 3.0, LocalTime.of(0, 30), true, "普通商品,超重,临界距离,高峰+特殊"}
|
};
|
|
// 打印表头
|
System.out.println("+--------+------------+--------+--------+----------+--------------+----------+-----------------+");
|
System.out.println("| 测试ID | 城市 | 品类 | 重量(kg) | 距离(km) | 时间 | 特殊条件 | 运费(元) | 描述");
|
System.out.println("+--------+------------+--------+--------+----------+--------------+--------------+-----------------+");
|
|
for (int i = 0; i < testCases.length; i++) {
|
Object[] testCase = testCases[i];
|
String city = (String) testCase[0];
|
String category = (String) testCase[1];
|
double weight = (double) testCase[2];
|
double distance = (double) testCase[3];
|
LocalTime time = (LocalTime) testCase[4];
|
boolean special = (boolean) testCase[5];
|
String description = (String) testCase[6];
|
|
double fee = calculateShippingFee(city, category, weight, distance, time, special);
|
|
// 格式化输出
|
System.out.printf("| %-6d | %-8s | %-6s | %-6.1f | %-8.1f | %-12s | %-8s | %-8.2f | %s%n",
|
i + 1,
|
city + ":" + CITY_LEVEL_MAP.getOrDefault(city, "E"),
|
category,
|
weight,
|
distance,
|
time,
|
special ? "是" : "否",
|
fee,
|
description);
|
}
|
|
// 打印表尾
|
System.out.println("+--------+------------+--------+--------+----------+--------------+--------------+-----------------+");
|
|
|
// 边界值测试
|
System.out.println("\n边界值测试C等级城市:");
|
System.out.println("1. 重量边界(5kg): " + calculateShippingFee("上海", "普通", 5.0, 3.0, LocalTime.of(10, 0), false));
|
System.out.println("2. 距离边界(1km): " + calculateShippingFee("上海", "普通", 3.0, 1.0, LocalTime.of(10, 0), false));
|
System.out.println("3. 时间边界(06:00): " + calculateShippingFee("上海", "普通", 3.0, 3.0, LocalTime.of(6, 0), false));
|
}
|
}
|