订单费用明细

This commit is contained in:
yanglei 2026-01-15 16:34:10 +08:00
parent fab86d8bab
commit 9986bc195b
4 changed files with 121 additions and 74 deletions

View File

@ -4,15 +4,51 @@ import cn.dev33.satoken.stp.StpUtil;
import com.cdzy.common.ex.EbikeException; import com.cdzy.common.ex.EbikeException;
import com.cdzy.common.model.dto.ResGPSDto; import com.cdzy.common.model.dto.ResGPSDto;
import com.cdzy.common.model.request.PageParam; import com.cdzy.common.model.request.PageParam;
import com.cdzy.operations.enums.*; import com.cdzy.operations.enums.BatteryStatus;
import com.cdzy.operations.mapper.*; import com.cdzy.operations.enums.BikeOrderType;
import com.cdzy.operations.enums.BikeQrStatus;
import com.cdzy.operations.enums.BikeStatus;
import com.cdzy.operations.enums.BikeUsageStatus;
import com.cdzy.operations.enums.OrderDispatchState;
import com.cdzy.operations.enums.OrderHandleState;
import com.cdzy.operations.enums.RegionStatus;
import com.cdzy.operations.enums.SpecialBillingConfigurationType;
import com.cdzy.operations.mapper.EbiekSpecialDayMapper;
import com.cdzy.operations.mapper.EbiekSpecialTimeMapper;
import com.cdzy.operations.mapper.EbikeBatteryInfoMapper;
import com.cdzy.operations.mapper.EbikeBikeInfoMapper;
import com.cdzy.operations.mapper.EbikeBikeOrderMapper;
import com.cdzy.operations.mapper.EbikeBikeQrMapper;
import com.cdzy.operations.mapper.EbikeDefaultBillingConfigurationMapper;
import com.cdzy.operations.mapper.EbikeRegionMapper;
import com.cdzy.operations.mapper.EbikeSiteMapper;
import com.cdzy.operations.mapper.EbikeSpecialBillingConfigurationMapper;
import com.cdzy.operations.model.dto.EbikeBikeInfoDto; import com.cdzy.operations.model.dto.EbikeBikeInfoDto;
import com.cdzy.operations.model.dto.EbikeDto; import com.cdzy.operations.model.dto.EbikeDto;
import com.cdzy.operations.model.dto.EbikeUserBikeInfo; import com.cdzy.operations.model.dto.EbikeUserBikeInfo;
import com.cdzy.operations.model.dto.EbikeUserLockDto; import com.cdzy.operations.model.dto.EbikeUserLockDto;
import com.cdzy.operations.model.entity.*; import com.cdzy.operations.model.entity.EbikeBatteryInfo;
import com.cdzy.operations.model.vo.*; import com.cdzy.operations.model.entity.EbikeBikeInfo;
import com.cdzy.operations.service.*; import com.cdzy.operations.model.entity.EbikeBikeOrder;
import com.cdzy.operations.model.entity.EbikeBikeQr;
import com.cdzy.operations.model.entity.EbikeDefaultBillingConfiguration;
import com.cdzy.operations.model.entity.EbikeDispatchConfiguration;
import com.cdzy.operations.model.entity.EbikeEcuInfo;
import com.cdzy.operations.model.entity.EbikeInventoryRecord;
import com.cdzy.operations.model.entity.EbikeRegion;
import com.cdzy.operations.model.entity.EbikeSpecialBillingConfiguration;
import com.cdzy.operations.model.entity.EbikeSpecialDay;
import com.cdzy.operations.model.entity.EbikeSpecialTime;
import com.cdzy.operations.model.vo.EbikeBatchLaunchVo;
import com.cdzy.operations.model.vo.EbikeBatchUnLaunchVo;
import com.cdzy.operations.model.vo.EbikeBikeBindVo;
import com.cdzy.operations.model.vo.EbikeBikeRadiusVo;
import com.cdzy.operations.model.vo.EbikeInventoryVo;
import com.cdzy.operations.service.EbikeBikeInfoService;
import com.cdzy.operations.service.EbikeDispatchConfigurationService;
import com.cdzy.operations.service.EbikeEcuInfoService;
import com.cdzy.operations.service.EbikeInventoryRecordService;
import com.cdzy.operations.service.EbikeInventoryService;
import com.cdzy.operations.utils.RedisUtil; import com.cdzy.operations.utils.RedisUtil;
import com.ebike.feign.model.vo.EbikeLockVo; import com.ebike.feign.model.vo.EbikeLockVo;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
@ -365,6 +401,8 @@ public class EbikeBikeInfoServiceImpl extends ServiceImpl<EbikeBikeInfoMapper, E
bikeOrder.setHandleState(OrderHandleState.VOIDED); bikeOrder.setHandleState(OrderHandleState.VOIDED);
orderMapper.update(bikeOrder); orderMapper.update(bikeOrder);
} }
// 删除redis中的 车辆多长时间无单生成调度工单
redisUtil.deleteNoDocument(bikeCode);
} }
@Override @Override

