From b3dd06f1c78713eb04f7f089b998cb5383accf08 Mon Sep 17 00:00:00 2001 From: attiya <2413103649@qq.com> Date: Sun, 27 Apr 2025 16:59:58 +0800 Subject: [PATCH] =?UTF-8?q?=E9=AB=98=E5=B3=B0=E5=87=BA=E8=A1=8C=E6=97=A5?= =?UTF-8?q?=E6=94=B6=E8=B4=B9=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...pper.java => EbikeOrderDetailsMapper.java} | 2 +- .../orders/model/dto/res/TimeSegment.java | 25 +++ .../impl/EbikeOrderDetailsServiceImpl.java | 4 +- .../service/impl/UserOrdersServiceImpl.java | 174 +++++++++++++++--- .../java/com/cdzy/orders/uitls/TimeUtils.java | 59 +++++- 5 files changed, 230 insertions(+), 34 deletions(-) rename ebike-orders/src/main/java/com/cdzy/orders/mapper/{EbikeOrderPaymentItemsMapper.java => EbikeOrderDetailsMapper.java} (71%) create mode 100644 ebike-orders/src/main/java/com/cdzy/orders/model/dto/res/TimeSegment.java diff --git a/ebike-orders/src/main/java/com/cdzy/orders/mapper/EbikeOrderPaymentItemsMapper.java b/ebike-orders/src/main/java/com/cdzy/orders/mapper/EbikeOrderDetailsMapper.java similarity index 71% rename from ebike-orders/src/main/java/com/cdzy/orders/mapper/EbikeOrderPaymentItemsMapper.java rename to ebike-orders/src/main/java/com/cdzy/orders/mapper/EbikeOrderDetailsMapper.java index e3644d22..81f2692e 100644 --- a/ebike-orders/src/main/java/com/cdzy/orders/mapper/EbikeOrderPaymentItemsMapper.java +++ b/ebike-orders/src/main/java/com/cdzy/orders/mapper/EbikeOrderDetailsMapper.java @@ -9,6 +9,6 @@ import com.cdzy.orders.model.entity.EbikeOrderDetails; * @author attiya * @since 2025-04-25 */ -public interface EbikeOrderPaymentItemsMapper extends BaseMapper { +public interface EbikeOrderDetailsMapper extends BaseMapper { } diff --git a/ebike-orders/src/main/java/com/cdzy/orders/model/dto/res/TimeSegment.java b/ebike-orders/src/main/java/com/cdzy/orders/model/dto/res/TimeSegment.java new file mode 100644 index 00000000..fc9393a0 --- /dev/null +++ b/ebike-orders/src/main/java/com/cdzy/orders/model/dto/res/TimeSegment.java @@ -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; +} diff --git a/ebike-orders/src/main/java/com/cdzy/orders/service/impl/EbikeOrderDetailsServiceImpl.java b/ebike-orders/src/main/java/com/cdzy/orders/service/impl/EbikeOrderDetailsServiceImpl.java index 03036c20..faed6326 100644 --- a/ebike-orders/src/main/java/com/cdzy/orders/service/impl/EbikeOrderDetailsServiceImpl.java +++ b/ebike-orders/src/main/java/com/cdzy/orders/service/impl/EbikeOrderDetailsServiceImpl.java @@ -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 implements EbikeOrderDetailsService { +public class EbikeOrderDetailsServiceImpl extends ServiceImpl implements EbikeOrderDetailsService { } diff --git a/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java b/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java index 8d2c400a..0127027a 100644 --- a/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java +++ b/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java @@ -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 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,53 +395,129 @@ public class UserOrdersServiceImpl extends ServiceImpl list, long minutes, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto,long orderId) { + BigDecimal timeSlotCostCalculation(List list, long minutes, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto) { + List ebikeSysRcostsetTimePeriodDtos = feignEbikeSysRcostsetDto.getEbikeSysRcostsetTimePeriodDtos(); + //TODO:时间是否跨天、划分不同自然时间段,每一段才判断在高峰时间段内有多长时间,并判断是否在高峰时间内起步 return new BigDecimal(0); } /** * 按照时间段计费 * - * @param minutes (骑行总分钟 * @param userOrders 订单信息 * @param feignEbikeSysRcostsetDto 计费规则 * @return 计费后总金额 */ - BigDecimal weekCostCalculation(List list, long minutes, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto,long orderId) { - return new BigDecimal(0); + BigDecimal weekCostCalculation(List 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 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 list, long minutes, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto,long orderId) { + BigDecimal dailyFee(TimeSegment timeSegment, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto, boolean isStart, boolean isWeek, List list, long orderId) { BigDecimal decimal = new BigDecimal(0); - Integer startupDuration = feignEbikeSysRcostsetDto.getStartupDuration(); - BigDecimal startupCost = feignEbikeSysRcostsetDto.getStartupCost(); - EbikeOrderDetails orderDetails = new EbikeOrderDetails(); - orderDetails.setOrderId(orderId); - orderDetails.setItemAmount(startupCost); - orderDetails.setItemType(OrderDetailsType.STARTING_FEE); - orderDetails.setItemName("起步费用"); - orderDetails.setCalculationRule(JSONObject.toJSONString(feignEbikeSysRcostsetDto)); - list.add(orderDetails); - decimal = decimal.add(startupCost); - //超出起步时长计费 - if (minutes > startupDuration) { - BigDecimal minutesNew = BigDecimal.valueOf(minutes); - BigDecimal startupDurationNew = BigDecimal.valueOf(startupDuration); - //超出时长 - BigDecimal subtract = minutesNew.subtract(startupDurationNew); + List 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(); + orderDetails.setOrderId(orderId); + orderDetails.setItemAmount(startupCost); + orderDetails.setItemType(OrderDetailsType.STARTING_FEE); + orderDetails.setItemName("起步费用"); + orderDetails.setCalculationRule(JSONObject.toJSONString(feignEbikeSysRcostsetDto)); + list.add(orderDetails); + decimal = decimal.add(startupCost); + //超出起步时长计费 + if (minutes > startupDuration) { + BigDecimal startupDurationNew = BigDecimal.valueOf(startupDuration); + //超出时长 + 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 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 list, EbikeUserOrders userOrders, ResFeignEbikeSysRcostsetDto feignEbikeSysRcostsetDto, long orderId) { + TimeSegment timeSegment = new TimeSegment(userOrders.getStartTime(), userOrders.getEndTime()); + return dailyFee(timeSegment, feignEbikeSysRcostsetDto, true, false, list, orderId); + } } diff --git a/ebike-orders/src/main/java/com/cdzy/orders/uitls/TimeUtils.java b/ebike-orders/src/main/java/com/cdzy/orders/uitls/TimeUtils.java index 7e177d77..cc595db5 100644 --- a/ebike-orders/src/main/java/com/cdzy/orders/uitls/TimeUtils.java +++ b/ebike-orders/src/main/java/com/cdzy/orders/uitls/TimeUtils.java @@ -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 splitByDays(LocalDateTime start, LocalDateTime end) { + List 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; + } + }