Browse Source

新增包租协议计费规则,扣除车牌登记的有效时长

loemkie 3 days ago
parent
commit
f1ddc9dce7

+ 83 - 3
src/main/java/com/qmrb/parking/fee/ParkingFeeCalculator.java

@@ -1,14 +1,18 @@
 package com.qmrb.parking.fee;
 
+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 +47,8 @@ public class ParkingFeeCalculator {
 
         public CouponRecord couponRecord = new CouponRecord(); // 优惠券记录
 
+        public String contractType;//合同类型 3包租协议
+
     }
 
     // 支付记录类
@@ -67,6 +73,7 @@ public class ParkingFeeCalculator {
         // 3. 计算计费时长
         Duration billingDuration = Duration.between(billingStartTime, billingEndTime);
 
+
         // 4. 检查离场时长宽容度
         /*if (feeRule.gracePeriodMinutes > 0) {
             for (PaymentRecord payment : parkingRecord.paymentRecords) {
@@ -139,12 +146,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 小时的费用
@@ -234,6 +242,78 @@ 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
      */

+ 6 - 3
src/main/java/com/qmrb/system/service/IContractPlaceNumberRelService.java

@@ -2,14 +2,15 @@ package com.qmrb.system.service;
 
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.qmrb.system.pojo.entity.BarnRecord;
+import com.baomidou.mybatisplus.extension.service.IService;
 import com.qmrb.system.pojo.entity.ContractPlaceNumberRel;
 import com.qmrb.system.pojo.form.ContractPlaceNumberRelForm;
-import com.qmrb.system.pojo.vo.ContractPlaceNumberRelVO;
 import com.qmrb.system.pojo.query.ContractPlaceNumberRelQuery;
-import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmrb.system.pojo.vo.ContractPlaceNumberRelVO;
 import jakarta.validation.Valid;
 
+import java.time.LocalDateTime;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -48,4 +49,6 @@ public interface IContractPlaceNumberRelService extends IService<ContractPlaceNu
 	 * @return
 	 */
 	Map<String,Object> getDenomination(Long recordId);
+
+	public List<ContractPlaceNumberRel> findValidRegistrations(String plateNumber, LocalDateTime entryTime);
 }

+ 39 - 26
src/main/java/com/qmrb/system/service/impl/CarParkChargingRulesServiceImpl.java

@@ -1,37 +1,37 @@
 package com.qmrb.system.service.impl;
 
-import java.math.BigDecimal;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.*;
-import java.util.stream.Collectors;
-
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.lang.Assert;
 import com.alibaba.fastjson.JSON;
-import com.qmrb.parking.fee.ParkingFeeCalculator;
-import com.qmrb.system.mapper.*;
-import com.qmrb.system.pojo.entity.*;
-import com.qmrb.system.service.ICarParkStoredCardService;
-import com.qmrb.system.service.IContractPlaceNumberRelService;
-import com.qmrb.system.service.OrderService;
-import org.springframework.beans.factory.annotation.Autowired;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.qmrb.system.common.enums.StatusEnum;
-import com.qmrb.system.pojo.vo.Option;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmrb.parking.fee.ParkingFeeCalculator;
 import com.qmrb.system.converter.CarParkChargingRulesConverter;
+import com.qmrb.system.mapper.BarnRecordMapper;
+import com.qmrb.system.mapper.CarParkChargingRulesMapper;
+import com.qmrb.system.mapper.CouponMapper;
+import com.qmrb.system.mapper.OrderMapper;
+import com.qmrb.system.pojo.entity.*;
 import com.qmrb.system.pojo.form.CarParkChargingRulesForm;
-import com.qmrb.system.pojo.vo.CarParkChargingRulesVO;
 import com.qmrb.system.pojo.query.CarParkChargingRulesQuery;
+import com.qmrb.system.pojo.vo.CarParkChargingRulesVO;
 import com.qmrb.system.service.ICarParkChargingRulesService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmrb.system.service.ICarParkStoredCardService;
