Browse Source

计费规则

loemkie 4 weeks ago
parent
commit
a97b6b151c
1 changed files with 184 additions and 0 deletions
  1. 184 0
      src/main/java/com/qmrb/parking/fee/ParkingFeeCalculator.java

+ 184 - 0
src/main/java/com/qmrb/parking/fee/ParkingFeeCalculator.java

@@ -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();
+    }
+}