View File

@ -488,7 +488,7 @@ public class RedisUtil {
} }
/** /**
* 数据库2专用存储 辆多长时间无单生成调度工单 并设置过期时间 * 数据库2专用删除 车辆多长时间无单生成调度工单
*/ */
public void deleteNoDocument(String bikeCode) { public void deleteNoDocument(String bikeCode) {
delete(Database.DB2, BIKE_NO_DOCUMENT_PREFIX + bikeCode); delete(Database.DB2, BIKE_NO_DOCUMENT_PREFIX + bikeCode);

View File

@ -19,9 +19,6 @@ import java.time.LocalDateTime;
@Data @Data
public class EbikeOrderDetailVo { public class EbikeOrderDetailVo {
/**
* 订单表主键ID
*/
private Long orderId; private Long orderId;
/** /**
@ -177,4 +174,24 @@ public class EbikeOrderDetailVo {
* 是否免费订单 true-免费 false-收费 * 是否免费订单 true-免费 false-收费
*/ */
private Boolean isFreeOrder; private Boolean isFreeOrder;
/**
* 用户花费时长费用
*/
private BigDecimal userDurationFee;
/**
* 用户花费起步费用
*/
private BigDecimal userBaseFee;
/**
* 用户花费调度费用
*/
private BigDecimal userDispatchFee;
/**
* 用户头盔费用
*/
private BigDecimal userHelmetFee;
} }

View File

@ -310,28 +310,13 @@ public class EbikeOrderServiceImpl extends ServiceImpl<EbikeOrderMapper, EbikeOr
public EbikeOrderDetailVo getInfo(Long orderId) { public EbikeOrderDetailVo getInfo(Long orderId) {
QueryWrapper queryWrapper = QueryWrapper.create() QueryWrapper queryWrapper = QueryWrapper.create()
.select( .select(
EBIKE_ORDER.ORDER_ID, EBIKE_ORDER.ALL_COLUMNS
EBIKE_ORDER.USER_ID,
EBIKE_ORDER.OPERATOR_ID,
EBIKE_ORDER.BIKE_CODE,
EBIKE_ORDER.ORDER_TYPE,
EBIKE_ORDER.ORDER_STATUS,
EBIKE_ORDER.TOTAL_AMOUNT,
EBIKE_ORDER.ACTUAL_AMOUNT,
EBIKE_ORDER.START_TIME,
EBIKE_ORDER.END_TIME,
EBIKE_ORDER.PAYMENT_TIME,
EBIKE_ORDER.PAYMENT_METHOD,
EBIKE_ORDER.BASE_DURATION_MINUTES,
EBIKE_ORDER.FREE_DURATION_MINUTES,
EBIKE_ORDER.CHARGE_DURATION_MINUTES,
EBIKE_ORDER.MAX_FEE_AMOUNT,
EBIKE_ORDER.START_LOCATION,
EBIKE_ORDER.END_LOCATION,
EBIKE_ORDER.IS_FREE_ORDER
) )
.where(EBIKE_ORDER.ORDER_ID.eq(orderId)); .where(EBIKE_ORDER.ORDER_ID.eq(orderId));
EbikeOrderDetailVo ebikeOrderDetailVo = this.mapper.selectOneByQueryAs(queryWrapper, EbikeOrderDetailVo.class); EbikeOrderDetailVo ebikeOrderDetailVo = this.mapper.selectOneByQueryAs(queryWrapper, EbikeOrderDetailVo.class);
if (Objects.isNull(ebikeOrderDetailVo)) {
throw new EbikeException("订单不存在");
}
//获取费用详情 //获取费用详情
List<EbikePaymentCostDetailVo> details = ebikeOrderDetailService.getOrderDetailsByOrderId(orderId); List<EbikePaymentCostDetailVo> details = ebikeOrderDetailService.getOrderDetailsByOrderId(orderId);
if (Objects.nonNull(details)) { if (Objects.nonNull(details)) {
@ -340,27 +325,27 @@ public class EbikeOrderServiceImpl extends ServiceImpl<EbikeOrderMapper, EbikeOr
switch (payDetailVo.getDetailType()) { switch (payDetailVo.getDetailType()) {
case 1 -> { case 1 -> {
BigDecimal current = payDetailVo.getDetailAmount(); BigDecimal current = payDetailVo.getDetailAmount();
ebikeOrderDetailVo.setDurationFee((current != null ? current : BigDecimal.ZERO)); ebikeOrderDetailVo.setUserDurationFee((current != null ? current : BigDecimal.ZERO));
} }
case 2 -> { case 2 -> {
BigDecimal current = payDetailVo.getDetailAmount(); BigDecimal current = payDetailVo.getDetailAmount();
ebikeOrderDetailVo.setBaseFee((current != null ? current : BigDecimal.ZERO)); ebikeOrderDetailVo.setUserBaseFee((current != null ? current : BigDecimal.ZERO));
} }
case 3 -> { case 3 -> {
BigDecimal current = payDetailVo.getDetailAmount(); BigDecimal current = payDetailVo.getDetailAmount();
ebikeOrderDetailVo.setOutOfServiceAreaFee((current != null ? current : BigDecimal.ZERO)); ebikeOrderDetailVo.setUserDispatchFee((current != null ? current : BigDecimal.ZERO));
} }
case 4 -> { case 4 -> {
BigDecimal current = payDetailVo.getDetailAmount(); BigDecimal current = payDetailVo.getDetailAmount();
ebikeOrderDetailVo.setOutOfParkingAreaFee((current != null ? current : BigDecimal.ZERO)); ebikeOrderDetailVo.setUserDispatchFee((current != null ? current : BigDecimal.ZERO));
} }
case 5 -> { case 5 -> {
BigDecimal current = payDetailVo.getDetailAmount(); BigDecimal current = payDetailVo.getDetailAmount();
ebikeOrderDetailVo.setNoParkingZoneFee((current != null ? current : BigDecimal.ZERO)); ebikeOrderDetailVo.setUserDispatchFee((current != null ? current : BigDecimal.ZERO));
} }
case 6 -> { case 6 -> {
BigDecimal current = payDetailVo.getDetailAmount(); BigDecimal current = payDetailVo.getDetailAmount();
ebikeOrderDetailVo.setHelmetManagementFee((current != null ? current : BigDecimal.ZERO)); ebikeOrderDetailVo.setUserHelmetFee((current != null ? current : BigDecimal.ZERO));
} }
} }
} }
@ -459,61 +444,68 @@ public class EbikeOrderServiceImpl extends ServiceImpl<EbikeOrderMapper, EbikeOr
* @return 订单费用 * @return 订单费用
*/ */
private EbikeCostDetailDto costCalculation(LocalDateTime endTime, private EbikeCostDetailDto costCalculation(LocalDateTime endTime,
EbikeOrder order, FeignEbikeUserLockDto data) { EbikeOrder order,
// 基础费用计算 FeignEbikeUserLockDto data) {
EbikeCostDetailDto dto = new EbikeCostDetailDto();
// 1. 基础参数
BigDecimal baseFee = Objects.requireNonNullElse(order.getBaseFee(), BigDecimal.ZERO); BigDecimal baseFee = Objects.requireNonNullElse(order.getBaseFee(), BigDecimal.ZERO);
BigDecimal durationFee = Objects.requireNonNullElse(order.getDurationFee(), BigDecimal.ZERO); BigDecimal durationFee = Objects.requireNonNullElse(order.getDurationFee(), BigDecimal.ZERO);
Integer freeDurationMinutes = order.getFreeDurationMinutes(); Integer freeDurationMinutes = Objects.requireNonNullElse(order.getFreeDurationMinutes(), 0);
Integer chargeDurationMinutes = order.getChargeDurationMinutes(); Integer baseDurationMinutes = Objects.requireNonNullElse(order.getBaseDurationMinutes(), 0);
// 获取总骑行时长 int chargeDurationMinutes = Objects.requireNonNullElse(order.getChargeDurationMinutes(), 1);
// 2. 计算总骑行时长
long totalRideSeconds = Duration.between(order.getStartTime(), endTime).toSeconds(); long totalRideSeconds = Duration.between(order.getStartTime(), endTime).toSeconds();
// 免费时间 // 向上取整为分钟
long freeDurationSeconds = freeDurationMinutes * GlobalConstants.LONG_SIXTY; long totalRideMinutes = (totalRideSeconds + 59) / 60;
// 如果骑行时长小于等于免费时长,不收费
BigDecimal baseCost; // 3. 免费阶段总时长 <= 免费时长
EbikeCostDetailDto ebikeCostDetailDto = new EbikeCostDetailDto(); if (totalRideMinutes <= freeDurationMinutes) {
// 起步费用 dto.setTotalCost(BigDecimal.ZERO);
ebikeCostDetailDto.setBaseFee(baseFee); return dto;
if (totalRideSeconds <= freeDurationSeconds) {
ebikeCostDetailDto.setTotalCost(BigDecimal.ZERO);
return ebikeCostDetailDto;
} else {
long totalRoundedMinutes = (totalRideSeconds + GlobalConstants.NUMBER_FIFTY_NINE) / GlobalConstants.NUMBER_SIXTY;
// 可计费分钟数 = 向上取整后的总分钟 - 免费分钟
long chargeableMinutes = totalRoundedMinutes - freeDurationMinutes;
long chargeUnits = (chargeableMinutes + chargeDurationMinutes - 1) / chargeDurationMinutes;
BigDecimal durationCost = durationFee.multiply(BigDecimal.valueOf(chargeUnits));
// 时长费用
ebikeCostDetailDto.setDurationFee(durationCost);
// 总基础费用
baseCost = baseFee.add(durationCost);
} }
// 计算调度费按优先级判断附加费用运营区 > 停车区外 > 禁停区
// 4. 起步阶段免费之后但未超出起步时长 只收起步费
long includedInBaseMinutes = freeDurationMinutes + baseDurationMinutes;
if (totalRideMinutes <= includedInBaseMinutes) {
dto.setBaseFee(baseFee);
dto.setTotalCost(baseFee);
return dto;
}
// 5. 超出起步阶段计算额外时长费用
long extraMinutes = totalRideMinutes - includedInBaseMinutes;
long chargeUnits = (extraMinutes + chargeDurationMinutes - 1) / chargeDurationMinutes;
BigDecimal durationCost = durationFee.multiply(BigDecimal.valueOf(chargeUnits));
dto.setBaseFee(baseFee);
dto.setDurationFee(durationCost);
BigDecimal baseCost = baseFee.add(durationCost);
// 6. 调度费用
BigDecimal additionalFee = BigDecimal.ZERO; BigDecimal additionalFee = BigDecimal.ZERO;
if (data != null) { if (data != null) {
// 运营区
if (Boolean.FALSE.equals(data.getBikeInRegion())) { if (Boolean.FALSE.equals(data.getBikeInRegion())) {
additionalFee = order.getOutOfServiceAreaFee() != null ? order.getOutOfServiceAreaFee() : BigDecimal.ZERO; additionalFee = Objects.requireNonNullElse(order.getOutOfServiceAreaFee(), BigDecimal.ZERO);
ebikeCostDetailDto.setOutOfServiceAreaFee(additionalFee); dto.setOutOfServiceAreaFee(additionalFee);
} else if (Boolean.FALSE.equals(data.getBikeInParking())) { } else if (Boolean.FALSE.equals(data.getBikeInParking())) {
// 不在运营区内但在停车区内 additionalFee = Objects.requireNonNullElse(order.getOutOfParkingAreaFee(), BigDecimal.ZERO);
additionalFee = order.getOutOfParkingAreaFee() != null ? order.getOutOfParkingAreaFee() : BigDecimal.ZERO; dto.setOutOfParkingAreaFee(additionalFee);
ebikeCostDetailDto.setOutOfParkingAreaFee(additionalFee); } else if (Boolean.TRUE.equals(data.getBikeInNoParking())) {
} else if (Boolean.FALSE.equals(data.getBikeInNoParking())) { additionalFee = Objects.requireNonNullElse(order.getNoParkingZoneFee(), BigDecimal.ZERO);
// 不在运营区内也不在停车区内但在禁停区内 dto.setNoParkingZoneFee(additionalFee);
additionalFee = order.getNoParkingZoneFee() != null ? order.getNoParkingZoneFee() : BigDecimal.ZERO;
ebikeCostDetailDto.setNoParkingZoneFee(additionalFee);
} }
} }
// 计算总费用
// 7. 总费用 + 封顶
BigDecimal totalCost = baseCost.add(additionalFee); BigDecimal totalCost = baseCost.add(additionalFee);
// 检查是否超过封顶金额
BigDecimal maxFee = order.getMaxFeeAmount(); BigDecimal maxFee = order.getMaxFeeAmount();
if (maxFee != null && totalCost.compareTo(maxFee) > 0) { if (maxFee != null && totalCost.compareTo(maxFee) > 0) {
totalCost = maxFee; totalCost = maxFee;
} }
ebikeCostDetailDto.setTotalCost(totalCost); dto.setTotalCost(totalCost);
return ebikeCostDetailDto; return dto;
} }
/** /**