+import com.qmrb.system.service.IContractPlaceNumberRelService;
+import jakarta.validation.Valid;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.core.lang.Assert;
-import jakarta.validation.Valid;
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * <p>
@@ -298,12 +298,15 @@ public class CarParkChargingRulesServiceImpl extends ServiceImpl<CarParkCharging
 				Map<String,Object> map = contractPlaceNumberRelService.getDenomination(recordId);
 				System.out.println("包租协议 = " + JSON.toJSONString(map));
 				if(CollectionUtil.isNotEmpty(map)){
+					parkingRecord.contractType = "3";//包租协议
+				}
+				/*if(CollectionUtil.isNotEmpty(map)){
 					ParkingFeeCalculator.CouponRecord couponRecord = new ParkingFeeCalculator.CouponRecord();
 					couponRecord.denomination = (Double) map.get("denomination");
 					couponRecord.expireTime = (LocalDateTime) map.get("expireTime");
 					couponRecord.couponType = "2";
 					parkingRecord.couponRecord = couponRecord;
-				}
+				}*/
 			}
 		}
 		if(CollectionUtil.isNotEmpty(orderList)){
@@ -327,8 +330,18 @@ public class CarParkChargingRulesServiceImpl extends ServiceImpl<CarParkCharging
 			// 计算费用
 			fee = ParkingFeeCalculator.calculateParkingFee(parkingRecord, feeRule, isMonthlyCardMode);
 		}else{
-			//直接计算费用
-			fee = ParkingFeeCalculator.calculateParkingFee(parkingRecord, feeRule, isMonthlyCardMode);
+			//判断是否为包租协议
+			if(StringUtils.equals("3",parkingRecord.contractType)){
+				// 查找有效的车牌登记记录(已按车牌筛选)
+				List<ContractPlaceNumberRel> registrations = contractPlaceNumberRelService.findValidRegistrations(carNumber, parkingRecord.entryTime);
+				//包租协议只计算临停费用
+				Map<String,Object> result  = ParkingFeeCalculator.calculateParkingFee(parkingRecord,feeRule, registrations);
+				fee = Double.parseDouble(result.get("fee").toString());
+			}else{
+				//直接计算费用
+				//1.临停费用 2.扫码协议
+				fee = ParkingFeeCalculator.calculateParkingFee(parkingRecord, feeRule, isMonthlyCardMode);
+			}
 		}
 		
 		return new BigDecimal(fee);

+ 38 - 21
src/main/java/com/qmrb/system/service/impl/ContractPlaceNumberRelServiceImpl.java

@@ -4,49 +4,37 @@ package com.qmrb.system.service.impl;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.qmrb.system.common.constant.SystemConstants;
-import com.qmrb.system.common.result.PageResult;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmrb.parking.fee.ParkingFeeCalculator;
 import com.qmrb.system.converter.ContractPlaceNumberRelConverter;
 import com.qmrb.system.framework.security.util.SecurityUtils;
 import com.qmrb.system.mapper.BarnRecordMapper;
+import com.qmrb.system.mapper.ContractPlaceNumberRelMapper;
 import com.qmrb.system.pojo.entity.*;
-import com.qmrb.system.pojo.entity.ContractPlaceNumberRel;
 import com.qmrb.system.pojo.form.ContractPlaceNumberRelForm;
-import com.qmrb.system.pojo.vo.BarnRecordVO;
-import com.qmrb.system.pojo.vo.ContractPlaceNumberRelHisVO;
-import com.qmrb.system.pojo.vo.ContractPlaceNumberRelVO;
 import com.qmrb.system.pojo.query.ContractPlaceNumberRelQuery;
-import com.qmrb.system.mapper.ContractPlaceNumberRelMapper;
-import com.qmrb.system.pojo.vo.PayOrderVO;
+import com.qmrb.system.pojo.vo.ContractPlaceNumberRelVO;
 import com.qmrb.system.service.IContractPlaceNumberRelHisService;
 import com.qmrb.system.service.IContractPlaceNumberRelService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmrb.system.service.IContractService;
 import com.qmrb.system.service.SysUserService;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import cn.hutool.core.lang.Assert;
-import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
-
 import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
  * <p>
@@ -74,6 +62,8 @@ public class ContractPlaceNumberRelServiceImpl extends ServiceImpl<ContractPlace
     @Autowired
     IContractPlaceNumberRelHisService contractPlaceNumberRelHisService;
 
+    @Autowired
+    ParkingFeeCalculator parkingFeeCalculator;
 
     /**
      * 分页查询
@@ -302,4 +292,31 @@ public class ContractPlaceNumberRelServiceImpl extends ServiceImpl<ContractPlace
         return null;
     }
 
+
+    /**
+     * 计算停车费用
+     * @param plateNumber 车牌号(仅用于查询)
+     * @return 停车费用信息
+     */
+  /*  public Map<String, Object> calculateParkingFee(ParkingFeeCalculator.ParkingRecord parkingRecord, ParkingFeeCalculator.FeeRule feeRule, String plateNumber) {
+        // 查找有效的车牌登记记录(已按车牌筛选)
+        List<ContractPlaceNumberRel> registrations = findValidRegistrations(plateNumber, parkingRecord.entryTime);
+
+        // 计算费用
+        return parkingFeeCalculator.calculateParkingFee(parkingRecord,feeRule, registrations);
+    }*/
+
+    /**
+     * 查找有效的车牌登记记录
+     */
+    public List<ContractPlaceNumberRel> findValidRegistrations(String plateNumber, LocalDateTime entryTime) {
+        LambdaQueryWrapper<ContractPlaceNumberRel> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(ContractPlaceNumberRel::getPlateNumber, plateNumber);
+        queryWrapper.eq(ContractPlaceNumberRel::getStatus, "1"); // 启用状态
+        queryWrapper.lt(ContractPlaceNumberRel::getStartTime, LocalDateTime.now()); // 起始时间 < 当前时间
+        queryWrapper.gt(ContractPlaceNumberRel::getEndTime, entryTime); // 到期时间 > 入场时间
+
+        return this.list(queryWrapper);
+    }
+
 }