|
@@ -0,0 +1,184 @@
|
|
|
+package com.qmrb.parking.fee;
|
|
|
+
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.LocalTime;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+public class ParkingFeeCalculator {
|
|
|
+
|
|
|
+ // 计费规则类
|
|
|
+ public static class FeeRule {
|
|
|
+ public int freeDurationMinutes; // 免费时长(分钟)
|
|
|
+ public double firstCycleFee; // 首周期费用
|
|
|
+ public int firstCycleDurationMinutes; // 首周期时长(分钟)
|
|
|
+ public int cycleDurationMinutes; // 计费周期时长(分钟)
|
|
|
+ public double cycleFee; // 每个计费周期的费用
|
|
|
+ public double dailyCapFee; // 24 小时封顶费用
|
|
|
+ public int gracePeriodMinutes; // 离场时长宽容度(分钟)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 停车记录类
|
|
|
+ public static class ParkingRecord {
|
|
|
+ public LocalDateTime entryTime; // 入场时间
|
|
|
+ public LocalDateTime exitTime; // 离场时间
|
|
|
+ public LocalDateTime monthlyCardExpiryTime; // 月卡到期时间
|
|
|
+ public double paidAmount; // 已支付金额
|
|
|
+ public List<PaymentRecord> paymentRecords = new ArrayList<>(); // 支付记录
|
|
|
+ }
|
|
|
+
|
|
|
+ // 支付记录类
|
|
|
+ public static class PaymentRecord {
|
|
|
+ public LocalDateTime paymentTime; // 支付时间
|
|
|
+ public double amount; // 支付金额
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算停车费用
|
|
|
+ public static double calculateParkingFee(ParkingRecord parkingRecord, FeeRule feeRule, boolean isMonthlyCardMode) {
|
|
|
+ // 1. 确定计费开始时间
|
|
|
+ LocalDateTime billingStartTime = parkingRecord.entryTime;
|
|
|
+ if (parkingRecord.monthlyCardExpiryTime != null && parkingRecord.monthlyCardExpiryTime.isAfter(parkingRecord.entryTime)) {
|
|
|
+ billingStartTime = parkingRecord.monthlyCardExpiryTime;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 确定计费结束时间
|
|
|
+ LocalDateTime billingEndTime = isMonthlyCardMode
|
|
|
+ ? LocalDateTime.of(parkingRecord.exitTime.toLocalDate(), LocalTime.MIN) // 办卡清费模式
|
|
|
+ : parkingRecord.exitTime; // 临停收费模式
|
|
|
+
|
|
|
+ // 3. 计算计费时长
|
|
|
+ Duration billingDuration = Duration.between(billingStartTime, billingEndTime);
|
|
|
+
|
|
|
+ // 4. 检查离场时长宽容度
|
|
|
+ if (feeRule.gracePeriodMinutes > 0) {
|
|
|
+ for (PaymentRecord payment : parkingRecord.paymentRecords) {
|
|
|
+ if (payment.paymentTime.isAfter(billingEndTime.minusMinutes(feeRule.gracePeriodMinutes))) {
|
|
|
+ billingDuration = billingDuration.minusMinutes(feeRule.gracePeriodMinutes);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 判断是否在免费时长内
|
|
|
+ if (billingDuration.toMinutes() <= feeRule.freeDurationMinutes) {
|
|
|
+ return 0.0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 计算费用
|
|
|
+ double totalFee = 0.0;
|
|
|
+
|
|
|
+ // 6.1 超过 24 小时的部分
|
|
|
+ if (billingDuration.toHours() >= 24) {
|
|
|
+ int fullDays = (int) (billingDuration.toHours() / 24);
|
|
|
+ totalFee += fullDays * feeRule.dailyCapFee;
|
|
|
+
|
|
|
+ // 剩余不足 24 小时的部分
|
|
|
+ Duration remainingDuration = billingDuration.minusHours(fullDays * 24);
|
|
|
+ double remainingFee = calculateRemainingFeeDue(remainingDuration, feeRule);
|
|
|
+ totalFee += Math.min(remainingFee, feeRule.dailyCapFee);
|
|
|
+ }
|
|
|
+ // 6.2 不超过 24 小时的部分
|
|
|
+ else {
|
|
|
+ totalFee = calculateRemainingFee(billingDuration, feeRule);
|
|
|
+ totalFee = Math.min(totalFee, feeRule.dailyCapFee);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 扣除已支付金额
|
|
|
+ totalFee = Math.max(0, totalFee - parkingRecord.paidAmount);
|
|
|
+
|
|
|
+ return totalFee;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算不足 24 小时的费用
|
|
|
+ private static double calculateRemainingFee(Duration duration, FeeRule feeRule) {
|
|
|
+ double totalFee = 0.0;
|
|
|
+
|
|
|
+ // 扣除首周期时长
|
|
|
+ Duration remainingDuration = duration.minusMinutes(feeRule.firstCycleDurationMinutes);
|
|
|
+ totalFee += feeRule.firstCycleFee;
|
|
|
+
|
|
|
+ // 计算剩余周期的费用
|
|
|
+ if (remainingDuration.toMinutes() > 0) {
|
|
|
+ int cycles = (int) Math.ceil((double) remainingDuration.toMinutes() / feeRule.cycleDurationMinutes);
|
|
|
+ totalFee += cycles * feeRule.cycleFee;
|
|
|
+ }
|
|
|
+
|
|
|
+ return totalFee;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 超过24小时的剩余不足24小时部分
|
|
|
+ * @param duration
|
|
|
+ * @param feeRule
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static double calculateRemainingFeeDue(Duration duration, FeeRule feeRule) {
|
|
|
+ double totalFee = 0.0;
|
|
|
+ // 计算剩余周期的费用
|
|
|
+ if (duration.toMinutes() > 0) {
|
|
|
+ int cycles = (int) Math.ceil((double) duration.toMinutes() / feeRule.cycleDurationMinutes);
|
|
|
+ totalFee += cycles * feeRule.cycleFee;
|
|
|
+ }
|
|
|
+
|
|
|
+ return totalFee;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* * 场景2:设【免费时长】=10分钟;【24小时收费金额】=50元;【首周期计费时长】=120分钟;【首周期计费金额】=20元;【计费周期】=30分钟;【每周期计费金额】=5元
|
|
|
+ */
|
|
|
+ public static void e2(){
|
|
|
+ // 示例计费规则
|
|
|
+ FeeRule feeRule = new FeeRule();
|
|
|
+ feeRule.freeDurationMinutes = 10;
|
|
|
+ feeRule.firstCycleFee = 20.0;
|
|
|
+ feeRule.firstCycleDurationMinutes = 120;//首周期计费时长
|
|
|
+ feeRule.cycleDurationMinutes = 30;//计费周期
|
|
|
+ feeRule.cycleFee = 5.0;//每周期计费金额
|
|
|
+ feeRule.dailyCapFee = 50.0;//24小时封顶费用
|
|
|
+ feeRule.gracePeriodMinutes = 15;
|
|
|
+
|
|
|
+ // 示例停车记录
|
|
|
+ ParkingRecord parkingRecord = new ParkingRecord();
|
|
|
+ parkingRecord.entryTime = LocalDateTime.of(2025, 3, 4, 0, 0);
|
|
|
+ parkingRecord.exitTime = LocalDateTime.of(2025, 3, 5, 0, 16);
|
|
|
+ parkingRecord.monthlyCardExpiryTime = null;
|
|
|
+ parkingRecord.paidAmount = 0.0;//已支付金额
|
|
|
+
|
|
|
+ // 计算费用
|
|
|
+ double fee = calculateParkingFee(parkingRecord, feeRule, false);
|
|
|
+ System.out.println("停车费用: " + fee + " 元");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设【免费时长】=0;【24小时收费金额】=30元;【首周期计费时长】=60分钟;【首周期计费金额】=20元;【计费周期】=30分钟;【每周期计费金额】=2元
|
|
|
+ */
|
|
|
+ public static void e1(){
|
|
|
+ // 示例计费规则
|
|
|
+ FeeRule feeRule = new FeeRule();
|
|
|
+ feeRule.freeDurationMinutes = 0;//免费时长
|
|
|
+ feeRule.firstCycleFee = 20.0;//首周期计费金额
|
|
|
+ feeRule.firstCycleDurationMinutes = 60;//首周期计费时长
|
|
|
+ feeRule.cycleDurationMinutes = 30;//计费周期
|
|
|
+ feeRule.cycleFee = 2.0;//每周期计费金额
|
|
|
+ feeRule.dailyCapFee = 30.0;//24小时封顶费用
|
|
|
+ feeRule.gracePeriodMinutes = 15;
|
|
|
+
|
|
|
+ // 示例停车记录
|
|
|
+ ParkingRecord parkingRecord = new ParkingRecord();
|
|
|
+ parkingRecord.entryTime = LocalDateTime.of(2025, 3, 3, 0, 0);
|
|
|
+ parkingRecord.exitTime = LocalDateTime.of(2025, 3, 8, 10, 0);
|
|
|
+ parkingRecord.monthlyCardExpiryTime = null;
|
|
|
+ parkingRecord.paidAmount = 0.0;//已支付金额
|
|
|
+
|
|
|
+ // 计算费用
|
|
|
+ double fee = calculateParkingFee(parkingRecord, feeRule, false);
|
|
|
+ System.out.println("停车费用: " + fee + " 元");
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * @param args
|
|
|
+ */
|
|
|
+ public static void main(String[] args) {
|
|
|
+ e1();
|
|
|
+// e2();
|
|
|
+ }
|
|
|
+}
|