README.md
@@ -1,4 +1,150 @@ ## Robotaxi # XPeng Robotaxi 小鹏汽车Robotaxi 基于 Spring Cloud Alibaba 的 Robotaxi 微服务系统,面向自动驾驶出行场景,包含账户、订单、地图、自动驾驶、远程驾驶等业务域,以及一组对应的 Spring Boot 应用(`xp-robotaxi-*-boot`)用于实际部署。 --- ## 一、技术栈概览 - Java 8 - Spring Boot 2.7.7 - Spring Cloud 2021.0.5 - Spring Cloud Alibaba 2021.0.4.0 - Nacos(注册中心 / 配置中心) - Sentinel(流量控制与熔断降级) - Seata(分布式事务) - MyBatis-Plus(ORM) - Druid(数据库连接池) - Swagger / Springfox(接口文档) - Redis、MySQL、MongoDB 等 --- ## 二、顶层模块结构 根工程 `robotaxi` 下包含以下子模块: - `xp-robotaxi-api`:对外和内部共享的 API、DTO、proto 风格 VO 定义 - `xp-robotaxi-common`:公共能力(日志、权限、安全、数据源、Redis 等) - 一组 `xp-robotaxi-*-boot`:每个业务域对应的 Spring Boot 应用,可独立部署 下面重点说明各个 `boot` 模块的功能职责。 --- ## 三、Boot 应用模块职责 ### 1. xp-robotaxi-gateway-boot(网关应用) - 基于 Spring Cloud Gateway 的统一入口网关 - 负责路由转发、鉴权校验、限流熔断、灰度发布 - 统一接入层日志与基本安全控制 ### 2. xp-robotaxi-account-boot(账户中心) - 账户/用户域的对外应用 - 用户注册登录、账号信息管理、角色与权限管理 - 为管理后台和其他业务服务提供账户相关 REST 接口 ### 3. xp-robotaxi-automatic-pilot-boot(自动驾驶业务) - Robotaxi 自动驾驶业务对外应用 - 负责云端与 XPU 的消息交互(MQTT / Kafka),包括下行指令和上行状态 - 订单与自动驾驶系统联动(创建任务、控制任务、状态应答等) - 封装自动驾驶相关业务接口,供运营平台和其他服务调用 ### 4. xp-robotaxi-itinerary-boot(行程管理) - 行程生命周期管理应用 - 行程创建、修改、取消 - 行程状态跟踪、历史行程查询 - 与订单、地图等服务协同进行路径规划与调度 ### 5. xp-robotaxi-maintenance-boot(运维与车辆维护) - 车辆运维与保障相关应用 - 车辆维护计划、保养记录、故障报修流程 - 保单管理和维修记录追踪 - 为运营平台提供车辆健康状态和维护数据 ### 6. xp-robotaxi-map-boot(地图与位置服务) - 地图与位置相关应用 - 路线规划、距离与时间预估 - POI 查询、地理围栏管理 - 与自动驾驶、行程、订单等服务协同提供位置能力 ### 7. xp-robotaxi-operation-boot(运营管理) - 平台运营域应用 - 车辆调度策略、运营数据统计与报表 - 价格策略配置、优惠活动管理 - 支持运营后台对 Robotaxi 业务进行集中管理 ### 8. xp-robotaxi-order-boot(订单服务) - 订单全生命周期管理应用 - 下单、改派、取消、完成等订单流程 - 订单状态机与状态变更通知 - 与支付、行程、自动驾驶等服务联动 ### 9. xp-robotaxi-payment-boot(支付与结算) - 支付结算域应用 - 集成微信等第三方支付渠道 - 支付请求、退款、对账、结算与发票管理 - 对订单服务提供支付能力,对财务提供对账数据 ### 10. xp-robotaxi-push-message-boot(消息推送) - 消息通知与推送应用 - 支持短信、APP 推送、邮件、站内信等多种渠道 - 提供统一消息模板与发送接口 - 与订单、行程、运营等服务集成,实现业务事件触达用户 ### 11. xp-robotaxi-remote-driving-boot(远程驾驶) - 云代驾 / 远程驾驶应用 - 远程控制指令下发、实时状态回传 - 支持紧急接管、远程辅助等场景 - 与自动驾驶、行程、车辆状态服务协同 ### 12. xp-robotaxi-vehicle-convoy-boot(车队管理) - 车队及编队管理应用 - 车队组建与成员管理 - 编队行驶控制与协同调度 - 提供面向车队运营场景的接口能力 ### 13. xp-robotaxi-job-boot(定时任务) - 统一定时任务调度应用 - 基于 Quartz,对多业务域的定时任务进行集中管理 - 任务配置、执行监控、失败重试等 ### 14. xp-robotaxi-file-boot(文件服务) - 文件上传/下载/存储应用 - 支持对象存储或分布式文件系统接入 - 提供统一的文件访问域名与权限控制能力 --- ## 四、基础能力模块概览 虽然具体代码在 `xp-robotaxi-common` 与 `xp-robotaxi-api` 中,这里简要说明它们的角色,方便理解整体结构: - `xp-robotaxi-common`:日志、安全、数据源、Redis、数据权限、分布式事务等通用能力 - `xp-robotaxi-api`:对各个 boot 应用暴露的统一 API/DTO/VO 定义,是服务间通信的契约层 --- ## 五、运行与部署提示(概览) - 所有 `xp-robotaxi-*-boot` 模块均为独立 Spring Boot 应用,可按需选择部署 - 网关(`xp-robotaxi-gateway-boot`)通常需要多实例部署以保证高可用 - 订单、支付、自动驾驶、运营等核心域建议根据业务量做水平扩展 - Nacos / Sentinel / Seata 等基础组件需先行部署,并在各 boot 应用中配置好连接信息 --- *文档更新时间: 2026-02-06* xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/order/XpOrderClientFallbackFactory.java
@@ -6,6 +6,7 @@ import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.vo.order.OrderTrajectoryVo; import com.xiaopeng.common.core.domain.R; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; @@ -94,6 +95,13 @@ log.error("更新订单状态失败,原因: {}", cause.getMessage()); return R.fail("更新订单状态失败"); } @Override public R<String> saveVehicleLocation(OrderTrajectoryVo orderTrajectory) { log.error("保存订单轨迹数据失败,原因: {}", cause.getMessage()); return R.fail("保存订单轨迹数据失败"); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/order/XpOrderClient.java
@@ -6,6 +6,7 @@ import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO; import com.xiaopeng.api.factory.order.XpOrderClientFallbackFactory; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.vo.order.OrderTrajectoryVo; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; import org.springframework.cloud.openfeign.FeignClient; @@ -111,6 +112,12 @@ @RequestParam Integer status); /** * 保存订单轨迹数据 * @param orderTrajectory * @return */ @PostMapping("/inner/v1/order/saveVehicleLocation") R<String> saveVehicleLocation(@RequestBody OrderTrajectoryVo orderTrajectory); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/OrderTrajectoryVo.java
New file @@ -0,0 +1,44 @@ package com.xiaopeng.api.vo.order; import com.xiaopeng.common.core.web.domain.MongoBaseModel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import org.springframework.data.mongodb.core.mapping.Document; @Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class OrderTrajectoryVo extends MongoBaseModel { /** * 订单id */ private Long orderId; /** * 经纬度 坐标 (104.45645456,34.41564654) */ private String location; /** * 方向角(0-359°) */ private Integer direction; /** * 速度 */ private Double speed; /** * 时间戳 毫秒 */ private Long locatetime; /** * 距离 米 */ private Double distance; } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -23,6 +23,7 @@ import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskType; import com.xiaopeng.api.vo.automaticPilot.protobuf.*; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.api.vo.order.OrderTrajectoryVo; import com.xiaopeng.automaticPilot.util.XPUUtil; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.enums.OrderStatusEnum; @@ -41,7 +42,7 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.HashMap; import java.util.Date;import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; @@ -418,6 +419,16 @@ dto.setDistance(10); dto.setDirection(60); xpPushMessageClient.pushOrderRoute(dto); //存储历史轨迹 OrderTrajectoryVo orderTrajectory = new OrderTrajectoryVo(); orderTrajectory.setOrderId(orderId); orderTrajectory.setLocation(longitude + "," + latitude); orderTrajectory.setDirection(dto.getDirection()); orderTrajectory.setSpeed(0D); orderTrajectory.setLocatetime(System.currentTimeMillis()); orderTrajectory.setCreateTime(new Date()); xpOrderClient.saveVehicleLocation(orderTrajectory); } //5秒钟循环一次 xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -15,10 +15,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.order.FareBreakdownVo; import com.xiaopeng.api.vo.order.XpHistoryEndPointsVo; import com.xiaopeng.api.vo.order.XpHistoryStartPointsVo; import com.xiaopeng.api.vo.order.XpPointInfoVo; import com.xiaopeng.api.vo.order.*; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.utils.WebUtils; @@ -43,6 +40,7 @@ import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.data.mongodb.core.MongoTemplate; @@ -474,7 +472,9 @@ */ @ApiOperation(value = "订单车辆定位实时存储") @PostMapping("/inner/v1/order/saveVehicleLocation") public R<String> saveVehicleLocation(@RequestBody OrderTrajectory orderTrajectory) { public R<String> saveVehicleLocation(@RequestBody OrderTrajectoryVo vo) { OrderTrajectory orderTrajectory = new OrderTrajectory(); BeanUtils.copyProperties(vo, orderTrajectory); xpOrderService.saveVehicleLocation(orderTrajectory); return R.ok(); } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -1880,10 +1880,11 @@ if (StringUtils.isEmpty(oldLocation)) { // 首次定位 xpOrder.setMileageKilometers(0D); xpOrder.setAppUserId(null); this.updateById(xpOrder); orderTrajectory.setDistance(0D); // 存储mongo数据 orderTrajectoryService.create(orderTrajectory); mongoTemplate.save(orderTrajectory); // 存储首次定位 redisService.setCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId(), orderTrajectory.getLocation()); } else { @@ -1892,11 +1893,12 @@ Double distance1 = distance.get("WGS84"); if (distance1 > 50 && xpOrder.getStatus() == 6) {//大于50米表示在移动,且在服务中的时候记录里程 xpOrder.setMileageKilometers(BigDecimal.valueOf(xpOrder.getMileageKilometers()).add(new BigDecimal(distance1)).setScale(2, RoundingMode.HALF_EVEN).doubleValue()); xpOrder.setAppUserId(null); this.updateById(xpOrder); // 存储本次距离 orderTrajectory.setDistance(distance1); // 存储mongo数据 orderTrajectoryService.create(orderTrajectory); mongoTemplate.save(orderTrajectory); } } else { log.error("调用高德计算距离出错"); @@ -1930,15 +1932,11 @@ } } // List<OrderTrajectory> trajectories = mongoTemplate.find( // new Query(Criteria.where("orderId").is(orderId)) // .with(Sort.by(Sort.Direction.ASC, "locatetime")), // OrderTrajectory.class // ); List<TaxiPricingDemoService.GpsPoint> mockGpsPoints = TaxiPricingDemoService.createMockGpsPoints(DateUtils.toDate(order.getCreateTime())); // 生成轨迹记录 List<OrderTrajectory> trajectories = TrajectoryGenerator.generateTrajectory(mockGpsPoints); List<OrderTrajectory> trajectories = mongoTemplate.find( new Query(Criteria.where("orderId").is(orderId)) .with(Sort.by(Sort.Direction.ASC, "locatetime")), OrderTrajectory.class ); orderDetailVo.setTrajectories(trajectories); if (order.getTripBegin() != null && order.getTripEnd() != null) { // 计算两个时间相差多少分钟