|
@@ -1,14 +1,19 @@
|
|
|
package com.qmrb.parking.fee;
|
|
|
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
+import com.qmrb.system.pojo.entity.ContractPlaceNumberRel;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
|
-import java.math.BigDecimal;
|
|
|
import java.time.Duration;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.LocalTime;
|
|
|
+import java.time.ZoneId;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
@Slf4j
|
|
|
public class ParkingFeeCalculator {
|
|
|
/**
|
|
@@ -43,6 +48,8 @@ public class ParkingFeeCalculator {
|
|
|
|
|
|
public CouponRecord couponRecord = new CouponRecord(); // 优惠券记录
|
|
|
|
|
|
+ public String contractType;//合同类型 3包租协议
|
|
|
+
|
|
|
}
|
|
|
|
|
|
// 支付记录类
|
|
@@ -67,6 +74,7 @@ public class ParkingFeeCalculator {
|
|
|
// 3. 计算计费时长
|
|
|
Duration billingDuration = Duration.between(billingStartTime, billingEndTime);
|
|
|
|
|
|
+
|
|
|
// 4. 检查离场时长宽容度
|
|
|
/*if (feeRule.gracePeriodMinutes > 0) {
|
|
|
for (PaymentRecord payment : parkingRecord.paymentRecords) {
|
|
@@ -139,12 +147,13 @@ public class ParkingFeeCalculator {
|
|
|
if(totalFee<0){
|
|
|
return 0.0;
|
|
|
}
|
|
|
- } else if (StringUtils.equals(couponRecord.couponType,"2")) {//时长券
|
|
|
+ }
|
|
|
+ /*else if (StringUtils.equals(couponRecord.couponType,"2")) {//时长券
|
|
|
//用计算不足24小时的部分来计算,不区分首周期
|
|
|
double dicFee = calculateRemainingFeeDue(Duration.ofMinutes(new BigDecimal(couponRecord.denomination).intValue()), feeRule);
|
|
|
log.info("优惠金额: "+dicFee+"元");
|
|
|
return totalFee - dicFee;
|
|
|
- }
|
|
|
+ }*/
|
|
|
return totalFee;
|
|
|
}
|
|
|
// 计算不足 24 小时的费用
|
|
@@ -210,6 +219,40 @@ public class ParkingFeeCalculator {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 包租协议测试
|
|
|
+ */
|
|
|
+ public static void e3(){
|
|
|
+ // 示例计费规则
|
|
|
+ FeeRule feeRule = new FeeRule();
|
|
|
+ feeRule.freeDurationMinutes = 0;//免费时长
|
|
|
+ feeRule.firstCycleFee = 20.0;//首周期计费金额
|
|
|
+ feeRule.firstCycleDurationMinutes = 60;//首周期计费时长
|
|
|
+ feeRule.cycleDurationMinutes = 30;//计费周期
|
|
|
+ feeRule.cycleFee = 2;//每周期计费金额
|
|
|
+ feeRule.dailyCapFee = 30;//24小时封顶费用
|
|
|
+ feeRule.gracePeriodMinutes = 15;// 离场时长宽容度(分钟)
|
|
|
+
|
|
|
+ // 示例停车记录
|
|
|
+ ParkingRecord parkingRecord = new ParkingRecord();
|
|
|
+ parkingRecord.entryTime = LocalDateTime.of(2025, 3, 28, 14, 0);
|
|
|
+ parkingRecord.exitTime = LocalDateTime.of(2025, 3, 29, 15, 50);
|
|
|
+ parkingRecord.monthlyCardExpiryTime = null;
|
|
|
+ /*parkingRecord.paidAmount = 0.01;//已支付金额
|
|
|
+ parkingRecord.paymentRecords=new ArrayList<>();*/
|
|
|
+ /*PaymentRecord paymentRecord = new PaymentRecord();
|
|
|
+ paymentRecord.paymentTime = LocalDateTime.of(2025, 3, 14, 10, 0);
|
|
|
+ parkingRecord.paymentRecords.add(paymentRecord);*/
|
|
|
+ List<ContractPlaceNumberRel> registrations = new ArrayList<>();
|
|
|
+ ContractPlaceNumberRel placeNumberRel = new ContractPlaceNumberRel();
|
|
|
+ placeNumberRel.setStartTime(DateUtil.parse("2025-03-28 12:00:00"));
|
|
|
+ placeNumberRel.setEndTime(DateUtil.parse(" 2025-03-29 14:00:00"));
|
|
|
+ registrations.add(placeNumberRel);
|
|
|
+ // 计算费用
|
|
|
+ Map<String, Object> result = calculateParkingFee(parkingRecord, feeRule, registrations);
|
|
|
+ System.out.println("停车费用: " + result.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 设【免费时长】=0;【24小时收费金额】=30元;【首周期计费时长】=60分钟;【首周期计费金额】=20元;【计费周期】=30分钟;【每周期计费金额】=2元
|
|
|
*/
|
|
|
public static void e1(){
|
|
@@ -234,11 +277,84 @@ public class ParkingFeeCalculator {
|
|
|
double fee = calculateParkingFee(parkingRecord, feeRule, false);
|
|
|
log.info("停车费用: " + fee + " 元");
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 包租协议计算停车费用
|
|
|
+ * @param registrations 车牌登记记录列表(已按车牌筛选)
|
|
|
+ * @return 包含停车时长和费用的Map
|
|
|
+ */
|
|
|
+ public static Map<String, Object> calculateParkingFee(ParkingRecord parkingRecord,FeeRule feeRule,
|
|
|
+ List<ContractPlaceNumberRel> registrations) {
|
|
|
+ LocalDateTime entryTime = parkingRecord.entryTime;
|
|
|
+ LocalDateTime currentTime = LocalDateTime.now();
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+
|
|
|
+ // 1. 计算总停车时长(分钟)
|
|
|
+ long totalMinutes = Duration.between(entryTime, currentTime).toMinutes();
|
|
|
+
|
|
|
+ // 2. 计算登记记录覆盖的有效时长(分钟)
|
|
|
+ long coveredMinutes = calculateCoveredMinutes(entryTime, currentTime, registrations);
|
|
|
+
|
|
|
+ // 3. 计算实际计费时长
|
|
|
+ long chargedMinutes = Math.max(0, totalMinutes - coveredMinutes);
|
|
|
+
|
|
|
+ // 4. 计算费用(不足1小时按1小时计算)
|
|
|
+// double hours = Math.ceil(chargedMinutes / 60.0);
|
|
|
+// double fee = hours * HOURLY_RATE;
|
|
|
+
|
|
|
+ result.put("totalMinutes", totalMinutes);
|
|
|
+ result.put("coveredMinutes", coveredMinutes);
|
|
|
+ result.put("chargedMinutes", chargedMinutes);
|
|
|
+ //实际收费的金额为入场时间+收费时长
|
|
|
+ parkingRecord.exitTime = parkingRecord.entryTime.plusMinutes(chargedMinutes);
|
|
|
+ result.put("fee", calculateParkingFee(parkingRecord, feeRule, false));
|
|
|
+ log.info("包租协议收费:"+result.toString());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算所有登记记录覆盖的有效时长
|
|
|
+ */
|
|
|
+ private static long calculateCoveredMinutes(LocalDateTime entryTime,
|
|
|
+ LocalDateTime currentTime,
|
|
|
+ List<ContractPlaceNumberRel> registrations) {
|
|
|
+ return registrations.stream()
|
|
|
+ .mapToLong(reg -> calculateSingleRegistrationMinutes(entryTime, currentTime, reg))
|
|
|
+ .sum();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算单条登记记录的有效时长
|
|
|
+ */
|
|
|
+ private static long calculateSingleRegistrationMinutes(LocalDateTime entryTime,
|
|
|
+ LocalDateTime currentTime,
|
|
|
+ ContractPlaceNumberRel registration) {
|
|
|
+ LocalDateTime regStartTime = registration.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
|
|
+ LocalDateTime regEndTime = registration.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
|
|
+
|
|
|
+ // 有效开始时间 = max(登记起始时间, 入场时间)
|
|
|
+ LocalDateTime effectiveStart = regStartTime.isAfter(entryTime)
|
|
|
+ ? regStartTime
|
|
|
+ : entryTime;
|
|
|
+
|
|
|
+ // 有效结束时间 = min(登记到期时间, 当前时间)
|
|
|
+ LocalDateTime effectiveEnd = regEndTime.isBefore(currentTime)
|
|
|
+ ? regEndTime
|
|
|
+ : currentTime;
|
|
|
+
|
|
|
+ // 计算有效时长(分钟)
|
|
|
+ if (effectiveStart.isBefore(effectiveEnd)) {
|
|
|
+ return Duration.between(effectiveStart, effectiveEnd).toMinutes();
|
|
|
+ }
|
|
|
+ return 0; // 无有效覆盖
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* @param args
|
|
|
*/
|
|
|
public static void main(String[] args) {
|
|
|
// e1();
|
|
|
- e2();
|
|
|
+// e2();
|
|
|
+ e3();
|
|
|
}
|
|
|
}
|