高峰出行日收费规则
This commit is contained in:
parent
d1e8cf67cc
commit
b3dd06f1c7
@ -9,6 +9,6 @@ import com.cdzy.orders.model.entity.EbikeOrderDetails;
|
||||
* @author attiya
|
||||
* @since 2025-04-25
|
||||
*/
|
||||
public interface EbikeOrderPaymentItemsMapper extends BaseMapper<EbikeOrderDetails> {
|
||||
public interface EbikeOrderDetailsMapper extends BaseMapper<EbikeOrderDetails> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.cdzy.orders.model.dto.res;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author attiya
|
||||
* @since 2025-04-27
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class TimeSegment implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
LocalDateTime start;
|
||||
LocalDateTime end;
|
||||
}
|
||||
@ -2,7 +2,7 @@ package com.cdzy.orders.service.impl;
|
||||
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import com.cdzy.orders.model.entity.EbikeOrderDetails;
|
||||
import com.cdzy.orders.mapper.EbikeOrderPaymentItemsMapper;
|
||||
import com.cdzy.orders.mapper.EbikeOrderDetailsMapper;
|
||||
import com.cdzy.orders.service.EbikeOrderDetailsService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -13,6 +13,6 @@ import org.springframework.stereotype.Service;
|
||||
* @since 2025-04-25
|
||||
*/
|
||||
@Service
|
||||
public class EbikeOrderDetailsServiceImpl extends ServiceImpl<EbikeOrderPaymentItemsMapper, EbikeOrderDetails> implements EbikeOrderDetailsService {
|
||||
public class EbikeOrderDetailsServiceImpl extends ServiceImpl<EbikeOrderDetailsMapper, EbikeOrderDetails> implements EbikeOrderDetailsService {
|
||||
|
||||
}
|
||||
|
||||
@ -6,11 +6,13 @@ import com.cdzy.common.model.JsonResult;
|
||||
import com.cdzy.common.model.ResGPSDto;
|
||||
import com.cdzy.orders.component.EbikeCoreHandler;
|
||||
import com.cdzy.orders.enums.*;
|
||||
import com.cdzy.orders.mapper.EbikeOrderDetailsMapper;
|
||||
import com.cdzy.orders.mapper.UserOrdersMapper;
|
||||
import com.cdzy.orders.model.dto.req.ReqBikeDto;
|
||||
import com.cdzy.orders.model.dto.req.ReqOrderDto;
|
||||
import com.cdzy.orders.model.dto.res.RedisPoint;
|
||||
import com.cdzy.orders.model.dto.res.RspBikeDto;
|
||||
import com.cdzy.orders.model.dto.res.TimeSegment;
|
||||
import com.cdzy.orders.model.entity.EbikeOrderDetails;
|
||||
import com.cdzy.orders.model.entity.EbikeUserOrders;
|
||||
import com.cdzy.orders.service.UserOrdersService;
|
||||
@ -20,6 +22,8 @@ import com.cdzy.orders.uitls.TimeUtils;
|
||||
import com.ebike.feign.clients.MaintenanceFeignClient;
|
||||
import com.ebike.feign.clients.OperateFeignClient;
|
||||
import com.ebike.feign.model.res.ResFeignEbikeSysRcostsetDto;
|
||||
import com.ebike.feign.model.res.ResFeignEbikeSysRcostsetTimePeriodDto;
|
||||
import com.ebike.feign.model.res.ResFeignEbikeSysRcostsetWeekDto;
|
||||
import com.ebike.feign.model.res.ResFeignOrderPaymentDto;
|
||||
import com.ebike.feign.model.rsp.FeignEbikeBikeInfoDto;
|
||||
import com.ebike.feign.model.rsp.FeignEbikeEcuInfo;
|
||||
@ -64,6 +68,9 @@ public class UserOrdersServiceImpl extends ServiceImpl<UserOrdersMapper, EbikeUs
|
||||
@Resource
|
||||
EbikeCoreHandler ebikeCoreHandler;
|
||||
|
||||
@Resource
|
||||
EbikeOrderDetailsMapper orderDetailsMapper;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Long saveRide(ReqOrderDto orderDto) {
|
||||
@ -370,13 +377,14 @@ public class UserOrdersServiceImpl extends ServiceImpl<UserOrdersMapper, EbikeUs
|
||||
//时长费用计算
|
||||
Character timeDivisionCharging = feignEbikeSysRcostsetDto.getTimeDivisionCharging();
|
||||
BigDecimal decimal = switch (timeDivisionCharging) {
|
||||
case TIME_SLOT -> timeSlotCostCalculation(list,minutes, userOrders, feignEbikeSysRcostsetDto,userOrders.getOrderId());
|
||||
case WEEK -> weekCostCalculation(list,minutes, userOrders, feignEbikeSysRcostsetDto,userOrders.getOrderId());
|
||||
default -> defaultCostCalculation(list,minutes, feignEbikeSysRcostsetDto,userOrders.getOrderId());
|
||||
case TIME_SLOT -> timeSlotCostCalculation(list, minutes, userOrders, feignEbikeSysRcostsetDto);
|
||||
case WEEK -> weekCostCalculation(list, userOrders, feignEbikeSysRcostsetDto);
|
||||
default -> defaultCostCalculation(list, userOrders, feignEbikeSysRcostsetDto, userOrders.getOrderId());
|
||||
};
|
||||
totalAmount = totalAmount.add(decimal);
|
||||
userOrders.setStatus(OrderStatus.PENDING_PAYMENT);
|
||||
userOrders.setTotalAmount(totalAmount);
|
||||
orderDetailsMapper.insertBatch(list);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -387,31 +395,106 @@ public class UserOrdersServiceImpl extends ServiceImpl<UserOrdersMapper, EbikeUs
|
||||
* @param feignEbikeSysRcostsetDto 计费规则
|
||||
* @return 计费后总金额
|
||||
*/
|
||||
BigDecimal timeSlotCostCalculation(List<EbikeOrderDetails> list, long minutes, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto,long orderId) {
|
||||
BigDecimal timeSlotCostCalculation(List<EbikeOrderDetails> list, long minutes, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto) {
|
||||
List<ResFeignEbikeSysRcostsetTimePeriodDto> ebikeSysRcostsetTimePeriodDtos = feignEbikeSysRcostsetDto.getEbikeSysRcostsetTimePeriodDtos();
|
||||
//TODO:时间是否跨天、划分不同自然时间段,每一段才判断在高峰时间段内有多长时间,并判断是否在高峰时间内起步
|
||||
return new BigDecimal(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照时间段计费
|
||||
*
|
||||
* @param minutes (骑行总分钟
|
||||
* @param userOrders 订单信息
|
||||
* @param feignEbikeSysRcostsetDto 计费规则
|
||||
* @return 计费后总金额
|
||||
*/
|
||||
BigDecimal weekCostCalculation(List<EbikeOrderDetails> list, long minutes, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto,long orderId) {
|
||||
return new BigDecimal(0);
|
||||
BigDecimal weekCostCalculation(List<EbikeOrderDetails> list,EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto) {
|
||||
BigDecimal decimal = new BigDecimal(0);
|
||||
LocalDateTime startTime = userOrders.getStartTime();
|
||||
LocalDateTime endTime = userOrders.getEndTime();
|
||||
boolean crossDay = TimeUtils.isCrossDay(startTime, endTime);
|
||||
Long orderId = userOrders.getOrderId();
|
||||
if (!crossDay) {
|
||||
//未跨天
|
||||
TimeSegment timeSegment = new TimeSegment(startTime, endTime);
|
||||
boolean checked = checkWeek(timeSegment, feignEbikeSysRcostsetDto);
|
||||
BigDecimal dailiedFee = dailyFee(timeSegment, feignEbikeSysRcostsetDto, true, checked, list, orderId);
|
||||
decimal = decimal.add(dailiedFee);
|
||||
} else {
|
||||
//跨天
|
||||
List<TimeSegment> timeSegments = TimeUtils.splitByDays(startTime, endTime);
|
||||
for (int i = 0; i < timeSegments.size(); i++) {
|
||||
TimeSegment timeSegment = timeSegments.get(i);
|
||||
boolean checked = checkWeek(timeSegment, feignEbikeSysRcostsetDto);
|
||||
BigDecimal dailiedFee = dailyFee(timeSegment, feignEbikeSysRcostsetDto, i < 1, checked, list, orderId);
|
||||
decimal = decimal.add(dailiedFee);
|
||||
}
|
||||
|
||||
}
|
||||
return decimal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照时间段计费(默认
|
||||
* 根据自然时间段计算高峰出行日费用
|
||||
*
|
||||
* @param minutes (骑行总分钟
|
||||
* @param feignEbikeSysRcostsetDto 计费规则
|
||||
* @return 计费后总金额
|
||||
* @param timeSegment 时间段
|
||||
* @param feignEbikeSysRcostsetDto 计算规则
|
||||
* @return 金额
|
||||
*/
|
||||
BigDecimal defaultCostCalculation(List<EbikeOrderDetails> list, long minutes, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto,long orderId) {
|
||||
BigDecimal dailyFee(TimeSegment timeSegment, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto, boolean isStart, boolean isWeek, List<EbikeOrderDetails> list, long orderId) {
|
||||
BigDecimal decimal = new BigDecimal(0);
|
||||
List<ResFeignEbikeSysRcostsetWeekDto> weekDtos = feignEbikeSysRcostsetDto.getEbikeSysRcostsetWeekDtos();
|
||||
LocalDateTime startTime = timeSegment.getStart();
|
||||
String weekNumber = TimeUtils.getDayOfWeekNumber(startTime);
|
||||
LocalDateTime endTime = timeSegment.getEnd();
|
||||
long minutes = TimeUtils.betweenMinutes(startTime, endTime);
|
||||
BigDecimal minutesNew = BigDecimal.valueOf(minutes);
|
||||
if (isWeek) {
|
||||
//高峰日出现计算
|
||||
for (ResFeignEbikeSysRcostsetWeekDto weekCostCalculation : weekDtos) {
|
||||
//根据符合要求的规则计算费用
|
||||
if (weekCostCalculation.getWeek().contains(weekNumber)) {
|
||||
if (isStart) {
|
||||
EbikeOrderDetails orderDetails = new EbikeOrderDetails();
|
||||
orderDetails.setOrderId(orderId);
|
||||
orderDetails.setItemAmount(weekCostCalculation.getStartupCost());
|
||||
orderDetails.setItemType(OrderDetailsType.TRAVEL_EXPENSES_ON_PEAK_DAYS);
|
||||
orderDetails.setItemName("高峰日出行费用-起步费用");
|
||||
orderDetails.setCalculationRule(JSONObject.toJSONString(feignEbikeSysRcostsetDto));
|
||||
list.add(orderDetails);
|
||||
Integer startupDuration = weekCostCalculation.getStartupDuration();
|
||||
if (minutes > startupDuration) {
|
||||
|
||||
BigDecimal startupDurationNew = BigDecimal.valueOf(startupDuration);
|
||||
//超出时长(总时长减去起步时长
|
||||
minutesNew = minutesNew.subtract(startupDurationNew);
|
||||
}
|
||||
}
|
||||
//时长计费
|
||||
BigDecimal durationCost = weekCostCalculation.getDurationCost();
|
||||
//时长多久计费一次
|
||||
Integer duration = weekCostCalculation.getDuration();
|
||||
//总计费几次(向上取整
|
||||
int ceil = NumberUtils.divideAndCeil(duration, minutesNew.intValue());
|
||||
BigDecimal ceilCost = BigDecimal.valueOf(ceil);
|
||||
//最终值
|
||||
BigDecimal multiply = durationCost.multiply(ceilCost);
|
||||
|
||||
EbikeOrderDetails durationCostOrderDetails = new EbikeOrderDetails();
|
||||
durationCostOrderDetails.setOrderId(orderId);
|
||||
durationCostOrderDetails.setItemAmount(multiply);
|
||||
durationCostOrderDetails.setItemType(OrderDetailsType.TRAVEL_EXPENSES_ON_PEAK_DAYS);
|
||||
durationCostOrderDetails.setItemName("高峰日出行费用-时长计费");
|
||||
durationCostOrderDetails.setCalculationRule(JSONObject.toJSONString(feignEbikeSysRcostsetDto));
|
||||
list.add(durationCostOrderDetails);
|
||||
decimal = decimal.add(multiply);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//非高峰出行日计费
|
||||
if (isStart) {
|
||||
//起始计费
|
||||
Integer startupDuration = feignEbikeSysRcostsetDto.getStartupDuration();
|
||||
BigDecimal startupCost = feignEbikeSysRcostsetDto.getStartupCost();
|
||||
EbikeOrderDetails orderDetails = new EbikeOrderDetails();
|
||||
@ -424,16 +507,17 @@ public class UserOrdersServiceImpl extends ServiceImpl<UserOrdersMapper, EbikeUs
|
||||
decimal = decimal.add(startupCost);
|
||||
//超出起步时长计费
|
||||
if (minutes > startupDuration) {
|
||||
BigDecimal minutesNew = BigDecimal.valueOf(minutes);
|
||||
BigDecimal startupDurationNew = BigDecimal.valueOf(startupDuration);
|
||||
//超出时长
|
||||
BigDecimal subtract = minutesNew.subtract(startupDurationNew);
|
||||
minutesNew = minutesNew.subtract(startupDurationNew);
|
||||
}
|
||||
}
|
||||
//时长计费
|
||||
BigDecimal durationCost = feignEbikeSysRcostsetDto.getDurationCost();
|
||||
//时长多久计费一次
|
||||
Integer duration = feignEbikeSysRcostsetDto.getDuration();
|
||||
//总计费几次(向上取整
|
||||
int ceil = NumberUtils.divideAndCeil(duration, subtract.intValue());
|
||||
int ceil = NumberUtils.divideAndCeil(duration, minutesNew.intValue());
|
||||
BigDecimal ceilCost = BigDecimal.valueOf(ceil);
|
||||
//最终值
|
||||
BigDecimal multiply = durationCost.multiply(ceilCost);
|
||||
@ -449,4 +533,36 @@ public class UserOrdersServiceImpl extends ServiceImpl<UserOrdersMapper, EbikeUs
|
||||
}
|
||||
return decimal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查是否高峰期
|
||||
*
|
||||
* @param timeSegment 自然时间段
|
||||
* @param feignEbikeSysRcostsetDto 计费规则
|
||||
* @return 是否
|
||||
*/
|
||||
boolean checkWeek(TimeSegment timeSegment, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto) {
|
||||
List<ResFeignEbikeSysRcostsetWeekDto> weekDtos = feignEbikeSysRcostsetDto.getEbikeSysRcostsetWeekDtos();
|
||||
LocalDateTime startTime = timeSegment.getStart();
|
||||
String weekNumber = TimeUtils.getDayOfWeekNumber(startTime);
|
||||
for (ResFeignEbikeSysRcostsetWeekDto weekCostCalculation : weekDtos) {
|
||||
if (weekCostCalculation.getWeek().contains(weekNumber)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照时间段计费(默认
|
||||
*
|
||||
* @param userOrders 订单信息
|
||||
* @param feignEbikeSysRcostsetDto 计费规则
|
||||
* @return 计费后总金额
|
||||
*/
|
||||
BigDecimal defaultCostCalculation(List<EbikeOrderDetails> list, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto, long orderId) {
|
||||
TimeSegment timeSegment = new TimeSegment(userOrders.getStartTime(), userOrders.getEndTime());
|
||||
return dailyFee(timeSegment, feignEbikeSysRcostsetDto, true, false, list, orderId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
package com.cdzy.orders.uitls;
|
||||
|
||||
import com.cdzy.orders.model.dto.res.TimeSegment;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -72,8 +76,8 @@ public class TimeUtils {
|
||||
* 计算两个时间之间的分钟差
|
||||
*/
|
||||
public static long betweenMinutes(LocalDateTime start, LocalDateTime end) {
|
||||
Objects.requireNonNull(start, "start time must not be null");
|
||||
Objects.requireNonNull(end, "end time must not be null");
|
||||
Objects.requireNonNull(start, "开始时间不能为空");
|
||||
Objects.requireNonNull(end, "结束时间不能为空");
|
||||
return ChronoUnit.MINUTES.between(start, end);
|
||||
}
|
||||
|
||||
@ -132,4 +136,55 @@ public class TimeUtils {
|
||||
Objects.requireNonNull(time, "time must not be null");
|
||||
return time.atZone(zoneId != null ? zoneId : ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取星期几的数字字符串表示(ISO-8601 标准,周一=1 到 周日=7)
|
||||
* @param dateTime 目标时间
|
||||
* @return 1-7 的整数
|
||||
*/
|
||||
public static String getDayOfWeekNumber(LocalDateTime dateTime) {
|
||||
return String.valueOf(dateTime.getDayOfWeek().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个时间是否跨天
|
||||
* @param time1 时间1
|
||||
* @param time2 时间2
|
||||
* @return true=跨天 | false=未跨天
|
||||
*/
|
||||
public static boolean isCrossDay(LocalDateTime time1, LocalDateTime time2) {
|
||||
return !time1.toLocalDate().equals(time2.toLocalDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间的自然日间隔天数(忽略时间部分)
|
||||
* @param start 开始时间
|
||||
* @param end 结束时间
|
||||
* @return 间隔自然日天数(end >= start 返回正数,反之返回负数)
|
||||
*/
|
||||
public static long daysBetween(LocalDateTime start, LocalDateTime end) {
|
||||
return ChronoUnit.DAYS.between(
|
||||
start.toLocalDate(),
|
||||
end.toLocalDate()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按自然日拆分时间段
|
||||
* @param start 开始时间
|
||||
* @param end 结束时间
|
||||
* @return 时间段列表
|
||||
*/
|
||||
public static List<TimeSegment> splitByDays(LocalDateTime start, LocalDateTime end) {
|
||||
List<TimeSegment> segments = new ArrayList<>();
|
||||
LocalDateTime currentStart = start;
|
||||
while (currentStart.isBefore(end)) {
|
||||
LocalDateTime nextDay = currentStart.plusDays(1).truncatedTo(ChronoUnit.DAYS);
|
||||
LocalDateTime segmentEnd = nextDay.isBefore(end) ? nextDay : end;
|
||||
segments.add(new TimeSegment(currentStart, segmentEnd));
|
||||
currentStart = segmentEnd;
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user