xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/SysRoleController.java
@@ -10,9 +10,7 @@ import com.xiaopeng.api.model.account.SysRole; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.web.page.PageInfo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.*; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Lazy; import org.springframework.validation.annotation.Validated; @@ -26,7 +24,6 @@ * @author mitao * @since 2026-02-03 */ @Api(tags = "角色管理") @RequiredArgsConstructor(onConstructor_ = @Lazy) @RestController public class SysRoleController { @@ -35,35 +32,35 @@ @ApiOperation("新增角色") @ApiOperation(value = "新增角色", tags = {"管理后台-角色管理"}) @PostMapping("/api/v1/role/add") public R<?> add(@Validated @RequestBody SysRoleAddDTO dto) { sysRoleService.addRole(dto); return R.ok(); } @ApiOperation("编辑角色") @ApiOperation(value = "编辑角色", tags = {"管理后台-角色管理"}) @PostMapping("/api/v1/role/edit") public R<?> edit(@Validated @RequestBody SysRoleEditDTO dto) { sysRoleService.editRole(dto); return R.ok(); } @ApiOperation("角色详情") @ApiOperation(value = "角色详情", tags = {"管理后台-角色管理"}) @GetMapping("/api/v1/role/queryById") public R<SysRoleDetailVO> queryById(@ApiParam(value = "角色id", required = true) @RequestParam Long id) { SysRoleDetailVO detail = sysRoleService.getRoleDetail(id); return R.ok(detail); } @ApiOperation("角色分页列表") @ApiOperation(value = "角色分页列表", tags = {"管理后台-角色管理"}) @PostMapping("/api/v1/role/pageList") public R<PageInfo<SysRolePageVO>> pageList(@Validated @RequestBody SysRolePageQuery query) { PageInfo<SysRolePageVO> pageInfo = sysRoleService.pageList(query); return R.ok(pageInfo); } @ApiOperation("删除角色") @ApiOperation(value = "删除角色", tags = {"管理后台-角色管理"}) @DeleteMapping("/api/v1/role/delete/{id}") public R<?> delete(@ApiParam(value = "角色id", required = true) @PathVariable Long id) { sysRoleService.deleteRole(id); @@ -94,4 +91,10 @@ Boolean checked = sysRoleService.checkMenuExistRole(menuId); return R.ok(checked); } @ApiOperation(value = "获取角色不分页列表", tags = {"管理后台-车队管理"}) @GetMapping("/api/v1/role/list") public R<?> getList() { return R.ok(sysRoleService.list()); } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/automaticPilot/PushOrderDto.java
@@ -1,5 +1,6 @@ package com.xiaopeng.api.dto.automaticPilot; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperationType; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -13,4 +14,10 @@ // 订单id @ApiModelProperty("订单id") private Long orderId; /** * 订单操作类型 */ private OrderOperationType operation_type; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/operation/ReassignOrderDTO.java
New file @@ -0,0 +1,24 @@ package com.xiaopeng.api.dto.operation; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; @Data @ApiModel(value = "改派订单参数") public class ReassignOrderDTO implements Serializable { @ApiModelProperty(value = "播单次数") private Integer count; @ApiModelProperty(value = "用户id") private String uid; @ApiModelProperty(value = "经度") private Double longitude; @ApiModelProperty(value = "纬度") private Double latitude; @ApiModelProperty(value = "订单号") private String orderNo; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/automatic/OrderMissionClientFallbackFactory.java
File was deleted xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/automaticPilot/OrderMissionClientFallbackFactory.java
New file @@ -0,0 +1,36 @@ package com.xiaopeng.api.factory.automaticPilot; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; import com.xiaopeng.common.core.domain.R; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; /** * 车辆整备Feign客户端降级工厂 * * @author mitao * @since 2026-02-11 */ @Slf4j @Component public class OrderMissionClientFallbackFactory implements FallbackFactory<OrderMissionClient> { @Override public OrderMissionClient create(Throwable cause) { return new OrderMissionClient() { @Override public R<Boolean> pushOrder(PushOrderDto dto) { log.error("远程调用获取检查点位信息异常:{}", cause.getMessage()); return R.fail("远程调用获取检查点位信息异常"); } @Override public R<Boolean> cancelOrder(PushOrderDto dto) { log.error("远程调用获取检查点位信息异常:{}", cause.getMessage()); return null; } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/ReassignOrderClientFallbackFactory.java
New file @@ -0,0 +1,26 @@ package com.xiaopeng.api.factory.operation; import com.xiaopeng.api.dto.operation.ReassignOrderDTO; import com.xiaopeng.api.feignClient.operation.ReassignOrderClient; import com.xiaopeng.common.core.domain.R; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.openfeign.FallbackFactory; public class ReassignOrderClientFallbackFactory implements FallbackFactory<ReassignOrderClient> { private static final Logger log = LoggerFactory.getLogger(ReassignOrderClientFallbackFactory.class); @Override public ReassignOrderClient create(Throwable cause) { log.error("改派服务调用失败: {}", cause.getMessage()); return new ReassignOrderClient(){ @Override public R<String> reassignOrder(ReassignOrderDTO dto) { return R.fail("改派派单失败,"+cause.getMessage()); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpElectronicClientFallbackFactory.java
@@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import java.util.Collection; @@ -30,6 +31,7 @@ return R.fail("获取电子围栏列表失败"); } @PostMapping("/api/v1/electronicFence/add") @Override public R add(ElectronicFenceAddDto dto) { log.error("添加电子围栏失败:{}", cause.getMessage()); xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpPricingClientFallbackFactory.java
@@ -34,10 +34,11 @@ } @Override public R<?> saveXpGeoQueryEntity(XpGeoQueryEntity xpGeoQueryEntity) { log.error("保存XpGeoQueryEntity失败", cause); public R<?> updateXpGeoQueryEntity(String orderId) { log.error("更新XpGeoQueryEntity失败", cause); return R.fail(); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/automaticPilot/OrderMissionClient.java
File was renamed from xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/automatic/OrderMissionClient.java @@ -1,20 +1,12 @@ package com.xiaopeng.api.feignClient.automatic; package com.xiaopeng.api.feignClient.automaticPilot; import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.factory.account.XpAppUserClientFallbackFactory; import com.xiaopeng.api.factory.automatic.OrderMissionClientFallbackFactory; import com.xiaopeng.api.model.account.XpOperationsUser; import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO; import com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; /** * 小程序用户Feign客户端 @@ -33,4 +25,13 @@ */ @PostMapping("/inner/pushOrder") R<Boolean> pushOrder(@RequestBody PushOrderDto dto); /** * 取消订单 * @param * @return */ @PostMapping("/inner/cancelOrder") R<Boolean> cancelOrder(@RequestBody PushOrderDto dto); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/ReassignOrderClient.java
New file @@ -0,0 +1,22 @@ package com.xiaopeng.api.feignClient.operation; import com.xiaopeng.api.dto.operation.ReassignOrderDTO; import com.xiaopeng.api.factory.operation.ReassignOrderClientFallbackFactory; import com.xiaopeng.api.factory.operation.XpSystemSettingClientFallbackFactory; import com.xiaopeng.api.model.operation.XpFeedbackType; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient(contextId = "ReassignOrderClient", value = ServiceNameConstantsEnum.XP_OPERATION_SERVICE, fallbackFactory = ReassignOrderClientFallbackFactory.class) public interface ReassignOrderClient { @PostMapping("/inner/v1/order/reassignOrder") R<String> reassignOrder(@RequestBody ReassignOrderDTO dto); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpPricingClient.java
@@ -27,8 +27,8 @@ /** * 保存XpGeoQueryEntity */ @PostMapping("/inner/saveXpGeoQueryEntity") R<?> saveXpGeoQueryEntity(@RequestBody XpGeoQueryEntity xpGeoQueryEntity); @PostMapping("/inner/updateXpGeoQueryEntity") R<?> updateXpGeoQueryEntity(@RequestParam String orderId); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/operation/XpGeoQueryEntity.java
@@ -30,7 +30,7 @@ * 时间戳 */ @Field("timestamp") private Date timestamp; private Long timestamp; /** * 秒数 @@ -81,11 +81,11 @@ this.location = location; } public Date getTimestamp() { public Long getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { public void setTimestamp(Long timestamp) { this.timestamp = timestamp; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrder.java
@@ -53,6 +53,7 @@ @ApiModelProperty(value = "订单来源 1=APP下单") private Integer orderSource; @ApiModelProperty(value = "乘车时间") private LocalDateTime rideTime; @@ -133,7 +134,8 @@ @ApiModelProperty(value = "时长分钟") private Double duration; @ApiModelProperty(value = "时长费所属时段type=1 普通时段 type=2 夜间行驶时段 3高峰时段 4节假日时段") private Integer durationType; @ApiModelProperty(value = "时长费") private BigDecimal durationMoney; @@ -145,7 +147,8 @@ @ApiModelProperty(value = "远途公里") private Double longDistance; @ApiModelProperty(value = "远途费所属时段type=1 普通时段 type=2 夜间行驶时段 3高峰时段 4节假日时段") private Integer longDistanceType; @ApiModelProperty(value = "远途费") private BigDecimal longDistancemoney; xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderDetail.java
New file @@ -0,0 +1,70 @@ package com.xiaopeng.api.model.order; import com.baomidou.mybatisplus.annotation.*; import com.xiaopeng.common.core.web.domain.BaseModel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; /** * <p> * 订单费用明细表 * </p> * * @author mitao * @since 2026-02-03 */ @Data @TableName("xp_order_detail") @ApiModel(value = "XpOrderDetail对象", description = "订单费用明细表") public class XpOrderDetail extends BaseModel { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) @ApiModelProperty(value = "自增id") private Long id; @ApiModelProperty(value = "订单id") private Long orderId; @ApiModelProperty(value = "type=1 普通时段 type=2 夜间行驶时段 3高峰时段 4节假日时段") private Integer type; @ApiModelProperty(value = "1=起步价 2=里程费") private Integer detailType; @ApiModelProperty(value = "时段") private String time; @ApiModelProperty(value = "时长分钟") private Integer duration; @ApiModelProperty(value = "里程") private BigDecimal mileage; @ApiModelProperty(value = "金额") private BigDecimal money; @TableLogic @ApiModelProperty(value = "是否删除 0=未删除 1=已删除") private Boolean isDeleted; @TableField(fill = FieldFill.INSERT) @ApiModelProperty(value = "创建时间") private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) @ApiModelProperty(value = "修改时间") private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT) @ApiModelProperty(value = "创建人") private String createBy; @TableField(fill = FieldFill.INSERT_UPDATE) @ApiModelProperty(value = "修改人") private String updateBy; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderEvaluate.java
@@ -30,7 +30,7 @@ private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; @ApiModelProperty(value = "订单id") xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/operation/RouteAndFareVo.java
@@ -1,5 +1,6 @@ package com.xiaopeng.api.vo.operation; import com.xiaopeng.api.vo.order.XpPricingCardVo; import io.swagger.annotations.ApiModel; @lombok.Data @@ -10,4 +11,7 @@ private FareVo fareEstimate; private XpPricingCardVo xpPricingCard; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java
@@ -15,7 +15,7 @@ private String time; @ApiModelProperty(value = "里程 单位公里") private BigDecimal mileage; @ApiModelProperty(value = "时间") @ApiModelProperty(value = "时间 分钟") private long duration; @ApiModelProperty(value = "金额") private BigDecimal money; xp-robotaxi-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -17,6 +17,7 @@ com.xiaopeng.api.factory.account.XpOperationUserClientFallbackFactory com.xiaopeng.api.factory.operation.XpElectronicClientFallbackFactory com.xiaopeng.api.factory.operation.XpOpenCityClientFallbackFactory com.xiaopeng.api.factory.operation.ReassignOrderClientFallbackFactory com.xiaopeng.api.factory.vehicleConvoy.UserVehicleClientFallbackFactory com.xiaopeng.api.factory.vehicleConvoy.XpFleetOperationsClientFallbackFactory com.xiaopeng.api.factory.pushMessage.XpPushMessageClientFallbackFactory xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -8,6 +8,8 @@ import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.RTTask; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskStatus; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskType; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperation; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperationType; import com.xiaopeng.automaticPilot.util.XPUUtil; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.utils.SnowFlake; @@ -52,4 +54,19 @@ } return R.ok(false); } @PostMapping("/inner/cancelOrder") R<Boolean> cancelOrder(@RequestBody PushOrderDto dto){ OrderOperation operation = new OrderOperation(); operation.setOrder_id(dto.getOrderId().toString()); operation.setTask_id("CANCEL_MISSION_"+ IdUtil.fastUUID()); operation.setOperation_type(dto.getOperation_type()); SnowFlake snowFlake = new SnowFlake(0, 0); snowFlake.withStartTimestamp(1420041600000L); long sequence = snowFlake.nextId(); xpuUtil.cloudOrderOperation(operation,dto.getVin(),sequence); redisService.setCacheObject(String.valueOf(sequence),"cancelOrder_"+dto.getOrderId()+"_"+ dto.getVin(),30l, TimeUnit.MINUTES); return R.ok(true); } } xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpVehicleRepairController.java
@@ -28,7 +28,6 @@ */ @Api(tags = "车辆维修") @RestController @RequestMapping("/vehicleRepair") public class XpVehicleRepairController { @Autowired xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/model/query/XpTaskRecordPageQuery.java
@@ -17,7 +17,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) @ApiModel(value = "任务/事故查询对象") @ApiModel(value = "任务事故查询对象") public class XpTaskRecordPageQuery extends BasePage { @NotNull(message = "车辆ID不能为空") xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceAreaServiceImpl.java
@@ -194,10 +194,10 @@ ArrayList<String> coordinates = new ArrayList<>(); List<Point> points = polygon.getPoints().getPoints(); for (Point point : points) { coordinates.add( point.getY()+","+point.getX() ); coordinates.add( point.getX()+","+point.getY() ); } String collect = coordinates.stream().collect(Collectors.joining(";")); if (ElectricFenceUtil.areaMonitoring(collect, dto.getLatitude()+","+dto.getLongitude())) { if (ElectricFenceUtil.areaMonitoring(collect, dto.getLongitude()+","+dto.getLatitude())) { String fenceId = polygon.getFenceId(); XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(fenceId); if(xpElectronicFence!=null){ xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/ReassignOrderController.java
New file @@ -0,0 +1,29 @@ package com.xiaopeng.operation.controller; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.api.dto.operation.ReassignOrderDTO; import com.xiaopeng.operation.utils.TaskOrderUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; /** * token 控制 * * @author ruoyi */ @Slf4j @RestController public class ReassignOrderController { @Autowired private TaskOrderUtil taskOrderUtil; @PostMapping("/inner/v1/order/reassignOrder") public R<String> reassignOrder(@RequestBody ReassignOrderDTO dto) { taskOrderUtil.reassignPushOrder(dto.getCount(),dto.getUid(),dto.getLongitude(),dto.getLatitude(),dto.getOrderNo()); return R.ok(); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpInvitationCodeController.java
@@ -129,7 +129,6 @@ } @Log(title = "邀请码导入", businessType = BusinessType.IMPORT) @ApiOperation(value = "邀请码导入") @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW) @PostMapping("/importExcel") public R<String> importExcel(@RequestPart("file") MultipartFile file) { ImportParams params = new ImportParams(); xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPointManagementController.java
@@ -48,7 +48,6 @@ } // 获取电子围栏下启用的运营区域 List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().in(XpElectronicFenceArea::getElectronicId, electronicFenceIds) .eq(XpElectronicFenceArea::getAreaType, 1) .eq(XpElectronicFenceArea::getAreaStatus, 1).list(); return R.ok(electronicFenceAreaList); } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPricingController.java
@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.Date; /** * <p> @@ -49,29 +50,28 @@ } @PostMapping("/base/v1/mini-program/navigation/route-and-fare") @ApiOperation(value = "导航-获取路线和费用",tags = {"小程序-获取路线和费用"},response = RouteAndFareVo.class) @ApiOperation(value = "导航-获取路线和费用", tags = {"小程序-获取路线和费用"}, response = RouteAndFareVo.class) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<RouteAndFareVo> getRouteAndFare(@RequestBody @Valid RouteAndFareDto routeAndFareDto, HttpServletRequest request) { // uid已通过拦截器otp校验,可以直接使用 String uid = request.getHeader("XP-Uid"); return R.ok(xpPricingService.getRouteAndFare(routeAndFareDto,uid)); return R.ok(xpPricingService.getRouteAndFare(routeAndFareDto, uid)); } // 根据开通城市 获取定价 @GetMapping("/getPricingByCityId/{cityId}") public R<XpPricing> getPricingByCityId(@PathVariable("cityId")Long cityId) { public R<XpPricing> getPricingByCityId(@PathVariable("cityId") Long cityId) { XpPricing pricing = xpPricingService.lambdaQuery().eq(XpPricing::getCityId, cityId).last("limit 1").one(); return R.ok(pricing); } @GetMapping("/base/v1/mini-program/pricing/rules/{openCityId}") @ApiOperation(value = "获取费用规则",tags = {"小程序-根据开通城市获取费用规则"},response = String.class) @ApiOperation(value = "获取费用规则", tags = {"小程序-根据开通城市获取费用规则"}, response = String.class) public R<?> getPricingRules(@PathVariable Long openCityId) { XpPricing xpPricing = xpPricingService.getOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, openCityId).eq(XpPricing::getPricingType, 1).last("limit 1")); if(xpPricing!=null){ if (xpPricing != null) { return R.ok(xpPricing.getPriceRule()); } return R.ok(""); @@ -84,20 +84,20 @@ * @return */ @PostMapping("/base/v1/mini-program/pricing/getWaitTime") @ApiOperation(value = "获取等待时间",tags = {"小程序-获取等待时间"},response = Integer.class) public R<Integer> getWaitTime(@RequestBody LonLatDto dto){ return R.ok(xpPricingService.getWaitTime(dto.getLongitude(),dto.getLatitude())); @ApiOperation(value = "获取等待时间", tags = {"小程序-获取等待时间"}, response = Integer.class) public R<Integer> getWaitTime(@RequestBody LonLatDto dto) { return R.ok(xpPricingService.getWaitTime(dto.getLongitude(), dto.getLatitude())); } /** * 保存信息 * @param xpGeoQueryEntity * @param * @return */ @PostMapping("/inner/saveXpGeoQueryEntity") public R<?> saveXpGeoQueryEntity(@RequestBody XpGeoQueryEntity xpGeoQueryEntity){ xpGeoQueryEntityService.save(xpGeoQueryEntity); @PostMapping("/inner/updateXpGeoQueryEntity") public R<?> updateXpGeoQueryEntity(@RequestParam String orderId) { xpGeoQueryEntityService.updateByOrderId(orderId); return R.ok(); } @@ -106,7 +106,7 @@ * 播单 */ @PostMapping("/base/v1/mini-program/order/save") @ApiOperation(value = "保存订单",tags = {"小程序-保存订单"}) @ApiOperation(value = "保存订单", tags = {"小程序-保存订单"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<SaveOrderVo> saveOrder(@RequestBody @Validated XpOrderDto dto, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); @@ -115,8 +115,6 @@ saveOrderVo.setOrderId(l); return R.ok(saveOrderVo); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpRegionController.java
@@ -1,28 +1,22 @@ package com.xiaopeng.operation.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.xiaopeng.api.model.operation.XpOpenCity; import com.xiaopeng.api.model.operation.XpRegion; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.api.model.operation.XpRegion; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.operation.query.getProvinceQuery; import com.xiaopeng.operation.service.XpOpenCityService; import com.xiaopeng.operation.service.XpRegionService; import com.xiaopeng.operation.vo.RegionDistrictSyncResultVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -105,5 +99,13 @@ return R.ok(list); } } /** * 管理后台-同步区级数据 */ @ApiOperation(value = "管理后台-同步区级数据", tags = {"管理后台-省市区管理"}) @PostMapping("/api/v1/region/syncDistrict") public R<RegionDistrictSyncResultVO> syncDistrict() { return R.ok(regionService.syncDistrictFromRegionAll()); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/mapper/XpRegionMapper.java
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiaopeng.api.model.operation.XpRegion; import java.util.List; /** * <p> * 省市区三级联动 Mapper 接口 @@ -14,4 +16,22 @@ */ public interface XpRegionMapper extends BaseMapper<XpRegion> { /** * 查询缺失的区级数据 * @return 待同步区级数据 */ List<XpRegion> selectMissingDistrictFromRegionAll(); /** * 查询可参与同步的市级总数 * @return 市级总数 */ Long countCityForDistrictSync(); /** * 查询候选区级总数(无论是否已存在) * @return 候选区级总数 */ Long countCandidateDistrictForSync(); } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/IXpGeoQueryEntityService.java
@@ -20,6 +20,9 @@ String time ); /** * 保存地理查询实体 * @param entity 地理查询实体 @@ -27,6 +30,12 @@ */ XpGeoQueryEntity save(XpGeoQueryEntity entity); /** * 修改 */ XpGeoQueryEntity updateByOrderId(String orderId); /** * 根据ID查询实体 * @param id 实体ID xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpRegionService.java
@@ -3,6 +3,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.operation.XpRegion; import com.xiaopeng.operation.vo.RegionDistrictSyncResultVO; import java.util.List; @@ -22,4 +23,10 @@ */ List<List<XpRegion>> getXpRegion(); /** * 从xp_region_all同步区级数据到xp_region * @return 同步结果 */ RegionDistrictSyncResultVO syncDistrictFromRegionAll(); } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpBannerServiceImpl.java
@@ -27,7 +27,6 @@ @Override public List<XpBannerListVo> getXpBannerList() { LambdaQueryWrapper<XpBanner> wrapper = new LambdaQueryWrapper<>(); // TODO 魔法数字 wrapper.eq(XpBanner::getBannerStatus,1); // 当前时间在开始和结束时间范围内 wrapper.ge(XpBanner::getStartTime,System.currentTimeMillis()); xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpElectronicFenceServiceImpl.java
@@ -229,12 +229,21 @@ area.setArea(area.getArea()); area.setCityCode(dto.getCityCode()); xpElectronicFenceAreaMapper.insert(area); } }else{ // 设置电子围栏ID并保存区域数据 for (XpElectronicFenceArea area : newElectronicFenceAreas) { area.setElectronicId(xpElectronicFence.getId()); area.setAreaStatus(2); area.setArea(area.getArea()); area.setCityCode(dto.getCityCode()); xpElectronicFenceAreaMapper.insert(area); } } if (dto.getStatus() == 2) { if (xpElectronicFence.getStatus() == 2) { for (XpElectronicFenceArea newElectronicFenceArea : newElectronicFenceAreas) { if (newElectronicFenceArea.getAreaType() == 1 && newElectronicFenceArea.getAreaStatus() == 1) { @@ -363,11 +372,24 @@ // 删除旧的区域数据,然后保存新的区域数据 xpElectronicFenceAreaMapper.delete(new LambdaQueryWrapper<XpElectronicFenceArea>() .eq(XpElectronicFenceArea::getElectronicId, dto.getId())); for (XpElectronicFenceArea area : updatedElectronicFenceAreas) { area.setElectronicId(dto.getId()); // 设置关联的电子围栏ID area.setCityCode(dto.getCityCode()); area.setArea(area.getArea()); xpElectronicFenceAreaMapper.insert(area); if (xpElectronicFence.getStatus() == 2) { // 设置电子围栏ID并保存区域数据 for (XpElectronicFenceArea area : updatedElectronicFenceAreas) { area.setElectronicId(xpElectronicFence.getId()); area.setAreaStatus(1); area.setArea(area.getArea()); area.setCityCode(dto.getCityCode()); xpElectronicFenceAreaMapper.insert(area); } }else{ // 设置电子围栏ID并保存区域数据 for (XpElectronicFenceArea area : updatedElectronicFenceAreas) { area.setElectronicId(xpElectronicFence.getId()); area.setAreaStatus(2); area.setArea(area.getArea()); area.setCityCode(dto.getCityCode()); xpElectronicFenceAreaMapper.insert(area); } } if (dto.getStatus() == 2) { for (XpElectronicFenceArea newElectronicFenceArea : updatedElectronicFenceAreas) { xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpGeoQueryEntityServiceImpl.java
@@ -1,17 +1,25 @@ package com.xiaopeng.operation.service.impl; import com.xiaopeng.api.model.operation.XpGeoQueryEntity; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.operation.repository.XpGeoQueryEntityRepository; import com.xiaopeng.operation.service.IXpGeoQueryEntityService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Service; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.stream.Collectors; /** * MongoDB 地理查询服务实现类 @@ -21,6 +29,9 @@ @Autowired private XpGeoQueryEntityRepository repository; @Autowired private MongoTemplate mongoTemplate; /** * 通过经纬度查询固定范围500米内、一周内、指定时段的数据 @@ -35,26 +46,43 @@ double latitude, String time ) { // 创建中心点坐标 Point point = new Point(longitude, latitude); // 设置距离范围为500米 Distance distance = new Distance(0.5, Metrics.KILOMETERS); // 0.5公里 = 500米 // 计算一周前的时间 // // 计算一周前的时间 Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.DAY_OF_YEAR, -7); Date startTime = calendar.getTime(); Long startTime = calendar.getTime().getTime(); // 执行查询 - 使用新的方法,包含时段过滤 return repository.findByLocationNearAndTimestampAfterAndTimeAndStatusGreaterThan( point, 500, startTime, time, // 时段过滤 1 // 状态大于1 // 1. 创建查询点 Point referencePoint = new Point(longitude, latitude); // 2. 构建 NearQuery - 完全使用 NearQuery 的 API NearQuery nearQuery = NearQuery.near(referencePoint, Metrics.KILOMETERS) .maxDistance(0.5) // 转成公里 .spherical(true); // 3.时间>startTime time = time参数 // 3. 创建额外的查询条件 Query query = new Query(); query.addCriteria(Criteria.where("timestamp").gte(startTime)); query.addCriteria(Criteria.where("time").is(time)); // 将 NearQuery 和额外的查询条件合并 nearQuery.query(query); // 4. 执行查询 - 明确指定集合名 GeoResults<XpGeoQueryEntity> geoResults = mongoTemplate.geoNear( nearQuery, XpGeoQueryEntity.class, "xp_geo_query_entity" // 明确指定集合名,避免映射错误 ); // 5. 提取结果 return geoResults.getContent().stream() .map(geoResult -> geoResult.getContent()) .collect(Collectors.toList()); } /** @@ -67,6 +95,25 @@ return repository.save(entity); } @Override public XpGeoQueryEntity updateByOrderId(String orderId) { Query query = new Query(Criteria.where("orderId").is(orderId)); XpGeoQueryEntity xpGeoQueryEntity = mongoTemplate.findOne(query, XpGeoQueryEntity.class); Long timestamp = xpGeoQueryEntity.getTimestamp(); long time = new Date().getTime(); // 计算2个时间的秒数差 long seconds = (time- timestamp) / 1000; Update update = new Update().set("seconds", seconds).set("status", 1); if(xpGeoQueryEntity.getStatus()==0){ mongoTemplate.findAndModify(query, update, XpGeoQueryEntity.class); } return null; } /** * 根据ID查询实体 * @param id 实体ID xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -20,6 +20,7 @@ import com.xiaopeng.api.vo.operation.FareVo; import com.xiaopeng.api.vo.operation.RouteAndFareVo; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.api.vo.order.XpPricingCardVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; @@ -35,12 +36,13 @@ import com.xiaopeng.operation.utils.XpPricingUtil; import com.xiaopeng.operation.utils.holiday.HolidayUtil; import com.xiaopeng.operation.utils.map.FastDistanceCalculator; import com.xiaopeng.operation.vo.XpPricingCardVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import javax.annotation.Resource; import java.time.Duration; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.*; @@ -89,8 +91,6 @@ @Resource private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper; @Resource private XpVehicleClient xpVehicleClient; @Resource private XpPointMapper xpPointMapper; @@ -206,6 +206,8 @@ // ); XpPricingCardVo xpPricingCardVo = new XpPricingCardVo(); // 3. 并行执行远程服务调用 判断是否同一经营区域 CompletableFuture<R<Boolean>> diagnosisAreaFuture = CompletableFuture.supplyAsync( () -> xpMapClient.diagnosisArea(routeAndFareDto), @@ -274,6 +276,9 @@ RouteAndFareVo routeAndFareVo = new RouteAndFareVo(); // routeAndFareVo.setRoute(routeVo); routeAndFareVo.setXpPricingCard(xpPricingCardVo); FareVo fareVo = new FareVo(); // fareVo.setEstimatedDistanceKm(routeVo.getDistance().doubleValue() / 1000); // fareVo.setEstimatedDurationMin(routeVo.getDuration() / 60); @@ -333,7 +338,7 @@ return 1; } else { // 已完成接单的数据 List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() > 1).collect(Collectors.toList()); List<XpGeoQueryEntity> validEntities = entities.stream().filter(e -> e.getStatus() == 1).collect(Collectors.toList()); // 计算平均等待时间 OptionalDouble averageOptional = validEntities.stream().mapToDouble(XpGeoQueryEntity::getSeconds).average(); @@ -347,6 +352,8 @@ double waitMinutes = (average * count) / 60; if (waitMinutes > 3) { throw new ServiceException("当前区域正在等待中,请耐心等待", I18nKeyConstant.WAITING_TIME); }else if(waitMinutes==0){ return 1; } return (int) waitMinutes; } @@ -415,6 +422,8 @@ // 生成订单数据 XpOrder xpOrder = new XpOrder(); xpOrder.setVehicleId(1l); // xpOrder.setStartMileage(Double.valueOf(startMileage)); @@ -517,6 +526,16 @@ xpOrder.setQueueName(s); xpOrderClient.updateOrder(xpOrder); // 存储mongodb 数据 XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); xpGeoQueryEntity.setOrderId(orderId.toString()); xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(),xpPoint.getPointLat())); // 计算2个时间的秒数 xpGeoQueryEntity.setTime(currentTimeSlot); xpGeoQueryEntity.setStatus(0); xpGeoQueryEntity.setTimestamp(System.currentTimeMillis()); geoQueryEntityService.save(xpGeoQueryEntity); return orderId; } } @@ -525,6 +544,18 @@ redisService.setCacheList(key, Arrays.asList(orderId + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + xpOrder.getOrderNumber() + "_" + distance + "_" + duration + "_" + routeAndFareDto.getRoutePlanRule())); xpOrder.setQueueName(key); xpOrderClient.updateOrder(xpOrder); // 存储mongodb 数据 XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); xpGeoQueryEntity.setOrderId(orderId.toString()); xpGeoQueryEntity.setLocation(new GeoJsonPoint(xpPoint.getPointLon(),xpPoint.getPointLat())); // 计算2个时间的秒数 xpGeoQueryEntity.setTime(currentTimeSlot); xpGeoQueryEntity.setStatus(0); geoQueryEntityService.save(xpGeoQueryEntity); return orderId; // xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpRegionServiceImpl.java
@@ -8,11 +8,14 @@ import com.xiaopeng.operation.mapper.XpRegionMapper; import com.xiaopeng.api.model.operation.XpRegion; import com.xiaopeng.operation.service.XpRegionService; import com.xiaopeng.operation.vo.RegionDistrictSyncResultVO; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** @@ -64,4 +67,29 @@ redisService.setCacheList(CacheConstants.XP_REGION, result); return result; } @Override @Transactional(rollbackFor = Exception.class) public RegionDistrictSyncResultVO syncDistrictFromRegionAll() { Long cityCount = Objects.requireNonNullElse(this.baseMapper.countCityForDistrictSync(), 0L); Long candidateDistrictCount = Objects.requireNonNullElse(this.baseMapper.countCandidateDistrictForSync(), 0L); List<XpRegion> missingDistrictList = this.baseMapper.selectMissingDistrictFromRegionAll(); long insertedCount = 0L; if (missingDistrictList != null && !missingDistrictList.isEmpty()) { this.saveBatch(missingDistrictList); insertedCount = missingDistrictList.size(); } redisService.deleteObject(CacheConstants.XP_REGION); redisService.deleteObject(CacheConstants.XP_REGION_CITY); RegionDistrictSyncResultVO result = new RegionDistrictSyncResultVO(); result.setCityCount(cityCount); result.setCandidateDistrictCount(candidateDistrictCount); result.setInsertedCount(insertedCount); result.setSkippedCount(Math.max(candidateDistrictCount - insertedCount, 0)); result.setMessage("区级数据同步完成"); return result; } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -1,14 +1,15 @@ package com.xiaopeng.operation.utils; import cn.hutool.core.bean.BeanUtil; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.dto.map.RouteAndFareDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.dto.operation.VehiclePositionByVinDto; import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; import com.xiaopeng.api.feignClient.map.XpMapClient; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient; import com.xiaopeng.api.feignClient.vehicleConvoy.VehicleControlClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.operation.XpElectronicFence; import com.xiaopeng.api.model.operation.XpTradingStrategy; @@ -16,6 +17,7 @@ import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.api.vo.operation.VehiclePositionVo; import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; @@ -25,8 +27,8 @@ import com.xiaopeng.operation.service.IXpTradingStrategyService; import com.xiaopeng.operation.service.XpPointService; import com.xiaopeng.operation.utils.mango.VehiclePositionService; import com.xiaopeng.api.vo.operation.VehiclePositionVo; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; @@ -39,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @Component public class TaskOrderUtil { @@ -72,6 +75,9 @@ @Autowired private XpMapClient xpMapClient; // @Autowired private OrderMissionClient orderMissionClient; // 自定义线程池 @@ -102,7 +108,7 @@ String[] split = firstElement.toString().split("_"); // 如果未处理 创建新任务 if (split[2].equals("0")) { String value = split[0] + "_" + split[1] + "_1" + "_"+split[3] + "_"+ split[4] + "_"+ split[5] + "_"+ split[6] + "_"+ split[7]; String value = split[0] + "_" + split[1] + "_1" + "_" + split[3] + "_" + split[4] + "_" + split[5] + "_" + split[6] + "_" + split[7]; // 状态改为处理中 redisService.updateFirst(name, value); threadPoolTaskExecutor.submit(() -> { @@ -220,8 +226,8 @@ @SneakyThrows private void processPushOrder(String quoteName, int count, String uid, String[] split, String orderNo, XpTradingStrategy xpTradingStrategy, String distance, String duration, String planRule) { System.out.println("进入推单环环节"); if(count==0){ count=1; if (count == 0) { count = 1; } // 订单状态 R<XpOrder> xpOrderR = xpOrderClient.queryOrderByOrderNo(orderNo); @@ -596,7 +602,142 @@ // 订单价值 if (Objects.equals(secondMax, tendencyOrderValue)) { // 订单价值 // 时间差权重 Integer weightTimeDifference = xpTradingStrategy.getWeightTimeDifference(); // 距离差权重 Integer weightDistanceDifference = xpTradingStrategy.getWeightDistanceDifference(); // 距离权重 Integer weightDistance = xpTradingStrategy.getWeightDistance(); // 时间权重 Integer weightTime = xpTradingStrategy.getWeightTime(); // 电量权重 Integer weightElectricity = xpTradingStrategy.getWeightElectricity(); // 找出最大的值是那个 Integer max1 = Math.max(weightTimeDifference, Math.max(weightDistanceDifference, Math.max(weightDistance, Math.max(weightTime, weightElectricity)))); // 时间差 if (max1.equals(weightTimeDifference)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.valueOf(split[0])); lonLatDto1.setLatitude(Double.valueOf(split[1])); routeAndFareDto.setDestination(lonLatDto1); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (distanceAndDuration.getCode() == 200) { // 送驾- 接驾 Integer durationDifference = Integer.parseInt(duration) - distanceAndDuration.getData().getDuration(); vehiclePosition.setTimeDifference(durationDifference); } } } if (max1.equals(weightDistanceDifference)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.valueOf(split[0])); lonLatDto1.setLatitude(Double.valueOf(split[1])); routeAndFareDto.setDestination(lonLatDto1); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (distanceAndDuration.getCode() == 200) { // 送驾- 接驾 Integer distanceDifference = Integer.parseInt(distance) - distanceAndDuration.getData().getDistance(); vehiclePosition.setDistanceDifference(distanceDifference); } } } if (max1.equals(weightDistance)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.parseDouble(split[0])); lonLatDto1.setLatitude(Double.parseDouble(split[1])); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); routeAndFareDto.setDestination(lonLatDto1); // 调用地图 获取时间 R<RouteVo> r = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (r.getCode() == 200) { vehiclePosition.setDistance(Double.valueOf(r.getData().getDistance())); } else { vehiclePosition.setDistance(0.0); } } } if (max1.equals(weightTime)) { for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { RouteAndFareDto routeAndFareDto = new RouteAndFareDto(); LonLatDto lonLatDto = new LonLatDto(); lonLatDto.setLongitude(vehiclePosition.getPoint().getX()); lonLatDto.setLatitude(vehiclePosition.getPoint().getY()); routeAndFareDto.setOrigin(lonLatDto); LonLatDto lonLatDto1 = new LonLatDto(); lonLatDto1.setLongitude(Double.parseDouble(split[0])); lonLatDto1.setLatitude(Double.parseDouble(split[1])); routeAndFareDto.setRoutePlanRule(Integer.valueOf(planRule)); routeAndFareDto.setDestination(lonLatDto1); // 调用地图 获取时间 R<RouteVo> r = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (r.getCode() == 200) { vehiclePosition.setSecond(r.getData().getDuration()); } else { vehiclePosition.setSecond(0); } } } if (max1.equals(weightElectricity)) { // 计算车的分数 分数最大是车的size availableVehiclesNearby = availableVehiclesNearby.stream().sorted(Comparator.comparing(VehiclePositionVo::getCurrentElectricity).reversed()).collect(Collectors.toList()); int size1 = tendencyElectricity; // 做比对的分数 int temp1 = availableVehiclesNearby.get(0).getCurrentElectricity(); for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { vehiclePosition.setCurrentElectricityScore(vehiclePosition.getCurrentElectricityScore() + size1); if (temp1 != vehiclePosition.getCurrentElectricity()) { size1--; } } } // 算出总分 再来排序计算 for (VehiclePositionVo vehiclePositionVo : availableVehiclesNearby) { vehiclePositionVo.setOrderValueTotal(vehiclePositionVo.getCurrentElectricityScore() + vehiclePositionVo.getDistanceScore() + vehiclePositionVo.getTimeScore() + vehiclePositionVo.getDistanceDifference() + vehiclePositionVo.getTimeDifference()); } availableVehiclesNearby = availableVehiclesNearby.stream().sorted(Comparator.comparing(VehiclePositionVo::getOrderValueTotal).reversed()).collect(Collectors.toList()); int size2 = tendencyOrderValue; // 做比对的分数 double temp2 = availableVehiclesNearby.get(0).getOrderValueTotal(); for (VehiclePositionVo vehiclePosition : availableVehiclesNearby) { vehiclePosition.setValueScore(vehiclePosition.getValueScore() + size2); if (temp2 != vehiclePosition.getOrderValueTotal()) { size2--; } } } } @@ -616,7 +757,7 @@ RLock lock = redissonClient.getLock("FIND_CAR_" + vehiclePositionVo.getVin()); lock.lock(10, TimeUnit.SECONDS); System.err.println("队列:"+quoteName+" 车辆:"+vehiclePositionVo.getVin()+"订单"+orderNo); System.err.println("队列:" + quoteName + " 车辆:" + vehiclePositionVo.getVin() + "订单" + orderNo); try { // VehiclePosition vehiclePosition = vehiclePositionService.findByVehicleId(vehiclePositionVo.getVehicle_id()); R<XpVehicle> vehiclePositionR = xpVehicleClient.findByVehicleVin(vehiclePositionVo.getVin()); @@ -630,27 +771,15 @@ } // 关闭巡游单子 TODO AddTaxiPassengerVo addTaxiPassengerVo = new AddTaxiPassengerVo(); addTaxiPassengerVo.setVin(vehiclePosition.getVehicleVin()); addTaxiPassengerVo.setUid(uid); addTaxiPassengerVo.setOrderNo(orderNo); // TODO appid addTaxiPassengerVo.setAppId(""); PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(xpOrder.getId()); pushOrderDto.setVin(vehiclePosition.getVehicleVin()); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo); if (baseVehicleUserDto!=null && baseVehicleUserDto.getCode() == 200) { // TODO 下单 // 调用推送服务 orderId + carVin orderMissionClient.pushOrder(pushOrderDto); // 调用推送服务 orderId + carVin // 绑定成功 弹出这个队列第一个订单 redisService.popFirstElement(quoteName); return; } } catch (Exception e) { e.printStackTrace(); } finally { @@ -666,33 +795,25 @@ lock.lock(10, TimeUnit.SECONDS); try { // 查询车辆状态 R<XpVehicle> vehicleR = xpVehicleClient.findByVehicleVin(vehicleVin); if(vehicleR.getCode()!=200){ continue; } XpVehicle vehicleId1 = vehicleR.getData(); R<XpVehicle> vehicleR = xpVehicleClient.findByVehicleVin(vehicleVin); if (vehicleR.getCode() != 200) { continue; } XpVehicle vehicleId1 = vehicleR.getData(); if (vehicleId1.getStatus() != 1 || vehicleId1.getState() != 0) { lock.unlock(); continue; } if (vehiclePosition.getHasOrder() == 1) { // 关闭巡游单子 TODO } AddTaxiPassengerVo addTaxiPassengerVo = new AddTaxiPassengerVo(); addTaxiPassengerVo.setVin(vehiclePosition.getVin()); addTaxiPassengerVo.setUid(uid); addTaxiPassengerVo.setOrderNo(orderNo); // TODO appid addTaxiPassengerVo.setAppId(""); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo); if (baseVehicleUserDto.getCode() == 200) { // 关闭巡游单子 TODO PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(xpOrder.getId()); pushOrderDto.setVin(vehiclePosition.getVin()); redisService.popFirstElement(quoteName); lock.unlock(); return; } // 调用推送服务 orderId + carVin orderMissionClient.pushOrder(pushOrderDto); } catch (Exception e) { } finally { @@ -704,5 +825,70 @@ } } /** * 订单处理 * @param count 轮数 * @param uid * @param longitude 经度 * @param latitude 纬度 * @param orderNo 订单号 */ @SneakyThrows public void reassignPushOrder(int count, String uid, Double longitude, Double latitude, String orderNo) { System.out.println("进入推单环环节"); if (count == 0) { count = 1; } // 订单状态 R<XpOrder> xpOrderR = xpOrderClient.queryOrderByOrderNo(orderNo); if (xpOrderR.getCode() != 200) { return; } if (xpOrderR.getData().getStatus() != 1) { return; } XpOrder data = xpOrderR.getData(); XpTradingStrategy xpTradingStrategy = xpTradingStrategyService.getOne(null); for (int i = 0; i < count; i++) { log.info("进入找车环节"); List<VehiclePosition> positionList = vehiclePositionService.findAvailableVehiclesNearby(longitude, latitude, xpTradingStrategy.getOnePushDistance(), xpTradingStrategy.getSimultaneouslyOrderCount()); // 获取车辆的区域信息 if (positionList.isEmpty()) { continue; } // 直接派单 定义一个锁来锁定车辆 VehiclePosition vehiclePosition = positionList.get(0); String vehicleVin = vehiclePosition.getVin(); RLock lock = redissonClient.getLock("FIND_CAR_" + vehiclePosition.getVin()); lock.lock(10, TimeUnit.SECONDS); try { // 查询车辆状态 R<XpVehicle> vehicleR = xpVehicleClient.findByVehicleVin(vehicleVin); if (vehicleR.getCode() != 200) { continue; } XpVehicle vehicleId1 = vehicleR.getData(); if (vehicleId1.getStatus() != 1 || vehicleId1.getState() != 0) { lock.unlock(); continue; } // 关闭巡游单子 TODO PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(data.getId()); pushOrderDto.setVin(vehiclePosition.getVin()); // 调用推送服务 orderId + carVin orderMissionClient.pushOrder(pushOrderDto); } catch (Exception e) { } finally { lock.unlock(); } } } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mapper/XpOrderDetailMapper.java
New file @@ -0,0 +1,17 @@ package com.xiaopeng.order.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiaopeng.api.model.order.XpOrderCancel; import com.xiaopeng.api.model.order.XpOrderDetail; /** * <p> * 订单取消 Mapper 接口 * </p> * * @author mitao * @since 2026-02-03 */ public interface XpOrderDetailMapper extends BaseMapper<XpOrderDetail> { } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpOrderDetailService.java
New file @@ -0,0 +1,17 @@ package com.xiaopeng.order.service; import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.order.XpOrderCancel; import com.xiaopeng.api.model.order.XpOrderDetail; /** * <p> * 订单取消 服务类 * </p> * * @author mitao * @since 2026-02-03 */ public interface IXpOrderDetailService extends IService<XpOrderDetail> { } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderDetailServiceImpl.java
New file @@ -0,0 +1,23 @@ package com.xiaopeng.order.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.model.order.XpOrderCancel; import com.xiaopeng.api.model.order.XpOrderDetail; import com.xiaopeng.order.mapper.XpOrderCancelMapper; import com.xiaopeng.order.mapper.XpOrderDetailMapper; import com.xiaopeng.order.service.IXpOrderCancelService; import com.xiaopeng.order.service.IXpOrderDetailService; import org.springframework.stereotype.Service; /** * <p> * 订单取消 服务实现类 * </p> * * @author mitao * @since 2026-02-03 */ @Service public class XpOrderDetailServiceImpl extends ServiceImpl<XpOrderDetailMapper, XpOrderDetail> implements IXpOrderDetailService { } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.ImmutableMap; import com.xiaopeng.api.dto.account.GetUserCouponDto; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.dto.order.GetOrderNumDto; import com.xiaopeng.api.dto.order.HistoryEndPointsDto; @@ -20,6 +21,7 @@ import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto; import com.xiaopeng.api.feignClient.account.XpAppUserClient; import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.operation.XpPricingClient; import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; @@ -32,14 +34,14 @@ import com.xiaopeng.api.model.operation.XpGeoQueryEntity; import com.xiaopeng.api.model.operation.XpFeedbackType; import com.xiaopeng.api.model.operation.XpPoint; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.order.XpOrderCancel; import com.xiaopeng.api.model.order.XpOrderEvaluate; import com.xiaopeng.api.model.order.XpOrderReassign; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.api.model.order.*; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.account.UserCouponVo; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudOrderOperation.OrderOperationType; import com.xiaopeng.api.vo.order.*; import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo; import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; @@ -61,6 +63,7 @@ import com.xiaopeng.order.job.jobs.OrderCancelJob; import com.xiaopeng.order.mangodb.model.OrderTrajectory; import com.xiaopeng.order.mangodb.service.OrderTrajectoryService; import com.xiaopeng.order.mapper.XpOrderDetailMapper; import com.xiaopeng.order.mapper.XpOrderMapper; import com.xiaopeng.order.model.dto.XpOrderCommentDto; import com.xiaopeng.order.model.dto.XpOrderPayDto; @@ -126,6 +129,9 @@ private final XpSystemSettingClient xpSystemSettingClient; private final OrderTrajectoryService orderTrajectoryService; private final XpPushMessageClient xpPushMessageClient; private final OrderMissionClient orderMissionClient; private final XpOrderDetailMapper xpOrderDetailMapper; private final OrderMissionClient orderMissionClient; @Autowired private MongoTemplate mongoTemplate; @@ -725,7 +731,6 @@ // TODO 加配置文件取 cancelTaxiPassengerVo.setAppId(""); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); if (baseVehicleUserDto.getCode() != 200) { throw new RuntimeException("取消订单失败"); @@ -748,6 +753,13 @@ Integer i = Integer.valueOf(cacheObject.toString()); redisService.setCacheCancelOrder("CANCEL_ORDER_" + uid, i + 1); } // 下发命令 PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(order.getId()); pushOrderDto.setVin(xpVehicle.getVehicleVin()); pushOrderDto.setOperation_type(OrderOperationType.VEHICLE_CANCEL_ORDER); orderMissionClient.cancelOrder(pushOrderDto); } @Override @@ -768,7 +780,10 @@ xpOrderCancelService.save(xpOrderCancel); // 推送消息 R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId()); XpVehicle xpVehicle = xpVehicleR.getData(); // 消息推送 Map<String, Object> params = new HashMap<>(); Map<String, Object> content = new HashMap<>(); @@ -783,6 +798,13 @@ sendMessageDTO.setAppUserId(String.valueOf(order.getAppUserId())); xpPushMessageClient.sendStatus(sendMessageDTO); log.info("订单取消消息推送完毕"); // 下发命令 PushOrderDto pushOrderDto = new PushOrderDto(); pushOrderDto.setOrderId(order.getId()); pushOrderDto.setVin(xpVehicle.getVehicleVin()); pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER); orderMissionClient.cancelOrder(pushOrderDto); } /** @@ -816,6 +838,7 @@ evaluate.setFeedbackContent(dto.getFeedbackContent()); evaluate.setFeedbackPictures(dto.getFeedbackPictures()); evaluate.setFeedbackTypeId(dto.getFeedbackTypeId()); evaluate.setCreateTime(LocalDateTime.now()); xpOrderEvaluateService.save(evaluate); } @@ -881,6 +904,7 @@ @Override public void endTrip(Long orderId, String uid) { List<XpOrderDetail> xpOrderDetails = new ArrayList<>(); // 起步价格明细 List<FareBreakdownDetailVo> start = new ArrayList<>(); // 里程费价格明细 @@ -921,6 +945,12 @@ userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); } // 根据订单所属开通城市id 查询定价 R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId()); XpPricing pricing = pricingByCityId.getData(); if (pricing == null) { throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); } // 费用 // 查询订单轨迹 List<OrderTrajectory> trajectories = mongoTemplate.find( @@ -930,23 +960,36 @@ ); List<TaxiPricingDemoService.GpsPoint> gpsPoints = new ArrayList<>(); for (OrderTrajectory trajectory : trajectories) { TaxiPricingDemoService.GpsPoint point = new TaxiPricingDemoService.GpsPoint(trajectory.getDistance(),trajectory.getLocatetime() ); TaxiPricingDemoService.GpsPoint point = new TaxiPricingDemoService.GpsPoint(trajectory.getDistance(), trajectory.getLocatetime()); gpsPoints.add(point); } TaxiPricingDemoService.PricingConfig config = new TaxiPricingDemoService.PricingConfig(); config.peak = new TaxiPricingDemoService.PricingRule("高峰期", "14:40", "14:46", 15.0, 3.0, 5.0, 3.0, 0.8); config.night = new TaxiPricingDemoService.PricingRule("夜间行驶", "14:47", "14:48", 18.0, 3.0, 5.0, 3.5, 1.0); config.holiday = new TaxiPricingDemoService.PricingRule("节假日", "18:47", "18:48", 18.0, 3.0, 5.0, 4, 2.0); config.normals.add(new TaxiPricingDemoService.PricingRule("普通时段", "14:46", "14:47", 10.0, 3.0, 5.0, 2.4, 0.5)); config.normals.add(new TaxiPricingDemoService.PricingRule("普通时段", "14:48", "17:59", 10.0, 3.0, 5.0, 2.4, 0.5)); JSONArray startingFareJsonArray = JSONArray.parseArray(pricing.getStartingFareJson()); JSONObject basicPricingJson = JSONObject.parseObject(pricing.getBasicPricingJson()); config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num21").split("-")[0], basicPricingJson.getString("num21").split("-")[1], Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25")), Double.parseDouble(basicPricingJson.getString("num26")), Double.parseDouble(basicPricingJson.getString("num27")), Double.parseDouble(basicPricingJson.getString("num28")))); config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num22").split("-")[0], basicPricingJson.getString("num22").split("-")[1], Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25")), Double.parseDouble(basicPricingJson.getString("num26")), Double.parseDouble(basicPricingJson.getString("num27")), Double.parseDouble(basicPricingJson.getString("num28")))); config.night = new TaxiPricingDemoService.PricingRule("夜间行驶", basicPricingJson.getString("num14").split("-")[0], basicPricingJson.getString("num14").split("-")[1], Double.parseDouble(basicPricingJson.getString("num16")), Double.parseDouble(basicPricingJson.getString("num17")), Double.parseDouble(basicPricingJson.getString("num18")), Double.parseDouble(basicPricingJson.getString("num19")), Double.parseDouble(basicPricingJson.getString("num20"))); config.holiday = new TaxiPricingDemoService.PricingRule("节假日", "00:00", "23:59", Double.parseDouble(basicPricingJson.getString("num30")), Double.parseDouble(basicPricingJson.getString("num31")), Double.parseDouble(basicPricingJson.getString("num32")), Double.parseDouble(basicPricingJson.getString("num33")), Double.parseDouble(basicPricingJson.getString("num234"))); for (Object o : startingFareJsonArray) { // 转化为jsonObject JSONObject jsonObject = (JSONObject) o; String num1 = jsonObject.getString("num1"); String num2 = jsonObject.getString("num2"); String num3 = jsonObject.getString("num3"); String num4 = jsonObject.getString("num4"); String num5 = jsonObject.getString("num5"); String num6 = jsonObject.getString("num6"); String num7 = jsonObject.getString("num7"); config.normals.add(new TaxiPricingDemoService.PricingRule("普通时段", num1, num2, Double.parseDouble(num3), Double.parseDouble(num4), Double.parseDouble(num5), Double.parseDouble(num6), Double.parseDouble(num7))); } // --- 远途加价区间配置 (关键修改) --- // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2) config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(5.0, 6.0, 1.2)); config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num6")), Double.parseDouble(basicPricingJson.getString("num7")), Double.parseDouble(basicPricingJson.getString("num8")))); // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0) config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(6.0, 7.0, 2.0)); config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num9")), Double.parseDouble(basicPricingJson.getString("num10")), Double.parseDouble(basicPricingJson.getString("num11")))); // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值 config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(7.0, 8.0, 3.5)); config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num12")), 999999, Double.parseDouble(basicPricingJson.getString("num13")))); TaxiPricingDemoService.TaxiPricingService service = new TaxiPricingDemoService.TaxiPricingService(); Map<String, BigDecimal> bill = service.calculateOrderPrice(DateUtils.toDate(order.getCreateTime()), order.getMileageKilometers(), gpsPoints, config); @@ -956,360 +999,118 @@ BigDecimal totalAmount = bill.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add); System.err.println("--------------------------"); System.err.println("应付总额: " + totalAmount + " 元"); // 封装费用明细数据 for (Map.Entry<String, BigDecimal> stringBigDecimalEntry : bill.entrySet()) { // 封装起步价 FareBreakdownDetailVo startDetail = new FareBreakdownDetailVo(); String key = stringBigDecimalEntry.getKey(); if (key.contains("起步价")) { startDetail.setTime(key.split("\\|")[1]); startDetail.setMoney(stringBigDecimalEntry.getValue()); if (key.contains("普通时段")) { startDetail.setType(1); } else if (key.contains("夜间")) { startDetail.setType(2); } else if (key.contains("高峰")) { startDetail.setType(3); } else if (key.contains("节假")) { startDetail.setType(4); } startDetail.setDuration(Long.parseLong(key.split("=")[1])); startDetail.setMileage(new BigDecimal(key.split("&")[1])); start.add(startDetail); } // 封装里程费 if (!key.contains("起步")&&!key.contains("时长")&&!key.contains("远途")&&!key.contains("distance")) { FareBreakdownDetailVo mileageDetail = new FareBreakdownDetailVo(); if (key.contains("普通时段")){ mileageDetail.setType(1); String key1 = "distance普通时段"+key.split("\\|")[1]; String key2 = "普通时段"+key.split("\\|")[1]; BigDecimal distance = bill.get(key1); BigDecimal money = bill.get(key2); mileageDetail.setMileage(distance); mileageDetail.setMoney(money); }else if (key.contains("夜间行驶")){ mileageDetail.setType(2); String key1 = "distance夜间行驶"+key.split("\\|")[1]; String key2 = "夜间行驶"+key.split("\\|")[1]; BigDecimal distance = bill.get(key1); BigDecimal money = bill.get(key2); mileageDetail.setMileage(distance); mileageDetail.setMoney(money); } else if (key.contains("高峰期")){ mileageDetail.setType(3); String key1 = "distance高峰期"+key.split("\\|")[1]; String key2 = "高峰期"+key.split("\\|")[1]; BigDecimal distance = bill.get(key1); BigDecimal money = bill.get(key2); mileageDetail.setMileage(distance); mileageDetail.setMoney(money); } else if (key.contains("节假")){ mileageDetail.setType(4); String key1 = "distance节假日"+key.split("\\|")[1]; String key2 = "节假日"+key.split("\\|")[1]; BigDecimal distance = bill.get(key1); BigDecimal money = bill.get(key2); mileageDetail.setMileage(distance); mileageDetail.setMoney(money); } mileage.add(mileageDetail); // // 计算实际价格 // // 根据订单所属开通城市id 查询定价 // R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId()); // XpPricing pricing = pricingByCityId.getData(); // if (pricing == null) { // throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); // } // // 查询订单轨迹 // List<OrderTrajectory> trajectories = mongoTemplate.find( // new Query(Criteria.where("orderId").is(orderId)) // .with(Sort.by(Sort.Direction.ASC, "locatetime")), // OrderTrajectory.class // ); // // 计算等待时间 // LocalDateTime boardingTime = order.getBoardingTime();// 上车时间 // LocalDateTime arrivalTime = order.getArrivalTime();// 车辆到达上车点时间 // // 行程开始时间 // Integer waitTime = Math.abs((int) Duration.between(boardingTime, arrivalTime).getSeconds()); // // // 行程总时间 秒 // LocalDateTime tripBegin = order.getTripBegin(); // LocalDateTime tripEnd = order.getTripEnd(); // long seconds = Duration.between(tripBegin, tripEnd).getSeconds(); // // 默认费率1 // double priceRate = 1; // if (pricing.getIsDynamicPrice() == 1) { // // 先判断是否已经调价过了 // Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid); // if (userDynamicPriceObj != null) { // // 是否调价过了 // priceRate = Double.parseDouble(userDynamicPriceObj.toString()); // } else { // // 没有调过 // //是否触发动态调价 redis获取 // Object dynamicPriceObj = redisService.getCacheObject("DYNAMIC_PRICE_" + order.getCityId()); // if (dynamicPriceObj != null) { // JSONObject jsonObject = JSONObject.parseObject(pricing.getDynamicPricingJson()); // // 触发动态调价 // // value=时间 // String timeStr = dynamicPriceObj.toString(); // // 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率 // Integer num = this.getOrderNum(new GetOrderNumDto(timeStr, order.getCityId())); // // X单上升一次调价 // Integer orderNum = getOrderNum(jsonObject, 4); // // 需要调价的次数 // int count = num / orderNum; // if (count > 0) { // // 计算每次上升的费率 // Integer upRate = getOrderNum(jsonObject, 5); // BigDecimal divide = new BigDecimal(upRate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); // for (int i = count; i > 0; i--) { // // 当前费率+(费率*上升百分比) // priceRate = new BigDecimal(priceRate).multiply(divide).add(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue(); // } // // 冷却时间 // Integer time = getOrderNum(jsonObject, 6); // // 触发调价了 // redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES); // } // // } // } // } // if (trajectories.isEmpty()) { // return; // } // // 起步价规则 // String startingFareJson = pricing.getStartingFareJson(); // JSONArray startingFareJsonArray = JSONArray.parseArray(startingFareJson); // // 基础信息规则 // String basicPricing = pricing.getBasicPricingJson(); // JSONObject basicPricingJson = JSONObject.parseObject(basicPricing); // // // 计算过后得到起步价 // String num3 = ""; // String num4 = ""; // String num5 = ""; // String num6 = ""; // String num7 = ""; // String startTime = ""; // String endTime = ""; // // 等待费设置 // String num4Wait = basicPricingJson.getString("num4"); // String num5Wait = basicPricingJson.getString("num5"); // // 远途费设置 // String numLong6 = basicPricingJson.getString("num6"); // String numLong7 = basicPricingJson.getString("num7"); // String numLong8 = basicPricingJson.getString("num8"); // String numLong9 = basicPricingJson.getString("num9"); // String numLong10 = basicPricingJson.getString("num10"); // String numLong11 = basicPricingJson.getString("num11"); // String numLong12 = basicPricingJson.getString("num12"); // String numLong13 = basicPricingJson.getString("num13"); // String numLong14 = basicPricingJson.getString("num14"); // String numLong15 = basicPricingJson.getString("num15"); // // 节假日基础价格规则 // String num29 = basicPricingJson.getString("num29"); // String num30 = basicPricingJson.getString("num30"); // String num31 = basicPricingJson.getString("num31"); // String num32 = basicPricingJson.getString("num32"); // String num33 = basicPricingJson.getString("num33"); // String num34 = basicPricingJson.getString("num34"); // // // 高峰期基础价格规则 // String num21 = basicPricingJson.getString("num21"); // String num22 = basicPricingJson.getString("num22"); // String num23 = basicPricingJson.getString("num23"); // String num24 = basicPricingJson.getString("num24"); // String num25 = basicPricingJson.getString("num25"); // String num26 = basicPricingJson.getString("num26"); // String num27 = basicPricingJson.getString("num27"); // String num28 = basicPricingJson.getString("num28"); // // 夜间行驶基础价格规则 // String num14 = basicPricingJson.getString("num14"); // String num15 = basicPricingJson.getString("num15"); // String num16 = basicPricingJson.getString("num16"); // String num17 = basicPricingJson.getString("num17"); // String num18 = basicPricingJson.getString("num18"); // String num19 = basicPricingJson.getString("num19"); // String num20 = basicPricingJson.getString("num20"); // // 起步里程 // Double startDistance = 0.00D; // // 临时超出起步里程的额外里程 // Double mileageDistance = 0.00D; // // 起步价所属时段 type 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段 // Integer startType = 1; // // 里程费所属时段 type 0默认临时 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段 // Integer mileageType = 0; // // 里程费所属时段临时字段 用于区分时段是否变化 type 0默认临时 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段 // Integer mileageTypeTemp = 0; // // // 起步价时段开始时间 // Date startDate = DateUtils.toDate(order.getTripBegin()); // // 根据订单创建时间确定使用哪个起步价规则 // Date createTime = DateUtils.toDate(order.getCreateTime()); // // 起步价时段结束时间 // Date endDate = new Date(); // // 计算订单总行驶距离,单位米 // Double totalDistance = 0.00D; // // 起步价计算完成表示 // boolean startFlag = true; // SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm"); // if (pricing.getPricingType() == 1) { // // // 计算起步价 // for (Object o : startingFareJsonArray) { // JSONObject startingFare = (JSONObject) o; // Calendar calendarStart = Calendar.getInstance(); // calendarStart.setTime(createTime); // calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0])); // calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1])); // // Calendar calendarEnd = Calendar.getInstance(); // calendarEnd.setTime(createTime); // calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0])); // calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1])); // // 找到符合条件的直接跳出循环 // if (createTime.getTime() > calendarStart.getTimeInMillis() && createTime.getTime() < calendarEnd.getTimeInMillis()) { // num3 = startingFare.getString("num3"); // num4 = startingFare.getString("num4"); // num5 = startingFare.getString("num5"); // num6 = startingFare.getString("num6"); // num7 = startingFare.getString("num7"); // startTime = startingFare.getString("num1"); // endTime = startingFare.getString("num2"); // break; // } // } // // 判断是否为夜间行驶 起步价修改 // Calendar calendarStart = Calendar.getInstance(); // calendarStart.setTime(createTime); // calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[0].split(":")[0])); // calendarStart.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[0].split(":")[1])); // Calendar calendarEnd = Calendar.getInstance(); // calendarEnd.setTime(createTime); // calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[1].split(":")[0])); // calendarEnd.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[1].split(":")[1])); // if (createTime.getTime() > calendarStart.getTimeInMillis() && createTime.getTime() < calendarEnd.getTimeInMillis() // ) { // num3 = num15; // num6 = num16; // num7 = num17; // startType = 2; // // } // // 判断是否为高峰期 // Calendar calendar1Start = Calendar.getInstance(); // calendar1Start.setTime(createTime); // calendar1Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[0].split(":")[0])); // calendar1Start.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[0].split(":")[1])); // // Calendar calendar1End = Calendar.getInstance(); // calendar1End.setTime(createTime); // calendar1End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[1].split(":")[0])); // calendar1End.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[1].split(":")[1])); // // Calendar calendar2Start = Calendar.getInstance(); // calendar2Start.setTime(createTime); // calendar2Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[0])); // calendar2Start.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[1])); // // Calendar calendar2End = Calendar.getInstance(); // calendar2End.setTime(createTime); // calendar2End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[1].split(":")[0])); // calendar2End.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[1].split(":")[1])); // // 判断是否为高峰期 起步价,里程费,时长费修改 // if ((createTime.getTime() > calendar1Start.getTimeInMillis() && createTime.getTime() < calendar1End.getTimeInMillis()) // || (createTime.getTime() > calendar2Start.getTimeInMillis() && createTime.getTime() < calendar2End.getTimeInMillis())) { // num3 = num23; // num6 = num24; // num7 = num25; // startType = 3; // } // // 判断当前是否属于节假日 // boolean holiday = holidayUtil.isHoliday(DateUtils.toLocalDate(createTime)); // if (holiday) { // // 判断是否为节假日 起步价修改 // num3 = num29; // num6 = num30; // num7 = num31; // startType = 4; // } // // 查询起步价结束时间段 // for (OrderTrajectory trajectory : trajectories) { // Long locatetime = trajectory.getLocatetime(); // endDate = new Date(locatetime); // LocalDateTime end = DateUtils.dateToLocalDateTime(endDate); // if (end.isAfter(tripBegin)) { // // 行程开始 开始计算里程数 // if (startDistance >= Double.parseDouble(num4)) { // // 行驶里程大于基础配置里程 那么起步价时段计算完成 // break; // } // startDistance += trajectory.getDistance(); // } // } // // 起步价价格明细封装 // FareBreakdownDetailVo startDetail = new FareBreakdownDetailVo(); // startDetail.setType(startType); // startDetail.setTime(timeFormat.format(startDate) + "-" + timeFormat.format(endDate)); // startDetail.setMileage(new BigDecimal(startDistance / 1000).setScale(2, RoundingMode.HALF_UP)); // // // 计算startDate和endDate相差多少分钟 // // 将 Date 转换为 LocalDateTime // LocalDateTime startDateTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // LocalDateTime endDateTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // // // 计算时间差(单位:分钟) // long minutesDifference = Duration.between(startDateTime, endDateTime).toMinutes(); // startDetail.setDuration(minutesDifference); // startDetail.setMoney(new BigDecimal(num3)); // start.add(startDetail); // // // // 计算起步价格、里程、时段、行驶时间 //// calculateStartingFare(trajectories, tripBegin, startFlag, startDistance, startingFareJsonArray, num3, num4, num5, endDate, order, num14, num15, num16, num17, startType, num21, num22, num23, num24, num25, num29, num30, num31, timeFormat, startDate, start); // // for (OrderTrajectory trajectory : trajectories) { //// FareBreakdownDetailVo mileageDetail = new FareBreakdownDetailVo(); //// mileageDetail.setType(); //// mileageDetail.setTime(); //// mileageDetail.setMileage(); //// mileageDetail.setDuration(); //// mileageDetail.setMoney(); // // Long locatetime = trajectory.getLocatetime(); // Date mileageDate = new Date(locatetime); // LocalDateTime begin = DateUtils.dateToLocalDateTime(mileageDate); // if (begin.isAfter(tripBegin)) { // totalDistance += trajectory.getDistance(); // if (totalDistance >= startDistance) { // // 开始累加起步里程 // mileageDistance += trajectory.getDistance(); // // 判断当前里程费属于什么时段 //// mileageTypeTemp // MileageDto param = new MileageDto(); // param.setMileage(num6); // param.setTime(num7); // param.setMileageTypeTemp(mileageTypeTemp); // MileageDto mileageDto = mileageType(param,startingFareJsonArray, mileageDate, begin, num14, num16, num17, num21, num22, num24, num25, num30, num31); // if (mileageType==0){ // // 首次 // mileageType = mileageDto.getMileageTypeTemp(); // mileageTypeTemp= mileageDto.getMileageTypeTemp(); // } else if (mileageType!=mileageTypeTemp){ // // 新时段 // FareBreakdownDetailVo fareBreakdownDetailVo = new FareBreakdownDetailVo(); // fareBreakdownDetailVo.setType(mileageType); // fareBreakdownDetailVo.setTime(timeFormat.format(endDate)+"-"+timeFormat.format(mileageDate)); // double md = mileageDistance / 1000; // fareBreakdownDetailVo.setMileage(new BigDecimal(md).setScale(2, RoundingMode.HALF_UP)); // // 计算startDate和endDate相差多少分钟 // // 将 Date 转换为 LocalDateTime // LocalDateTime startMileageTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // LocalDateTime endMileageTime = mileageDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // // // 计算时间差(单位:分钟) // long minutesDifferenceMileage = Duration.between(startMileageTime, endMileageTime).toMinutes(); // startDetail.setDuration(minutesDifference); // fareBreakdownDetailVo.setDuration(minutesDifferenceMileage); // // 如果md小数位是0 那么舍弃小数,如果小数位不为0 那么进1 // fareBreakdownDetailVo.setMileage(new BigDecimal(md).setScale(2, RoundingMode.HALF_UP)); // Integer temp = (int)md; // if (md> temp){ // temp=temp+1; // } // // 超出的里程 // fareBreakdownDetailVo.setMoney(new BigDecimal(temp*6)); // mileage.add(fareBreakdownDetailVo); // // 修改时段标识 // mileageType = mileageTypeTemp; // // 修改新时段开始时间 // endDate = mileageDate; // }else{ // // 时段未变化 // } // } // } // } // // 开始计算里程费用 // for (OrderTrajectory trajectory : trajectories) { // Long locatetime = trajectory.getLocatetime(); // Date mileageDate = new Date(locatetime); // LocalDateTime begin = DateUtils.dateToLocalDateTime(mileageDate); // // 行驶里程大于起步里程配置 开始计算里程和里程费 // if (begin.isAfter(tripBegin)) { // totalDistance += trajectory.getDistance(); // double v = totalDistance / 1000.0; // if (v >= Double.parseDouble(num5)) { // // 增加超出起步里程的里程数 // mileageDistance += trajectory.getDistance(); // // } // if (mileageType == 0) { // // 第一次计算里程费 // // } // } // } // } else { // // } // //// XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice( //// order.getAppUserId().toString(),JSON.parseObject(pricing.getBasicPricingJson()), //// JSON.parseArray(pricing.getStartingFareJson()), //// pricing.getDynamicPricingJson(), //// pricing.getIsDynamicPrice(), //// totalDistance, //// seconds, //// order.getCityId() //// ); } // 封装时长费 FareBreakdownDetailVo timeDetail = new FareBreakdownDetailVo(); if (key.contains("时长费")) { timeDetail.setType(startDetail.getType()); timeDetail.setMoney(stringBigDecimalEntry.getValue()); timeDetail.setDuration(Long.parseLong(key.split("&")[1])); duration.add(timeDetail); } // 封装远途费 FareBreakdownDetailVo longDetail = new FareBreakdownDetailVo(); if (key.contains("远途费")) { longDetail.setType(startDetail.getType()); longDetail.setMoney(stringBigDecimalEntry.getValue()); longDetail.setMileage(new BigDecimal(key.split("&")[1])); longDistance.add(longDetail); } } for (FareBreakdownDetailVo fareBreakdownDetailVo : start) { XpOrderDetail xpOrderDetail = new XpOrderDetail(); BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail); xpOrderDetail.setOrderId(orderId); xpOrderDetail.setDetailType(1); xpOrderDetail.setCreateTime(LocalDateTime.now()); xpOrderDetails.add(xpOrderDetail); } BigDecimal mileageMoney = BigDecimal.ZERO; for (FareBreakdownDetailVo fareBreakdownDetailVo : mileage) { XpOrderDetail xpOrderDetail = new XpOrderDetail(); BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail); xpOrderDetail.setOrderId(orderId); xpOrderDetail.setDetailType(2); xpOrderDetail.setCreateTime(LocalDateTime.now()); mileageMoney = mileageMoney.add(fareBreakdownDetailVo.getMoney()); xpOrderDetails.add(xpOrderDetail); } order.setMileageMoney(mileageMoney); for (XpOrderDetail xpOrderDetail : xpOrderDetails) { xpOrderDetailMapper.insert(xpOrderDetail); } for (FareBreakdownDetailVo fareBreakdownDetailVo : duration) { order.setDurationType(fareBreakdownDetailVo.getType()); order.setDurationMoney(fareBreakdownDetailVo.getMoney()); order.setDuration(Double.parseDouble(fareBreakdownDetailVo.getDuration()+"")); } for (FareBreakdownDetailVo fareBreakdownDetailVo : longDistance) { order.setLongDistanceType(fareBreakdownDetailVo.getType()); order.setLongDistance(fareBreakdownDetailVo.getMileage().doubleValue()); order.setLongDistancemoney(fareBreakdownDetailVo.getMoney()); } baseMapper.updateById(order); } @@ -1651,8 +1452,11 @@ orderDetailVo.setFeedbackPictures(evaluate.getFeedbackPictures()); orderDetailVo.setFeedbackContent(evaluate.getFeedbackContent()); orderDetailVo.setOrderRating(evaluate.getOrderRating()); XpFeedbackType feedbackType = xpSystemSettingClient.getFeedbackTypeById(evaluate.getFeedbackTypeId()).getData(); orderDetailVo.setFeedbackTypeName(feedbackType.getFeedbackType()); if(evaluate.getFeedbackTypeId()!=null){ XpFeedbackType feedbackType = xpSystemSettingClient.getFeedbackTypeById(evaluate.getFeedbackTypeId()).getData(); orderDetailVo.setFeedbackTypeName(feedbackType.getFeedbackType()); } } List<OrderTrajectory> trajectories = mongoTemplate.find( new Query(Criteria.where("orderId").is(orderId)) @@ -1684,35 +1488,35 @@ @Transactional // @ShardingTransactionType(TransactionType.BASE) public void popOrder(PopOrderDto dto) { XpOrder order = this.getById(dto.getOrderId()); order.setAcceptanceTime(LocalDateTime.now()); AddTaxiPassengerVo addTaxiPassengerVo = new AddTaxiPassengerVo(); R<XpVehicle> byVehicleVin = xpVehicleClient.findByVehicleVin(dto.getXpVehicleVin()); if (byVehicleVin.getCode()==200){ if (byVehicleVin.getCode() == 200) { order.setVehicleId(byVehicleVin.getData().getId()); addTaxiPassengerVo.setVin(byVehicleVin.getData().getVehicleVin()); } order.setStatus(2); // Redis弹出这个队列元素 // value 订单Id_经纬度_状态_用户id_订单编号_距离_时间 String popFirstElement = redisService.popFirstElement(order.getQueueName()); XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); String[] split = popFirstElement.split("_"); xpGeoQueryEntity.setOrderId(split[0]); String[] split1 = split[1].split(","); xpGeoQueryEntity.setLocation(new GeoJsonPoint(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]))); xpGeoQueryEntity.setTimestamp(new Date()); // 计算2个时间的秒数 LocalDateTime createTime = order.getCreateTime(); LocalDateTime acceptanceTime = order.getAcceptanceTime(); xpGeoQueryEntity.setSeconds((int) Duration.between(createTime, acceptanceTime).getSeconds()); String currentTimeSlot = DateUtils.getCurrentTimeSlot(); xpGeoQueryEntity.setTime(currentTimeSlot); // 存mongo数据 xpPricingClient.saveXpGeoQueryEntity(xpGeoQueryEntity); this.updateById(order); addTaxiPassengerVo.setUid(order.getAppUserId().toString()); addTaxiPassengerVo.setOrderNo(order.getOrderNumber()); // TODO appid addTaxiPassengerVo.setAppId(""); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo); if(baseVehicleUserDto.getCode()==200){ // Redis弹出这个队列元素 // value 订单Id_经纬度_状态_用户id_订单编号_距离_时间 String popFirstElement = redisService.popFirstElement(order.getQueueName()); XpGeoQueryEntity xpGeoQueryEntity = new XpGeoQueryEntity(); String[] split = popFirstElement.split("_"); xpGeoQueryEntity.setOrderId(split[0]); // 存mongo数据 xpPricingClient.updateXpGeoQueryEntity(split[0]); this.updateById(order); } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/test/GpsPoint.java
New file @@ -0,0 +1,13 @@ package com.xiaopeng.order.test; import lombok.Data; import java.util.Date; @Data public class GpsPoint { private Double distance; // 与上一点距离(米) private Long locatetime; // 毫秒时间戳 private Date createTime; // 创建日期 // ... getter & setter } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/test/TaxiPricingDemoService.java
New file @@ -0,0 +1,300 @@ package com.xiaopeng.order.test; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalTime; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; /** * 出租车计费引擎服务 */ public class TaxiPricingDemoService { public static void main(String[] args) { PricingConfig config = setupMockConfig(); Calendar cal = Calendar.getInstance(); cal.set(2026, Calendar.MARCH, 13, 14, 40, 15); Date orderTime = cal.getTime(); // 模拟总里程 25.0 km,以触发多级远途加价 double totalMillige = 7.874; // 模拟 GPS 轨迹点 List<GpsPoint> points = createMockGpsPoints(orderTime); TaxiPricingService service = new TaxiPricingService(); Map<String, BigDecimal> bill = service.calculateOrderPrice(orderTime, totalMillige, points, config); System.err.println("======= 订单计费账单 ======="); bill.forEach((detail, price) -> System.err.println(detail + ": " + price + " 元")); BigDecimal totalAmount = bill.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add); System.err.println("--------------------------"); System.err.println("应付总额: " + totalAmount + " 元"); } public static class TaxiPricingService { public Map<String, BigDecimal> calculateOrderPrice(Date orderTime, double totalMileageKm, List<GpsPoint> points, PricingConfig config) { Map<String, BigDecimal> result = new LinkedHashMap<>(); BigDecimal totalMileage = BigDecimal.valueOf(totalMileageKm); PricingRule baseRule = config.findMatchedRule(orderTime); result.put("起步价(" + baseRule.ruleName + ")"+"-"+baseRule.start+"-"+baseRule.end, baseRule.baseFare); if (totalMileage.compareTo(baseRule.baseKm) <= 0) { return formatMap(result); } BigDecimal remainBaseM = baseRule.baseKm.multiply(new BigDecimal("1000")); BigDecimal remainBaseMs = baseRule.baseMin.multiply(new BigDecimal("60000")); BigDecimal currentTotalM = BigDecimal.ZERO; BigDecimal currentTotalMs = BigDecimal.ZERO; boolean isOverBaseM = false; boolean isOverBaseMs = false; // 超出里程和分钟数的集合 Map<String, BigDecimal> bill = new LinkedHashMap<>(); Map<String, BigDecimal> activeRuleNameRate = new LinkedHashMap<>(); Set<String> activeRuleNameList = new HashSet<>(); for (int i = 1; i < points.size(); i++) { GpsPoint curr = points.get(i); GpsPoint prev = points.get(i - 1); BigDecimal segM = BigDecimal.valueOf(curr.getDistance()); BigDecimal segMs = BigDecimal.valueOf(curr.getLocatetime() - prev.getLocatetime()); currentTotalM = currentTotalM.add(segM); // 累计当前已行驶的总里程 currentTotalMs = currentTotalMs.add(segMs); // 累计当前已行驶的总时间 PricingRule activeRule = config.findMatchedRule(new Date(curr.getLocatetime())); // 扣除起步额度逻辑 BigDecimal billM = BigDecimal.ZERO; if (currentTotalM.compareTo(remainBaseM) > 0) { // billM = currentTotalM.subtract(remainBaseM); isOverBaseM = true; } BigDecimal billMs = BigDecimal.ZERO; if (currentTotalMs.compareTo(remainBaseMs) > 0) { // billMs = currentTotalMs.subtract(remainBaseMs); isOverBaseMs = true; } // 1. 基础费率计算 // BigDecimal distFee = billM.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP).multiply(activeRule.perKmRate); // BigDecimal timeFee = billMs.multiply(activeRule.perMinRate); // 2. 远途加价计算 (基于区间判定) // BigDecimal longExtra = calculateLongDistExtra(currentTotalM, billM, config.longSteps); // BigDecimal totalSeg = distFee.add(timeFee).add(longExtra); // 超出里程 if (isOverBaseM) { bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ml", BigDecimal.ZERO).add(segM)); } // 超出时间 if (isOverBaseMs) { bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ms", BigDecimal.ZERO).add(segMs)); } // 若不存在规则名称,则添加 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", k -> activeRule.perKmRate); // 超出里程的加价 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms",k ->activeRule.perMinRate); // 超出时间的加价 activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml"); activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms"); // result.put(activeRule.ruleName, result.getOrDefault(activeRule.ruleName, BigDecimal.ZERO).add(totalSeg)); } // 1. 基础费率计算 for (String ruleName : activeRuleNameList) { String[] split = ruleName.split("_"); if(ruleName.endsWith("_Ml")){ BigDecimal exceedValue = bill.get(ruleName); if(Objects.nonNull(exceedValue)){ exceedValue = exceedValue.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP); }else{ exceedValue = BigDecimal.ZERO; } BigDecimal exceedRate = activeRuleNameRate.get(ruleName); result.put(split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate))); }else{ BigDecimal exceedValue = bill.get(ruleName); if(Objects.nonNull(exceedValue)){ exceedValue = exceedValue.divide(new BigDecimal(60000), 2, RoundingMode.HALF_UP); }else{ exceedValue = BigDecimal.ZERO; } BigDecimal exceedRate = activeRuleNameRate.get(ruleName); result.put(split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate))); } } // 2. 远途加价计算 (基于区间判定) BigDecimal longExtra = calculateLongDistExtra(BigDecimal.valueOf(totalMileageKm), config.longSteps); result.put("远途费", longExtra); return formatMap(result); } /** * 远途加价核心逻辑:判断当前行驶到的里程处于哪个加价区间 */ private BigDecimal calculateLongDistExtra(BigDecimal billM, List<LongRangeStep> steps) { if (billM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO; // 当前点所在的公里数 BigDecimal extraStart = BigDecimal.ZERO; BigDecimal extraRate = BigDecimal.ZERO; // 匹配区间规则 for (LongRangeStep step : steps) { if (step.isMatch(billM)) { extraRate = step.addPrice; extraStart = step.numStart; break; } } return billM.subtract(extraStart).multiply(extraRate); } private Map<String, BigDecimal> formatMap(Map<String, BigDecimal> map) { return map.entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, e -> e.getValue().setScale(2, RoundingMode.HALF_UP), (a, b) -> b, LinkedHashMap::new)); } } // --- 远途区间模型 --- public static class LongRangeStep { BigDecimal numStart; // 超过公里数 BigDecimal numEnd; // 不足公里数 BigDecimal addPrice; // 加收单价 public LongRangeStep(double start, double end, double price) { this.numStart = BigDecimal.valueOf(start); this.numEnd = BigDecimal.valueOf(end); this.addPrice = BigDecimal.valueOf(price); } public boolean isMatch(BigDecimal currentKm) { // [numStart, numEnd) return currentKm.compareTo(numStart) >= 0 && currentKm.compareTo(numEnd) < 0; } } public static class PricingConfig { PricingRule holiday; PricingRule peak; PricingRule night; List<PricingRule> normals = new ArrayList<>(); List<LongRangeStep> longSteps = new ArrayList<>(); // 修改为区间列表 public PricingRule findMatchedRule(Date date) { LocalTime t = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime(); if (holiday.isMatch(t)) return holiday; if (peak.isMatch(t)) return peak; if (night.isMatch(t)) return night; for (PricingRule r : normals) if (r.isMatch(t)) return r; return normals.get(0); } } public static class PricingRule { String ruleName; LocalTime start, end; BigDecimal baseFare, baseKm, baseMin, perKmRate, perMinRate; public PricingRule(String n, String s, String e, double bf, double bk, double bm, double pkr, double pmr) { this.ruleName = n; this.start = LocalTime.parse(s); this.end = LocalTime.parse(e); this.baseFare = BigDecimal.valueOf(bf); this.baseKm = BigDecimal.valueOf(bk); this.baseMin = BigDecimal.valueOf(bm); this.perKmRate = BigDecimal.valueOf(pkr); this.perMinRate = BigDecimal.valueOf(pmr); } public boolean isMatch(LocalTime now) { return start.isBefore(end) ? (!now.isBefore(start) && !now.isAfter(end)) : (!now.isBefore(start) || !now.isAfter(end)); } } public static class GpsPoint { private double distance; private long locatetime; public GpsPoint(double d, long t) { this.distance = d; this.locatetime = t; } public double getDistance() { return distance; } public long getLocatetime() { return locatetime; } } private static PricingConfig setupMockConfig() { PricingConfig config = new PricingConfig(); config.peak = new PricingRule("高峰期", "14:40", "14:46", 15.0, 3.0, 5.0, 3.0, 0.8); config.night = new PricingRule("夜间行驶", "14:47", "14:48", 18.0, 3.0, 5.0, 3.5, 1.0); config.holiday = new PricingRule("节假日", "18:47", "18:48", 18.0, 3.0, 5.0, 4, 2.0); config.normals.add(new PricingRule("普通时段", "14:46", "14:47", 10.0, 3.0, 5.0, 2.4, 0.5)); config.normals.add(new PricingRule("普通时段", "14:48", "17:59", 10.0, 3.0, 5.0, 2.4, 0.5)); // --- 远途加价区间配置 (关键修改) --- // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2) config.longSteps.add(new LongRangeStep(5.0, 6.0, 1.2)); // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0) config.longSteps.add(new LongRangeStep(6.0, 7.0, 2.0)); // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值 config.longSteps.add(new LongRangeStep(7.0, 8.0, 3.5)); return config; } private static List<GpsPoint> createMockGpsPoints(Date startTime) { List<GpsPoint> list = new ArrayList<>(); long startTs = startTime.getTime(); list.add(new GpsPoint(0, startTs)); // 模拟行驶了 20km list.add(new GpsPoint(120, startTs + 2 * 10 * 1000)); list.add(new GpsPoint(204, startTs + 2 * 20 * 1000)); list.add(new GpsPoint(231, startTs + 2 * 30 * 1000)); list.add(new GpsPoint(252, startTs + 2 * 40 * 1000)); list.add(new GpsPoint(188, startTs + 2 * 50 * 1000)); list.add(new GpsPoint(150, startTs + 2 * 60 * 1000)); list.add(new GpsPoint(126, startTs + 2 * 70 * 1000)); list.add(new GpsPoint(138, startTs + 2 * 80 * 1000)); list.add(new GpsPoint(119, startTs + 2 * 90 * 1000)); list.add(new GpsPoint(147, startTs + 2 * 100 * 1000)); list.add(new GpsPoint(155, startTs + 2 * 110 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 120 * 1000)); list.add(new GpsPoint(172, startTs + 2 * 130 * 1000)); list.add(new GpsPoint(202, startTs + 2 * 140 * 1000)); list.add(new GpsPoint(162, startTs + 2 * 150 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 160 * 1000)); list.add(new GpsPoint(132, startTs + 2 * 170 * 1000)); list.add(new GpsPoint(142, startTs + 2 * 180 * 1000)); list.add(new GpsPoint(262, startTs + 2 * 190 * 1000)); list.add(new GpsPoint(242, startTs + 2 * 200 * 1000)); list.add(new GpsPoint(282, startTs + 2 * 210 * 1000)); list.add(new GpsPoint(222, startTs + 2 * 220 * 1000)); list.add(new GpsPoint(212, startTs + 2 * 230 * 1000)); list.add(new GpsPoint(142, startTs + 2 * 240 * 1000)); list.add(new GpsPoint(262, startTs + 2 * 250 * 1000)); list.add(new GpsPoint(169, startTs + 2 * 260 * 1000)); list.add(new GpsPoint(170, startTs + 2 * 270 * 1000)); list.add(new GpsPoint(180, startTs + 2 * 280 * 1000)); list.add(new GpsPoint(165, startTs + 2 * 290 * 1000)); list.add(new GpsPoint(180, startTs + 2 * 300 * 1000)); list.add(new GpsPoint(198, startTs + 2 * 310 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 320 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 330 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 340 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 350 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 360 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 370 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 380 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 390 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 400 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 410 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 420 * 1000)); list.add(new GpsPoint(182, startTs + 2 * 430 * 1000)); return list; } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java
@@ -41,7 +41,7 @@ BigDecimal totalMileage = BigDecimal.valueOf(totalMileageKm); PricingRule baseRule = config.findMatchedRule(orderTime); result.put("起步价(" + baseRule.ruleName + ")"+"-"+baseRule.start+"-"+baseRule.end, baseRule.baseFare); result.put("起步价(" + baseRule.ruleName + ")"+"|"+baseRule.start+"-"+baseRule.end+"="+baseRule.baseMin+"&"+baseRule.baseKm, baseRule.baseFare); if (totalMileage.compareTo(baseRule.baseKm) <= 0) { return formatMap(result); @@ -92,19 +92,19 @@ // BigDecimal totalSeg = distFee.add(timeFee).add(longExtra); // 超出里程 if (isOverBaseM) { bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ml", BigDecimal.ZERO).add(segM)); bill.put(activeRule.ruleName + "|"+activeRule.start + "-" + activeRule.end + "_Ml", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ml", BigDecimal.ZERO).add(segM)); } // 超出时间 if (isOverBaseMs) { bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ms", BigDecimal.ZERO).add(segMs)); bill.put(activeRule.ruleName + "|"+activeRule.start + "-" + activeRule.end + "_Ms", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ms", BigDecimal.ZERO).add(segMs)); } // 若不存在规则名称,则添加 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", k -> activeRule.perKmRate); // 超出里程的加价 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms",k ->activeRule.perMinRate); // 超出时间的加价 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "|"+activeRule.start + "-" + activeRule.end + "_Ml", k -> activeRule.perKmRate); // 超出里程的加价 activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "|"+activeRule.start + "-" + activeRule.end + "_Ms",k ->activeRule.perMinRate); // 超出时间的加价 activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml"); activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms"); activeRuleNameList.add(activeRule.ruleName + "|"+activeRule.start + "-" + activeRule.end + "_Ml"); activeRuleNameList.add(activeRule.ruleName + "|"+activeRule.start + "-" + activeRule.end + "_Ms"); // result.put(activeRule.ruleName, result.getOrDefault(activeRule.ruleName, BigDecimal.ZERO).add(totalSeg)); } // 1. 基础费率计算 @@ -113,6 +113,7 @@ if(ruleName.endsWith("_Ml")){ BigDecimal exceedValue = bill.get(ruleName); if(Objects.nonNull(exceedValue)){ result.put("distance"+split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue)); exceedValue = exceedValue.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP); }else{ exceedValue = BigDecimal.ZERO; @@ -127,12 +128,14 @@ exceedValue = BigDecimal.ZERO; } BigDecimal exceedRate = activeRuleNameRate.get(ruleName); result.put(split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate))); result.put("时长费"+split[0]+"&"+exceedValue, result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate))); } } // 2. 远途加价计算 (基于区间判定) BigDecimal longExtra = calculateLongDistExtra(BigDecimal.valueOf(totalMileageKm), config.longSteps); result.put("远途费", longExtra); // 计算远途里程 BigDecimal longExtraValue = calculateLongDistExtra(BigDecimal.valueOf(totalMileageKm), config.longSteps); result.put("远途费&"+longExtraValue, longExtra); return formatMap(result); } @@ -155,6 +158,26 @@ } } return billM.subtract(extraStart).multiply(extraRate); } /** * 计算远途费的里程 */ private BigDecimal calculateLongDistExtraValue(BigDecimal billM, List<LongRangeStep> steps) { if (billM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO; // 当前点所在的公里数 BigDecimal extraStart = BigDecimal.ZERO; BigDecimal extraRate = BigDecimal.ZERO; // 匹配区间规则 for (LongRangeStep step : steps) { if (step.isMatch(billM)) { extraRate = step.addPrice; extraStart = step.numStart; break; } } return billM.subtract(extraStart); } private Map<String, BigDecimal> formatMap(Map<String, BigDecimal> map) { @@ -183,7 +206,7 @@ public static class PricingConfig { public PricingRule holiday; public PricingRule peak; public List<PricingRule> peaks = new ArrayList<>(); public PricingRule night; public List<PricingRule> normals = new ArrayList<>(); public List<LongRangeStep> longSteps = new ArrayList<>(); // 修改为区间列表 @@ -191,7 +214,7 @@ public PricingRule findMatchedRule(Date date) { LocalTime t = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime(); if (holiday.isMatch(t)) return holiday; if (peak.isMatch(t)) return peak; for (PricingRule r : peaks) if (r.isMatch(t)) return r; if (night.isMatch(t)) return night; for (PricingRule r : normals) if (r.isMatch(t)) return r; return normals.get(0); @@ -229,7 +252,8 @@ private static PricingConfig setupMockConfig() { PricingConfig config = new PricingConfig(); config.peak = new PricingRule("高峰期", "14:40", "14:46", 15.0, 3.0, 5.0, 3.0, 0.8); config.peaks.add(new PricingRule("高峰期", "14:40", "14:46", 15.0, 3.0, 5.0, 3.0, 0.8)); config.peaks.add(new PricingRule("高峰期", "19:40", "20:46", 15.0, 3.0, 5.0, 3.0, 0.8)); config.night = new PricingRule("夜间行驶", "14:47", "14:48", 18.0, 3.0, 5.0, 3.5, 1.0); config.holiday = new PricingRule("节假日", "18:47", "18:48", 18.0, 3.0, 5.0, 4, 2.0); config.normals.add(new PricingRule("普通时段", "14:46", "14:47", 10.0, 3.0, 5.0, 2.4, 0.5)); xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java
@@ -149,4 +149,24 @@ return R.ok(); } @ApiOperation(value = "车队删除车辆成员", tags = {"管理后台-车队管理"}) @PostMapping("/api/v1/fleet/vehicle/remove") @ApiImplicitParams({ @ApiImplicitParam(name = "fleetId", value = "车队ID", dataTypeClass = Long.class, required = true), @ApiImplicitParam(name = "vehicleId", value = "车辆ID", dataTypeClass = Long.class, required = true)}) public R<?> removeVehicle(Long fleetId, Long vehicleId) { xpFleetService.removeVehicle(fleetId, vehicleId); return R.ok(); } @ApiOperation(value = "车队删除人员成员", tags = {"管理后台-车队管理"}) @PostMapping("/api/v1/fleet/operation-user/remove") @ApiImplicitParams({ @ApiImplicitParam(name = "fleetId", value = "车队ID", dataTypeClass = Long.class, required = true), @ApiImplicitParam(name = "operationUserId", value = "人员ID", dataTypeClass = Long.class, required = true)}) public R<?> removeOperationUser(Long fleetId, Long operationUserId) { xpFleetService.removeOperationUser(fleetId, operationUserId); return R.ok(); } } xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/IXpFleetService.java
@@ -100,4 +100,18 @@ */ void addOperationUserId(Long fleetId, Long operationUserId); /** * 车队删除车辆成员 * @param fleetId 车队ID * @param vehicleId 车辆ID */ void removeVehicle(Long fleetId, Long vehicleId); /** * 车队删除人员成员 * @param fleetId 车队ID * @param operationUserId 人员ID */ void removeOperationUser(Long fleetId, Long operationUserId); } xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java
@@ -516,4 +516,22 @@ operations.setOperationsUserId(operationUserId); xpFleetOperationsService.save(operations); } @Override public void removeVehicle(Long fleetId, Long vehicleId) { // 直接删除关联关系(幂等性:不存在也不报错) xpFleetVehicleService.lambdaUpdate() .eq(XpFleetVehicle::getFleetId, fleetId) .eq(XpFleetVehicle::getVehicleId, vehicleId) .remove(); } @Override public void removeOperationUser(Long fleetId, Long operationUserId) { // 直接删除关联关系(幂等性:不存在也不报错) xpFleetOperationsService.lambdaUpdate() .eq(XpFleetOperations::getFleetId, fleetId) .eq(XpFleetOperations::getOperationsUserId, operationUserId) .remove(); } }