From 738cb26e99211c5e5eb05d6b83d57bd69d22a6739c4b98812dbe8a8100aeab80 Mon Sep 17 00:00:00 2001 From: yanglei Date: Tue, 11 Nov 2025 15:59:49 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=BB=93=E6=9D=9F=E9=AA=91?= =?UTF-8?q?=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/EbikeOrderController.java | 221 +++++++++--------- .../user/model/dto/EbikeUserCyclingDto.java | 2 +- .../model/dto/EbikeUserCyclingEndDto.java | 44 ++++ .../cdzy/user/model/entity/EbikeOrder.java | 25 ++ .../com/cdzy/user/model/vo/EbikeOrderVo.java | 2 +- .../cdzy/user/service/EbikeOrderService.java | 5 +- .../user/service/impl/EbikeOrderImpl.java | 80 +++++-- ebike-user/src/main/resources/db/init.sql | 10 + 8 files changed, 252 insertions(+), 137 deletions(-) create mode 100644 ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingEndDto.java diff --git a/ebike-user/src/main/java/com/cdzy/user/controller/EbikeOrderController.java b/ebike-user/src/main/java/com/cdzy/user/controller/EbikeOrderController.java index 3ddf6e4..d7c4c8c 100644 --- a/ebike-user/src/main/java/com/cdzy/user/controller/EbikeOrderController.java +++ b/ebike-user/src/main/java/com/cdzy/user/controller/EbikeOrderController.java @@ -2,6 +2,7 @@ package com.cdzy.user.controller; import com.cdzy.common.model.response.JsonResult; import com.cdzy.user.model.dto.EbikeUserCyclingDto; +import com.cdzy.user.model.dto.EbikeUserCyclingEndDto; import com.cdzy.user.model.entity.EbikeOrder; import com.cdzy.user.model.vo.EbikeOrderVo; import com.cdzy.user.service.EbikeOrderService; @@ -30,120 +31,10 @@ public class EbikeOrderController { @Resource private EbikeOrderService ebikeOrderService; - /** - * 开始骑行(生成订单、开锁) - * - * @param orderDto 骑行信息 (返回订单开始时间、开始点位) - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @PostMapping("saveRide") - public JsonResult saveRide(@RequestBody @Validated EbikeUserCyclingDto orderDto) { - EbikeOrderVo result = ebikeOrderService.saveRide(orderDto); - return JsonResult.success(result); - } - - /** - * 查看用户是否有未完成订单。 - * - * @param userId 用户id - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @GetMapping("checkHistoryOrder") - public JsonResult checkHistoryOrder(@Validated @NotNull(message = "用户id不能为空") @RequestParam("userId") Long userId) { - EbikeOrder userOrders = ebikeOrderService.checkHistoryOrder(userId); - return JsonResult.success(userOrders); - } - - /** - * 订单支付。 - * - * @param orderPaymentDto 支付信息 - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @PostMapping("payment") - public JsonResult payment(@RequestBody @Validated FeignOrderPaymentDto orderPaymentDto) { - ebikeOrderService.payment(orderPaymentDto); - return JsonResult.success(); - } - - /** - * 订单退款申请。 - * - * @param orderId 订单ID - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @GetMapping("refundApply") - public JsonResult refundApply(@RequestParam("orderId") Long orderId) { - ebikeOrderService.refundApply(orderId); - return JsonResult.success(); - } - - /** - * 订单退款。 - * - * @param orderId 订单ID - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @GetMapping("refund") - public JsonResult refund(@RequestParam("orderId") Long orderId) { - ebikeOrderService.refund(orderId); - return JsonResult.success(); - } - - /** - * 订单退款完成。 - * - * @param orderId 订单ID - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @GetMapping("doneRefund") - public JsonResult doneRefund(@RequestParam("orderId") Long orderId) { - ebikeOrderService.doneRefund(orderId); - return JsonResult.success(); - } - - /** - * 订单退款失败。 - * - * @param orderId 订单ID - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @GetMapping("failRefund") - public JsonResult failRefund(@RequestParam("orderId") Long orderId) { - ebikeOrderService.failRefund(orderId); - return JsonResult.success(); - } - - /** - * 订单退款驳回。 - * - * @param orderId 订单ID - * @ {@code 200} 添加成功,{@code 500} 添加失败 - */ - @GetMapping("rejectRefund") - public JsonResult rejectRefund(@RequestParam("orderId") Long orderId) { - ebikeOrderService.rejectRefund(orderId); - return JsonResult.success(); - } - - /** - * 根据用户订单表主键获取详细信。 - * - * @param orderId 用户订单表主键 - * @ 用户订单表详情 - */ - @GetMapping("getInfo/{orderId}") - public JsonResult getInfo(@PathVariable("orderId") Long orderId) { - EbikeOrder userOrder = ebikeOrderService.getById(orderId); - return JsonResult.success(userOrder); - } - - /** * 车辆列表 * * @param feignEbikeBikeRadiusVo 坐标信息 - * @ {@code 200} 添加成功,{@code 500} 添加失败 */ @PostMapping("bikeList") public JsonResult bikeList(@RequestBody @Validated FeignEbikeBikeRadiusVo feignEbikeBikeRadiusVo) { @@ -161,4 +52,114 @@ public class EbikeOrderController { FeignEbikeUserBikeInfo result = ebikeOrderService.queryBikeInfo(bikeCode); return JsonResult.success(result); } + + /** + * 开始骑行(生成订单、开锁) + * + * @param orderDto 骑行信息 (返回订单开始时间、开始点位) + */ + @PostMapping("saveRide") + public JsonResult saveRide(@RequestBody @Validated EbikeUserCyclingDto orderDto) { + EbikeOrder result = ebikeOrderService.saveRide(orderDto); + return JsonResult.success(result); + } + + /** + * 查看用户是否有未完成订单。 + * + * @param userId 用户id + */ + @GetMapping("checkHistoryOrder") + public JsonResult checkHistoryOrder(@Validated @NotNull(message = "用户id不能为空") @RequestParam("userId") Long userId) { + EbikeOrder userOrders = ebikeOrderService.checkHistoryOrder(userId); + return JsonResult.success(userOrders); + } + + /** + * 完成骑行(关锁还车)。 + * + * @param endDto 骑行信息 + */ + @PostMapping("doneRide") + public JsonResult doneRide(@RequestBody @Validated EbikeUserCyclingEndDto endDto) { + ebikeOrderService.doneRide(endDto); + return JsonResult.success(); + } + + /** + * 订单支付。 + * + * @param orderPaymentDto 支付信息 + */ + @PostMapping("payment") + public JsonResult payment(@RequestBody @Validated FeignOrderPaymentDto orderPaymentDto) { + ebikeOrderService.payment(orderPaymentDto); + return JsonResult.success(); + } + + /** + * 订单退款申请。 + * + * @param orderId 订单ID + */ + @GetMapping("refundApply") + public JsonResult refundApply(@RequestParam("orderId") Long orderId) { + ebikeOrderService.refundApply(orderId); + return JsonResult.success(); + } + + /** + * 订单退款。 + * + * @param orderId 订单ID + */ + @GetMapping("refund") + public JsonResult refund(@RequestParam("orderId") Long orderId) { + ebikeOrderService.refund(orderId); + return JsonResult.success(); + } + + /** + * 订单退款完成。 + * + * @param orderId 订单ID + */ + @GetMapping("doneRefund") + public JsonResult doneRefund(@RequestParam("orderId") Long orderId) { + ebikeOrderService.doneRefund(orderId); + return JsonResult.success(); + } + + /** + * 订单退款失败。 + * + * @param orderId 订单ID + */ + @GetMapping("failRefund") + public JsonResult failRefund(@RequestParam("orderId") Long orderId) { + ebikeOrderService.failRefund(orderId); + return JsonResult.success(); + } + + /** + * 订单退款驳回。 + * + * @param orderId 订单ID + */ + @GetMapping("rejectRefund") + public JsonResult rejectRefund(@RequestParam("orderId") Long orderId) { + ebikeOrderService.rejectRefund(orderId); + return JsonResult.success(); + } + + /** + * 根据用户订单表主键获取详细信。 + * + * @param orderId 用户订单表主键 + */ + @GetMapping("getInfo/{orderId}") + public JsonResult getInfo(@PathVariable("orderId") Long orderId) { + EbikeOrder userOrder = ebikeOrderService.getById(orderId); + return JsonResult.success(userOrder); + } } diff --git a/ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingDto.java b/ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingDto.java index 9b8cfe2..0dba8c0 100644 --- a/ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingDto.java +++ b/ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingDto.java @@ -39,6 +39,6 @@ public class EbikeUserCyclingDto { @Column(typeHandler = PGpointTypeHandler.class) @JsonSerialize(using = PGpointSerializer.class) @JsonDeserialize(using = PGpointDeserializer.class) - @NotNull(message = "骑行起始点不能为空") + @NotNull(message = "骑行结束点不能为空") private PGpoint startPoint; } diff --git a/ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingEndDto.java b/ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingEndDto.java new file mode 100644 index 0000000..2a96731 --- /dev/null +++ b/ebike-user/src/main/java/com/cdzy/user/model/dto/EbikeUserCyclingEndDto.java @@ -0,0 +1,44 @@ +package com.cdzy.user.model.dto; + +import com.cdzy.user.handler.PGpointDeserializer; +import com.cdzy.user.handler.PGpointSerializer; +import com.cdzy.user.handler.PGpointTypeHandler; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.mybatisflex.annotation.Column; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.postgresql.geometric.PGpoint; + +/** + * 用户骑行结束信息 + * + * @author: yanglei + * @since: 2025-11-11 14:46 + */ +@Data +public class EbikeUserCyclingEndDto { + + /** + * 用户ID,关联用户表 + */ + @NotNull(message = "用户ID不能为空") + private Long userId; + + + /** + * 骑行设备编号(仅骑行订单有效) + */ + @NotBlank(message = "车辆编号不能为空") + private String bikeCode; + + /** + * 骑行结束点 + */ + @Column(typeHandler = PGpointTypeHandler.class) + @JsonSerialize(using = PGpointSerializer.class) + @JsonDeserialize(using = PGpointDeserializer.class) + @NotNull(message = "骑行起始点不能为空") + private PGpoint endPoint; +} diff --git a/ebike-user/src/main/java/com/cdzy/user/model/entity/EbikeOrder.java b/ebike-user/src/main/java/com/cdzy/user/model/entity/EbikeOrder.java index 78505a3..a58ad66 100644 --- a/ebike-user/src/main/java/com/cdzy/user/model/entity/EbikeOrder.java +++ b/ebike-user/src/main/java/com/cdzy/user/model/entity/EbikeOrder.java @@ -128,6 +128,31 @@ public class EbikeOrder implements Serializable { */ private Integer chargeDurationMinutes; + /** + * 禁停区调度费(元) + */ + private BigDecimal noParkingZoneFee; + + /** + * 停车区外调度费(元) + */ + private BigDecimal outOfParkingAreaFee; + + /** + * 运营区域外调度费(元) + */ + private BigDecimal outOfServiceAreaFee; + + /** + * 封顶金额(元) + */ + private BigDecimal maxFeeAmount; + + /** + * 头盔管理费(元) + */ + private BigDecimal helmetManagementFee; + /** * 地理位置GeoHash编码(用于区域优惠分析) */ diff --git a/ebike-user/src/main/java/com/cdzy/user/model/vo/EbikeOrderVo.java b/ebike-user/src/main/java/com/cdzy/user/model/vo/EbikeOrderVo.java index 97f4371..e7c80a6 100644 --- a/ebike-user/src/main/java/com/cdzy/user/model/vo/EbikeOrderVo.java +++ b/ebike-user/src/main/java/com/cdzy/user/model/vo/EbikeOrderVo.java @@ -18,7 +18,7 @@ public class EbikeOrderVo { private Long orderId; /** - * 订单编号 + * 订单创建时间 */ private LocalDateTime orderTime; } diff --git a/ebike-user/src/main/java/com/cdzy/user/service/EbikeOrderService.java b/ebike-user/src/main/java/com/cdzy/user/service/EbikeOrderService.java index 53b42eb..604f812 100644 --- a/ebike-user/src/main/java/com/cdzy/user/service/EbikeOrderService.java +++ b/ebike-user/src/main/java/com/cdzy/user/service/EbikeOrderService.java @@ -1,6 +1,7 @@ package com.cdzy.user.service; import com.cdzy.user.model.dto.EbikeUserCyclingDto; +import com.cdzy.user.model.dto.EbikeUserCyclingEndDto; import com.cdzy.user.model.entity.EbikeOrder; import com.cdzy.user.model.vo.EbikeOrderVo; import com.ebike.feign.model.dto.FeignEbikeDto; @@ -26,7 +27,7 @@ public interface EbikeOrderService extends IService { * @param orderDto 用户骑行信息 * @return 骑行订单 */ - EbikeOrderVo saveRide(EbikeUserCyclingDto orderDto); + EbikeOrder saveRide(EbikeUserCyclingDto orderDto); /** * 检查历史订单 @@ -91,4 +92,6 @@ public interface EbikeOrderService extends IService { * @return 车辆基本信息 */ FeignEbikeUserBikeInfo queryBikeInfo(String bikeCode); + + void doneRide(EbikeUserCyclingEndDto endDto); } diff --git a/ebike-user/src/main/java/com/cdzy/user/service/impl/EbikeOrderImpl.java b/ebike-user/src/main/java/com/cdzy/user/service/impl/EbikeOrderImpl.java index c58d6e4..b86edc8 100644 --- a/ebike-user/src/main/java/com/cdzy/user/service/impl/EbikeOrderImpl.java +++ b/ebike-user/src/main/java/com/cdzy/user/service/impl/EbikeOrderImpl.java @@ -9,10 +9,10 @@ import com.cdzy.user.enums.OrderType; import com.cdzy.user.mapper.EbikeOrderMapper; import com.cdzy.user.model.dto.EbikeUnlockResultDto; import com.cdzy.user.model.dto.EbikeUserCyclingDto; +import com.cdzy.user.model.dto.EbikeUserCyclingEndDto; import com.cdzy.user.model.entity.EbikeOrder; import com.cdzy.user.model.vo.EbikeOrderVo; import com.cdzy.user.service.EbikeOrderService; -import com.cdzy.user.utils.RedisUtil; import com.ebike.feign.clients.OperationsFeignClient; import com.ebike.feign.model.dto.FeignEbikeDto; import com.ebike.feign.model.dto.FeignEbikeUserBikeInfo; @@ -27,6 +27,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.time.Duration; import java.time.LocalDateTime; import java.util.List; import java.util.Objects; @@ -52,7 +54,7 @@ public class EbikeOrderImpl extends ServiceImpl im @Transactional @Override - public EbikeOrderVo saveRide(EbikeUserCyclingDto orderDto) { + public EbikeOrder saveRide(EbikeUserCyclingDto orderDto) { Long userId = orderDto.getUserId(); String bikeCode = orderDto.getBikeCode(); // 校验用户当前是否存在订单 @@ -65,29 +67,16 @@ public class EbikeOrderImpl extends ServiceImpl im throw new EbikeException("请完成未支付订单后再试"); } } - // 创建订单 - FeignEbikeUserBikeInfo bikeInfo = queryBikeInfo(bikeCode); - if (bikeInfo == null) { - log.error("开锁成功后查询车辆信息为空, bikeCode: {}, userId: {}", bikeCode, userId); - throw new EbikeException("当前车辆信息不存在"); - } EbikeOrder order = EbikeOrder.builder() .userId(userId) - .operatorId(bikeInfo.getOperatorId()) .bikeCode(orderDto.getBikeCode()) .orderType(OrderType.ONCE) .startLocation(orderDto.getStartPoint()) .startTime(LocalDateTime.now()) - .baseFee(bikeInfo.getBaseFee()) - .durationFee(bikeInfo.getDurationFee()) - .freeDurationMinutes(bikeInfo.getFreeDurationMinutes()) - .chargeDurationMinutes(bikeInfo.getChargeDurationMinutes()) .createTime(LocalDateTime.now()) .build(); - save(order); - try { // 尝试开锁 EbikeUnlockResultDto unlockResult = attemptUnlockWithValidation(bikeCode, userId); @@ -101,11 +90,7 @@ public class EbikeOrderImpl extends ServiceImpl im .updateTime(LocalDateTime.now()) .build(); updateById(orders); - - EbikeOrderVo userOrder = new EbikeOrderVo(); - userOrder.setOrderId(order.getOrderId()); - userOrder.setOrderTime(order.getCreateTime()); - return userOrder; + return orders; } catch (Exception e) { log.error("开锁失败, userId={}, bikeCode={}", userId, bikeCode, e); throw new RuntimeException("开锁失败", e); @@ -174,6 +159,49 @@ public class EbikeOrderImpl extends ServiceImpl im return jsonResult.getData(); } + @Transactional + @Override + public void doneRide(EbikeUserCyclingEndDto endDto) { + // 先校验是否有当前订单 + EbikeOrder order = getOrder(endDto); + order.setEndTime(LocalDateTime.now()); + order.setEndLocation(endDto.getEndPoint()); + // 获取车辆信息,计算费用 + FeignEbikeUserBikeInfo bikeInfo = queryBikeInfo(endDto.getBikeCode()); + BigDecimal totalAmount = costCalculation(order.getStartTime(), order.getEndTime(), bikeInfo); + order.setOrderStatus(OrderStatus.PENDING_PAYMENT); + order.setTotalAmount(totalAmount); + order.setActualAmount(totalAmount); + updateById(order); + // 关锁 + + //生成支付订单 + } + + private BigDecimal costCalculation(LocalDateTime startTime, LocalDateTime endTime, FeignEbikeUserBikeInfo bikeInfo) { + BigDecimal baseFee = bikeInfo.getBaseFee(); + BigDecimal durationFee = bikeInfo.getDurationFee(); + Integer freeDurationMinutes = bikeInfo.getFreeDurationMinutes(); + Integer chargeDurationMinutes = bikeInfo.getChargeDurationMinutes(); + // 骑行时长 + long totalRideMinutes = Duration.between(startTime, endTime).toMinutes(); + // 如果骑行时长小于等于免费时长,只收取起步费 + if (totalRideMinutes <= freeDurationMinutes) { + return baseFee; + } + // 计算计费时长 = 总时长 - 免费时长 + long chargeableMinutes = totalRideMinutes - freeDurationMinutes; + // 计算计费单位数量(向上取整):(计费时长 / 计费时长单位) + long chargeUnits = (chargeableMinutes + chargeDurationMinutes - 1) / chargeDurationMinutes; + // 计算总费用:起步费用 + 计费单位数量 * 时长费用 + BigDecimal durationCost = durationFee.multiply(BigDecimal.valueOf(chargeUnits)); + BigDecimal totalCost = baseFee.add(durationCost); + + log.info("费用计算: 总时长={}分钟, 免费时长={}分钟, 计费时长={}分钟, 计费单位={}个, 总费用={}", + totalRideMinutes, freeDurationMinutes, chargeableMinutes, chargeUnits, totalCost); + return totalCost; + } + /** * 校验车辆是否可用 * @@ -189,11 +217,15 @@ public class EbikeOrderImpl extends ServiceImpl im } } - - private EbikeOrder getOrder(EbikeUserCyclingDto orderDto) { + /** + * 查询当前用户骑行订单 + * + * @param endDto 用户骑行结束请求参数 + */ + private EbikeOrder getOrder(EbikeUserCyclingEndDto endDto) { QueryWrapper queryWrapper = QueryWrapper.create() - .where(EBIKE_ORDER.BIKE_CODE.eq(orderDto.getBikeCode())) - .where(EBIKE_ORDER.USER_ID.eq(orderDto.getUserId())) + .where(EBIKE_ORDER.BIKE_CODE.eq(endDto.getBikeCode())) + .where(EBIKE_ORDER.USER_ID.eq(endDto.getUserId())) .where(EBIKE_ORDER.ORDER_STATUS.eq(OrderStatus.IN_PROGRESS)); EbikeOrder userOrders = ebikeOrderTransactionMapper.selectOneByQuery(queryWrapper); if (userOrders == null) { diff --git a/ebike-user/src/main/resources/db/init.sql b/ebike-user/src/main/resources/db/init.sql index de2f191..50ae2c4 100644 --- a/ebike-user/src/main/resources/db/init.sql +++ b/ebike-user/src/main/resources/db/init.sql @@ -203,6 +203,11 @@ CREATE TABLE "public"."ebike_order" ( "duration_fee" numeric(10,2), "free_duration_minutes" int4, "charge_duration_minutes" int4, + "no_parking_zone_fee" numeric(10,2), + "out_of_parking_area_fee" numeric(10,2), + "out_of_service_area_fee" numeric(10,2), + "max_fee_amount" numeric(10,2), + "helmet_management_fee" numeric(10,2), "geo_hash" varchar(12) COLLATE "pg_catalog"."default", "start_location" point DEFAULT NULL, "end_location" point DEFAULT NULL, @@ -233,6 +238,11 @@ COMMENT ON COLUMN "public"."ebike_order"."base_fee" IS '起步费用'; COMMENT ON COLUMN "public"."ebike_order"."duration_fee" IS '时长费用'; COMMENT ON COLUMN "public"."ebike_order"."free_duration_minutes" IS '免费时长(分钟)'; COMMENT ON COLUMN "public"."ebike_order"."charge_duration_minutes" IS '时长(分钟)'; +COMMENT ON COLUMN "public"."ebike_order"."no_parking_zone_fee" IS '禁停区调度费(元)'; +COMMENT ON COLUMN "public"."ebike_order"."out_of_parking_area_fee" IS '停车区外调度费(元)'; +COMMENT ON COLUMN "public"."ebike_order"."out_of_service_area_fee" IS '运营区域外调度费(元)'; +COMMENT ON COLUMN "public"."ebike_order"."max_fee_amount" IS '封顶金额(元)'; +COMMENT ON COLUMN "public"."ebike_order"."helmet_management_fee" IS '头盔管理费(元)'; COMMENT ON COLUMN "public"."ebike_order"."geo_hash" IS '地理位置GeoHash编码(用于区域优惠分析)'; COMMENT ON COLUMN "public"."ebike_order"."start_location" IS '骑行初始点'; COMMENT ON COLUMN "public"."ebike_order"."end_location" IS '骑行还车点';