liujie
2026-03-02 47aa3631a929533782652bb72fa689f90f19623d
Merge remote-tracking branch 'origin/master'
17个文件已修改
729 ■■■■■ 已修改文件
README.md 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/config/MyLocaleResolver.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpOperationsUserServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserProfileVO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/config/MyLocaleResolver.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/config/MyLocaleResolver.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/config/MyLocaleResolver.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/config/MyLocaleResolver.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java 315 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/config/MyLocaleResolver.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderDetailVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderPageVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-payment-boot/src/main/java/com/xiaopeng/payment/config/MyLocaleResolver.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/config/MyLocaleResolver.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-remote-driving-boot/src/main/java/com/xiaopeng/remoteDriving/config/MyLocaleResolver.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/config/MyLocaleResolver.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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-account-boot/src/main/java/com/xiaopeng/account/config/MyLocaleResolver.java
@@ -23,6 +23,7 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空 
        //If the request is not empty.
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpOperationsUserServiceImpl.java
@@ -182,6 +182,7 @@
        //保存审核记录
        XpQualificationAudit xpQualificationAudit = BeanUtil.copyProperties(dto, XpQualificationAudit.class, "id");
        xpQualificationAudit.setAuditStatus(1);
        xpQualificationAudit.setOperationsUserId(dto.getId());
        xpQualificationAuditService.save(xpQualificationAudit);
    }
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/account/XpOperationsUserProfileVO.java
@@ -62,6 +62,21 @@
    @ApiModelProperty(value = "关联车队信息")
    private FleetInfoVO fleet;
    @ApiModelProperty(value = "身份证正面")
    private String idCardFront;
    @ApiModelProperty(value = "身份证背面")
    private String idCardBack;
    @ApiModelProperty(value = "驾驶证")
    private String driverLicense;
    @ApiModelProperty(value = "无犯罪记录正面")
    private String certificateOfNoCriminalRecord;
    @ApiModelProperty(value = "培训结业证")
    private String trainingGraduationCertificate;
    /**
     * 角色列表
     */
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/config/MyLocaleResolver.java
@@ -23,6 +23,7 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空 
        //If the request is not empty.
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -153,9 +153,22 @@
     * @return
     */
    @GetMapping("/base/v1/order/acceptingOrder")
    public R acceptingOrder(Long orderId, Long vehicleId, Double startLon, Double startLat){
    public R acceptingOrder(Long orderId, String code, Long vehicleId, Double startLon, Double startLat){
        if(null == vehicleId){
            vehicleId = 1L;
        }
        if(null == startLat || null == startLon){
            startLat = 23.157155D;
            startLon = 113.382373D;
        }
        XpVehicle xpVehicle = xpVehicleClient.queryById(vehicleId).getData();
        XpOrder xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        XpOrder xpOrder = null;
        if(null != orderId){
            xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        }
        if(StringUtils.isNotEmpty(code)){
            xpOrder = xpOrderClient.queryOrderByOrderNo(code).getData();
        }
        OrderRouteAndFareDto dto = new OrderRouteAndFareDto();
        dto.setOrderId(orderId);
        LonLatDto origin = new LonLatDto();
@@ -199,17 +212,6 @@
            log.error("推送消息失败:{}", stringR.getMsg());
        }
        xpOrderClient.updateOrderStatus(orderId, OrderStatusEnum.PICK_UP.getCode());
//        PushOrderRouteDTO dto3 = new PushOrderRouteDTO();
//        dto3.setOrderId(orderId);
//        dto3.setRouteType(1);
//        dto3.setLongitude(startLon);
//        dto3.setLatitude(startLat);
//        dto3.setDuration(10);
//        dto3.setDistance(2);
//        dto3.setDirection(60);
//        xpPushMessageClient.pushOrderRoute(dto3);
        return r;
    }
@@ -219,9 +221,18 @@
     * @param orderId
     */
    @GetMapping("/base/v1/order/pushStartDriverLocation")
    private void pushStartDriverLocation(Long vehicleId, Long orderId){
    private void pushStartDriverLocation(Long vehicleId, Long orderId, String code){
        if(null == vehicleId){
            vehicleId = 1L;
        }
        XpVehicle xpVehicle = xpVehicleClient.queryById(vehicleId).getData();
        XpOrder xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        XpOrder xpOrder = null;
        if(null != orderId){
            xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        }
        if(StringUtils.isNotEmpty(code)){
            xpOrder = xpOrderClient.queryOrderByOrderNo(code).getData();
        }
        String vin = xpVehicle.getVehicleVin();
        Long appUserId = xpOrder.getAppUserId();
        Object cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_START_DATA:" + xpOrder.getAppUserId());
@@ -309,8 +320,14 @@
     * @return
     */
    @GetMapping("/base/v1/order/updateOrderStatus")
    public R updateOrderStatus(Long orderId, Integer status) {
        XpOrder xpOrder = xpOrderClient.queryOrderById(orderId).getData();
    public R updateOrderStatus(Long orderId, String code, Integer status) {
        XpOrder xpOrder = null;
        if(null != orderId){
            xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        }
        if(StringUtils.isNotEmpty(code)){
            xpOrder = xpOrderClient.queryOrderByOrderNo(code).getData();
        }
        //构建推送消息
        Map<String, Object> params = new HashMap<>();
        Map<String, Object> content = new HashMap<>();
@@ -368,9 +385,18 @@
     * @param orderId
     */
    @GetMapping("/base/v1/order/pushEndDriverLocation")
    private void pushEndDriverLocation(Long vehicleId, Long orderId){
    private void pushEndDriverLocation(Long vehicleId, Long orderId, String code){
        if(null == vehicleId){
            vehicleId = 1L;
        }
        XpVehicle xpVehicle = xpVehicleClient.queryById(vehicleId).getData();
        XpOrder xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        XpOrder xpOrder = null;
        if(null != orderId){
            xpOrder = xpOrderClient.queryOrderById(orderId).getData();
        }
        if(StringUtils.isNotEmpty(code)){
            xpOrder = xpOrderClient.queryOrderByOrderNo(code).getData();
        }
        String vin = xpVehicle.getVehicleVin();
        Long appUserId = xpOrder.getAppUserId();
        Object cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + xpOrder.getAppUserId());
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/config/MyLocaleResolver.java
@@ -23,11 +23,9 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        if(language==null){
            language="1";
        }
        //如果请求不为空
        //如果请求不为空
        //If the request is not empty.
        if (language.equals("1")) {
            locale = new Locale("zh", "CN");
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/config/MyLocaleResolver.java
@@ -23,12 +23,10 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空 
        //If the request is not empty.
        if(null == language){
            language = "1";
        }
        if (language.equals("1")) {
            locale = new Locale("zh", "CN");
        } else if (language.equals("2")) {
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/config/MyLocaleResolver.java
@@ -23,6 +23,7 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空 
        //If the request is not empty.
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
@@ -110,174 +110,169 @@
    }
    @Override
    public R addElectronic(ElectronicFenceAddDto dto) {
        try {
            // 获取现有的运营区域(areaType=1)
            LambdaQueryWrapper<XpElectronicFenceArea> xpElectronicFenceAreaLambdaQueryWrapper = new LambdaQueryWrapper<>();
            xpElectronicFenceAreaLambdaQueryWrapper.eq(XpElectronicFenceArea::getAreaType, 1);
            List<XpElectronicFenceArea> existingOperationAreas = xpElectronicFenceAreaMapper.selectList(xpElectronicFenceAreaLambdaQueryWrapper);
        // 获取现有的运营区域(areaType=1)
        LambdaQueryWrapper<XpElectronicFenceArea> xpElectronicFenceAreaLambdaQueryWrapper = new LambdaQueryWrapper<>();
        xpElectronicFenceAreaLambdaQueryWrapper.eq(XpElectronicFenceArea::getAreaType, 1);
        List<XpElectronicFenceArea> existingOperationAreas = xpElectronicFenceAreaMapper.selectList(xpElectronicFenceAreaLambdaQueryWrapper);
            // 获取待添加的新区域
            List<XpElectronicFenceArea> newElectronicFenceAreas = dto.getElectronicFenceAreas();
        // 获取待添加的新区域
        List<XpElectronicFenceArea> newElectronicFenceAreas = dto.getElectronicFenceAreas();
            // 检查新区域与现有运营区域是否有重叠
            for (XpElectronicFenceArea newArea : newElectronicFenceAreas) {
                if (newArea.getAreaType() == 1) { // 只检查运营区域
                    for (XpElectronicFenceArea existingArea : existingOperationAreas) {
                        if (polygonOverlapService.checkPolygonOverlap(
                                newArea.getFencePoints(),
                                existingArea.getFencePoints())) {
                            throw new ServiceException(
                                    "运营区域{}与现有运营区域{}发生重叠",
                                    I18nKeyConstant.EDIT_ELECTRONIC_ERROR,
                                    new Object[]{existingArea.getAreaName(), existingArea.getAreaName()}
                            );
                        }
                    }
                }
            }
            // 保存主表数据
            XpElectronicFence xpElectronicFence = new XpElectronicFence();
            BeanUtils.copyProperties(dto, xpElectronicFence);
            xpElectronicFence.setStatus(0);
            if (dto.getStatus() == 0) {
                xpElectronicFence.setStatus(0);
            } else if (dto.getStatus() == 1) {
                if (dto.getEffectType() == 1) {
                    // 设置为启用中
                    xpElectronicFence.setStatus(2);
                    xpElectronicFence.setEffectTime(LocalDateTime.now());
                } else {
                    if (dto.getEffectTime().isBefore(LocalDateTime.now())) {
                        // 设置为启用中
                        xpElectronicFence.setStatus(2);
                    } else {
                        xpElectronicFence.setStatus(1);
                    }
                }
            }
            xpElectronicFence.setCityCode(dto.getCityCode());
            // 生成围栏id 规则:城市首字母-类型简写-4位自增流水号(如 SZ-OP-001),支持复制;数据停用或删除后,流水号不回收
            XpRegion xpRegion = xpRegionMapper.selectOne(new LambdaQueryWrapper<XpRegion>()
                    .eq(XpRegion::getCode, dto.getCityCode()));
            String name = xpRegion.getName();
            this.save(xpElectronicFence);
            // 根据城市中文名,截取前两位的首字母并且大写
            // 截取前两个字符
            String prefix = name.substring(0, 2);
            // 获取拼音首字母并大写
            String result = getPinyinInitials(prefix).toUpperCase();
            xpElectronicFence.setElectronicNumber(result + "-OP-" + String.format("%04d", xpElectronicFence.getId()));
            if (dto.getStatus() == 0) {
                xpElectronicFence.setStatus(0);
            } else if (dto.getStatus() == 1) {
                if (dto.getEffectType() == 1) {
                    // 设置为启用中
                    xpElectronicFence.setStatus(2);
                    xpElectronicFence.setEffectTime(LocalDateTime.now());
                } else {
                    if (dto.getEffectTime().isBefore(LocalDateTime.now())) {
                        // 设置为启用中
                        xpElectronicFence.setStatus(2);
                    } else {
                        // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效
                        Map<String, ? extends Object> maps =
                                new ImmutableMap.Builder<String, Long>().
                                        put("type", 1L)
                                        .put("id", xpElectronicFence.getId())
                                        .build();
                        QuartzManager.addJob(
                                ElectronicEffectJob.class,
                                (ElectronicEffectJob.effect + xpElectronicFence.getId()).toUpperCase(),
                                TimeJobType.ELECTRONIC_EFFECT,
                                DateUtils.localDateTimeToDate(dto.getEffectTime()),
                                maps
        // 检查新区域与现有运营区域是否有重叠
        for (XpElectronicFenceArea newArea : newElectronicFenceAreas) {
            if (newArea.getAreaType() == 1) { // 只检查运营区域
                for (XpElectronicFenceArea existingArea : existingOperationAreas) {
                    if (polygonOverlapService.checkPolygonOverlap(
                            newArea.getFencePoints(),
                            existingArea.getFencePoints())) {
                        throw new ServiceException(
                                "运营区域{}与现有运营区域{}发生重叠",
                                I18nKeyConstant.EDIT_ELECTRONIC_ERROR,
                                new Object[]{existingArea.getAreaName(), existingArea.getAreaName()}
                        );
                        xpElectronicFence.setStatus(1);
                    }
                }
                if (dto.getFailureType() != null && dto.getFailureType() == 2) {
                    if (dto.getFailureTime().isBefore(LocalDateTime.now())) {
                        // 设置待启用
                        xpElectronicFence.setStatus(1);
                    } else {
                        // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效
                        Map<String, ? extends Object> maps =
                                new ImmutableMap.Builder<String, Long>().
                                        put("type", 2L)
                                        .put("id", xpElectronicFence.getId())
                                        .build();
                        QuartzManager.addJob(
                                ElectronicEffectJob.class,
                                (ElectronicEffectJob.failure + xpElectronicFence.getId()).toUpperCase(),
                                TimeJobType.ELECTRONIC_FAILURE,
                                DateUtils.localDateTimeToDate(dto.getEffectTime()),
                                maps
                        );
                        xpElectronicFence.setStatus(1);
                    }
                }
            }
            this.updateById(xpElectronicFence);
            if (xpElectronicFence.getStatus() == 2) {
                // 设置电子围栏ID并保存区域数据
                for (XpElectronicFenceArea area : newElectronicFenceAreas) {
                    area.setElectronicId(xpElectronicFence.getId());
                    area.setAreaStatus(1);
                    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 (xpElectronicFence.getStatus() == 2) {
                for (XpElectronicFenceArea newElectronicFenceArea : newElectronicFenceAreas) {
                    if (newElectronicFenceArea.getAreaType() == 1
                            && newElectronicFenceArea.getAreaStatus() == 1) {
                        XpGeoFencingPolygon xpGeoFencingPolygon = new XpGeoFencingPolygon();
                        xpGeoFencingPolygon.setFenceId(String.valueOf(xpElectronicFence.getId()));
                        xpGeoFencingPolygon.setName(xpElectronicFence.getElectronicName());
                        xpGeoFencingPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId()));
                        xpGeoFencingPolygon.setCityCode(xpElectronicFence.getCityCode());
                        xpGeoFencingPolygon.setOperationStrategy(xpElectronicFence.getOperationStrategy());
                        // 将围栏点转换为GeoJSON多边形
                        GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints());
                        xpGeoFencingPolygon.setPoints(geoJsonPolygon);
                        mongoTemplate.insert(xpGeoFencingPolygon);
                    }
                    if (newElectronicFenceArea.getAreaType() == 2
                            && newElectronicFenceArea.getAreaStatus() == 1) {
                        XpGeoFencingProhibitedPolygon xpGeoFencingProhibitedPolygon = new XpGeoFencingProhibitedPolygon();
                        xpGeoFencingProhibitedPolygon.setFenceId(String.valueOf(xpElectronicFence.getId()));
                        xpGeoFencingProhibitedPolygon.setName(xpElectronicFence.getElectronicName());
                        xpGeoFencingProhibitedPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId()));
                        // 将围栏点转换为GeoJSON多边形
                        GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints());
                        xpGeoFencingProhibitedPolygon.setPoints(geoJsonPolygon);
                        mongoTemplate.save(xpGeoFencingProhibitedPolygon);
                    }
                }
            }
            return R.ok();
        } catch (Exception e) {
            log.error("新增电子围栏失败", e);
            throw new ServiceException("新增电子围栏失败", I18nKeyConstant.ADD_ELECTRONIC_ERROR);
        }
        // 保存主表数据
        XpElectronicFence xpElectronicFence = new XpElectronicFence();
        BeanUtils.copyProperties(dto, xpElectronicFence);
        xpElectronicFence.setStatus(0);
        if (dto.getStatus() == 0) {
            xpElectronicFence.setStatus(0);
        } else if (dto.getStatus() == 1) {
            if (dto.getEffectType() == 1) {
                // 设置为启用中
                xpElectronicFence.setStatus(2);
                xpElectronicFence.setEffectTime(LocalDateTime.now());
            } else {
                if (dto.getEffectTime().isBefore(LocalDateTime.now())) {
                    // 设置为启用中
                    xpElectronicFence.setStatus(2);
                } else {
                    xpElectronicFence.setStatus(1);
                }
            }
        }
        xpElectronicFence.setCityCode(dto.getCityCode());
        // 生成围栏id 规则:城市首字母-类型简写-4位自增流水号(如 SZ-OP-001),支持复制;数据停用或删除后,流水号不回收
        XpRegion xpRegion = xpRegionMapper.selectOne(new LambdaQueryWrapper<XpRegion>()
                .eq(XpRegion::getCode, dto.getCityCode()));
        String name = xpRegion.getName();
        this.save(xpElectronicFence);
        // 根据城市中文名,截取前两位的首字母并且大写
        // 截取前两个字符
        String prefix = name.substring(0, 2);
        // 获取拼音首字母并大写
        String result = getPinyinInitials(prefix).toUpperCase();
        xpElectronicFence.setElectronicNumber(result + "-OP-" + String.format("%04d", xpElectronicFence.getId()));
        if (dto.getStatus() == 0) {
            xpElectronicFence.setStatus(0);
        } else if (dto.getStatus() == 1) {
            if (dto.getEffectType() == 1) {
                // 设置为启用中
                xpElectronicFence.setStatus(2);
                xpElectronicFence.setEffectTime(LocalDateTime.now());
            } else {
                if (dto.getEffectTime().isBefore(LocalDateTime.now())) {
                    // 设置为启用中
                    xpElectronicFence.setStatus(2);
                } else {
                    // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效
                    Map<String, ? extends Object> maps =
                            new ImmutableMap.Builder<String, Long>().
                                    put("type", 1L)
                                    .put("id", xpElectronicFence.getId())
                                    .build();
                    QuartzManager.addJob(
                            ElectronicEffectJob.class,
                            (ElectronicEffectJob.effect + xpElectronicFence.getId()).toUpperCase(),
                            TimeJobType.ELECTRONIC_EFFECT,
                            DateUtils.localDateTimeToDate(dto.getEffectTime()),
                            maps
                    );
                    xpElectronicFence.setStatus(1);
                }
            }
            if (dto.getFailureType() != null && dto.getFailureType() == 2) {
                if (dto.getFailureTime().isBefore(LocalDateTime.now())) {
                    // 设置待启用
                    xpElectronicFence.setStatus(1);
                } else {
                    // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效
                    Map<String, ? extends Object> maps =
                            new ImmutableMap.Builder<String, Long>().
                                    put("type", 2L)
                                    .put("id", xpElectronicFence.getId())
                                    .build();
                    QuartzManager.addJob(
                            ElectronicEffectJob.class,
                            (ElectronicEffectJob.failure + xpElectronicFence.getId()).toUpperCase(),
                            TimeJobType.ELECTRONIC_FAILURE,
                            DateUtils.localDateTimeToDate(dto.getEffectTime()),
                            maps
                    );
                    xpElectronicFence.setStatus(1);
                }
            }
        }
        this.updateById(xpElectronicFence);
        if (xpElectronicFence.getStatus() == 2) {
            // 设置电子围栏ID并保存区域数据
            for (XpElectronicFenceArea area : newElectronicFenceAreas) {
                area.setElectronicId(xpElectronicFence.getId());
                area.setAreaStatus(1);
                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 (xpElectronicFence.getStatus() == 2) {
            for (XpElectronicFenceArea newElectronicFenceArea : newElectronicFenceAreas) {
                if (newElectronicFenceArea.getAreaType() == 1
                        && newElectronicFenceArea.getAreaStatus() == 1) {
                    XpGeoFencingPolygon xpGeoFencingPolygon = new XpGeoFencingPolygon();
                    xpGeoFencingPolygon.setFenceId(String.valueOf(xpElectronicFence.getId()));
                    xpGeoFencingPolygon.setName(xpElectronicFence.getElectronicName());
                    xpGeoFencingPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId()));
                    xpGeoFencingPolygon.setCityCode(xpElectronicFence.getCityCode());
                    xpGeoFencingPolygon.setOperationStrategy(xpElectronicFence.getOperationStrategy());
                    // 将围栏点转换为GeoJSON多边形
                    GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints());
                    xpGeoFencingPolygon.setPoints(geoJsonPolygon);
                    mongoTemplate.insert(xpGeoFencingPolygon);
                }
                if (newElectronicFenceArea.getAreaType() == 2
                        && newElectronicFenceArea.getAreaStatus() == 1) {
                    XpGeoFencingProhibitedPolygon xpGeoFencingProhibitedPolygon = new XpGeoFencingProhibitedPolygon();
                    xpGeoFencingProhibitedPolygon.setFenceId(String.valueOf(xpElectronicFence.getId()));
                    xpGeoFencingProhibitedPolygon.setName(xpElectronicFence.getElectronicName());
                    xpGeoFencingProhibitedPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId()));
                    // 将围栏点转换为GeoJSON多边形
                    GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints());
                    xpGeoFencingProhibitedPolygon.setPoints(geoJsonPolygon);
                    mongoTemplate.save(xpGeoFencingProhibitedPolygon);
                }
            }
        }
        return R.ok();
    }
    @Override
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/config/MyLocaleResolver.java
@@ -23,8 +23,6 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
        }
        if (!StringUtils.hasLength(language)){
            language = "1";
        }
        //如果请求不为空 
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderDetailVO.java
@@ -38,7 +38,7 @@
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime rideTime;
    @ApiModelProperty(value = "状态 0=已取消,1=待接单,2=待出发,3=待到达预约点,4=待乘客上车,5=服务中,6=待支付,7=待评价,8=已完成")
    @ApiModelProperty(value = "状态 0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中,7=待客下车,8=待付款,9=待评价,10=已完成")
    private Integer status;
    @ApiModelProperty(value = "下单城市名称")
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderPageVO.java
@@ -57,7 +57,6 @@
    @ApiModelProperty(value = "订单金额")
    private BigDecimal orderMoney;
    @ApiModelProperty(value = "状态 0=已取消,1=待接单,2=待出发,3=待到达预约点,4=待乘客上车,5=服务中,6=待支付,7=待评价,8=已完成")
    @ApiModelProperty(value = "状态 0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中,7=待客下车,8=待付款,9=待评价,10=已完成")
    private Integer status;
}
xp-robotaxi-payment-boot/src/main/java/com/xiaopeng/payment/config/MyLocaleResolver.java
@@ -23,6 +23,7 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空 
        //If the request is not empty.
xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/config/MyLocaleResolver.java
@@ -1,80 +1,81 @@
//package com.xiaopeng.pushMessage.config;
//
//import com.alibaba.fastjson.JSON;
//import com.alibaba.fastjson.JSONObject;
//import org.springframework.util.StringUtils;
//import org.springframework.web.servlet.LocaleResolver;
//
//import javax.servlet.ServletInputStream;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import java.io.BufferedReader;
//import java.io.InputStreamReader;
//import java.util.Locale;
//
//public class MyLocaleResolver implements LocaleResolver {
//
//    @Override
//    public Locale resolveLocale(HttpServletRequest request) {
//        //1=中文,2=英文,3=印尼文
//        //1=Chinese, 2=English, 3=Indonesian.
//        String language = getQueryParams(request, "language");
//
//        Locale locale = null;
//        if (StringUtils.isEmpty(language)) {
//            locale = Locale.getDefault();
//        }
//        //如果请求不为空
//        //If the request is not empty.
//        if (language.equals("1")) {
//            locale = new Locale("zh", "CN");
//        } else if (language.equals("2")) {
//            locale = new Locale("en", "US");
//        }
//        return locale;
//    }
//
//    @Override
//    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
//    }
//
//
//    public String getQueryParams(HttpServletRequest request, String key) {
//        try {
//            String method = request.getMethod();
//            if("POST".equals(method)){
//                String parameter = request.getParameter(key);
//                if(!StringUtils.isEmpty(parameter)){
//                    return parameter;
//                }
//                Object attribute = request.getAttribute(key);
//                if(null != attribute){
//                    return attribute.toString();
//                }
//                StringBuffer sb = new StringBuffer();
//                ServletInputStream inputStream = request.getInputStream();
//                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
//                String s;
//                while ((s = bufferedReader.readLine()) != null){
//                    sb.append(s);
//                }
//                bufferedReader.close();
//                inputStream.close();
//                JSONObject jsonObject = JSON.parseObject(sb.toString());
//                return jsonObject.getString(key);
//            }else{
//                String parameter = request.getParameter(key);
//                if(!StringUtils.isEmpty(parameter)){
//                    return parameter;
//                }
//                Object attribute = request.getAttribute(key);
//                if(null != attribute){
//                    return attribute.toString();
//                }
//            }
//        }catch (Exception e){
//            e.printStackTrace();
//        }
//        return null;
//    }
//}
package com.xiaopeng.pushMessage.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Locale;
public class MyLocaleResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        //1=中文,2=英文,3=印尼文
        //1=Chinese, 2=English, 3=Indonesian.
        String language = getQueryParams(request, "language");
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空
        //If the request is not empty.
        if (language.equals("1")) {
            locale = new Locale("zh", "CN");
        } else if (language.equals("2")) {
            locale = new Locale("en", "US");
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
    }
    public String getQueryParams(HttpServletRequest request, String key) {
        try {
            String method = request.getMethod();
            if("POST".equals(method)){
                String parameter = request.getParameter(key);
                if(!StringUtils.isEmpty(parameter)){
                    return parameter;
                }
                Object attribute = request.getAttribute(key);
                if(null != attribute){
                    return attribute.toString();
                }
                StringBuffer sb = new StringBuffer();
                ServletInputStream inputStream = request.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
                String s;
                while ((s = bufferedReader.readLine()) != null){
                    sb.append(s);
                }
                bufferedReader.close();
                inputStream.close();
                JSONObject jsonObject = JSON.parseObject(sb.toString());
                return jsonObject.getString(key);
            }else{
                String parameter = request.getParameter(key);
                if(!StringUtils.isEmpty(parameter)){
                    return parameter;
                }
                Object attribute = request.getAttribute(key);
                if(null != attribute){
                    return attribute.toString();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
xp-robotaxi-remote-driving-boot/src/main/java/com/xiaopeng/remoteDriving/config/MyLocaleResolver.java
@@ -23,6 +23,7 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空 
        //If the request is not empty.
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/config/MyLocaleResolver.java
@@ -23,6 +23,7 @@
        Locale locale = null;
        if (StringUtils.isEmpty(language)) {
            locale = Locale.getDefault();
            language = "1";
        }
        //如果请求不为空
        //If the request is not empty.