“mitao”
2026-02-14 244228feba355b4df8e160fd4c7c353f674044f1
Merge remote-tracking branch 'origin/master'

# Conflicts:
# xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpClockInController.java
# xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpClockInService.java
# xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java
12个文件已添加
41个文件已修改
1507 ■■■■■ 已修改文件
xp-robotaxi-account-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpFleetClientFallbackFactory.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpFleetClient.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/maintenance/XpClockIn.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderTimeoutLog.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/map/PointByIpVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/XpPointsListVo.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpCheckLocationController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpClockInController.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpSchedulingDetailController.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpShiftController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/dto/ClockDTO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/dto/ShiftAttendanceDTO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/dto/VerifyRangeDTO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpCheckLocationService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpClockInService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpSchedulingDetailService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpCheckLocationServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpSchedulingDetailServiceImpl.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/vo/ShiftAttendanceVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/IndexController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceAreaServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/AmapWalkingRouteUtil.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/GaodeMapService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/pom.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/constant/I18nKeyConstant.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpPointService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TimeRangeChecker.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/XpPricingUtil.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/bootstrap.yml 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/i18n/messages.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/i18n/messages_en_US.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/i18n/messages_zh_CN.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpPointMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/base/AbstractJob.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/jobs/EndTripJob.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/jobs/OrderCancelJob.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mapper/XpOrderTimeoutLogMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/XpOrderTimeoutLogService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderTimeoutLogServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-push-message-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/pom.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/pom.xml
@@ -109,17 +109,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/vehicleConvoy/XpFleetClientFallbackFactory.java
@@ -29,6 +29,13 @@
                // 降级返回空列表
                return R.ok(new ArrayList<>());
            }
            @Override
            public R<XpFleet> getFleetByOperationsUserId(Long operationsUserId) {
                log.error("通过运营人员id查询车队信息失败,原因: {}", cause.getMessage());
                // 降级返回空列表
                return R.fail("通过运营人员id查询车队信息失败,"+cause.getMessage());
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpFleetClient.java
@@ -5,8 +5,10 @@
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;
@@ -28,4 +30,12 @@
     */
    @PostMapping("/inner/fleet/listByIds")
    R<List<XpFleet>> listByIds(@RequestBody List<Long> ids);
    /**
     * 通过运营人员id查询车队信息
     *
     * @param operationsUserId 运营人员id
     * @return 车队信息
     */
    @GetMapping("/inner/v1/fleet/getFleetByOperationsUserId")
    R<XpFleet> getFleetByOperationsUserId(@RequestParam Long operationsUserId);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/maintenance/XpClockIn.java
@@ -3,9 +3,12 @@
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xiaopeng.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -36,19 +39,33 @@
    @ApiModelProperty(value = "用户id")
    private Long appUserId;
    @ApiModelProperty(value = "打卡日期")
    private LocalDateTime clockTime;
    @ApiModelProperty(value = "上班打卡日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime clockInTime;
    @ApiModelProperty(value = "上班打卡日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime clockOutTime;
    @ApiModelProperty(value = "班次id")
    private Long shiftId;
    @ApiModelProperty(value = "打卡结果 1=正常 2=迟到 3=缺卡")
    private Integer clockResult;
    @ApiModelProperty(value = "上班打卡结果 1=正常 2=迟到 3=缺卡")
    private Integer clockInResult;
    @ApiModelProperty(value = "打卡经度")
    private Double clockLon;
    @ApiModelProperty(value = "下班打卡结果 1=正常 2=早退 3=缺卡")
    private Integer clockOutResult;
    @ApiModelProperty(value = "打卡纬度")
    private Double clockLat;
    @ApiModelProperty(value = "上班打卡经度")
    private Double clockInLon;
    @ApiModelProperty(value = "上班打卡纬度")
    private Double clockInLat;
    @ApiModelProperty(value = "下班打卡经度")
    private Double clockOutLon;
    @ApiModelProperty(value = "下班打卡纬度")
    private Double clockOutLat;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderTimeoutLog.java
New file
@@ -0,0 +1,44 @@
package com.xiaopeng.api.model.order;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xiaopeng.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * <p>
 * 订单超时表
 * </p>
 *
 * @author xiaochen
 * @since 2026-02-14
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("xp_order_timeout_log")
@ApiModel(value="XpOrderTimeoutLog对象", description="订单超时表")
public class XpOrderTimeoutLog extends BaseModel {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "自增id")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "订单id")
    @TableField("order_id")
    private Long orderId;
    @ApiModelProperty(value = "小程序用户id")
    @TableField("user_id")
    private Long userId;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/map/PointByIpVo.java
New file
@@ -0,0 +1,15 @@
package com.xiaopeng.api.vo.map;
import com.xiaopeng.api.dto.operation.LonLatDto;
import io.swagger.annotations.ApiModel;
import lombok.Data;
@Data
@ApiModel("根据ip获取点位")
public class PointByIpVo {
    private LonLatDto point;
    private String city;
    private String adcode;
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/XpPointsListVo.java
@@ -28,4 +28,13 @@
    @ApiModelProperty("电子围栏区域id")
    private Long electronicAreaId;
    private Integer effectivePeriod;
    private String effectiveTimePeriod;
    private Integer state=1;
}
xp-robotaxi-automatic-pilot-boot/pom.xml
@@ -106,17 +106,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java
@@ -270,6 +270,56 @@
    /**
     * XPU发布系统运行状态---车辆实时位置
     *
     * @param msgContent
     */
    public void systemStatusXpuAndReassign(MsgContent msgContent) {
        //开始处理消息对应的业务逻辑
        SystemRuntimeStatus systemRuntimeStatus = msgContent.getContent_detail().getObject("systemRuntimeStatus", SystemRuntimeStatus.class);
        AbnormalReason abnormalReason = systemRuntimeStatus.getSystem_status().getAbnormal_reason();
        // 判断是否为故障状态
        if (abnormalReason == AbnormalReason.FAULT) {
            log.info("车辆故障,开始处理故障");
            // TODO 添加异常任务
            // 解绑用户与车辆关系
            CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
            String orderId = systemRuntimeStatus.getRunning_order_status_info().getOrder_id();
            XpOrder xpOrder = xpOrderClient.queryOrderById(Long.parseLong(orderId)).getData();
            XpVehicle vehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
            if (vehicle != null) {
                // 修改车辆状态
                vehicle.setOperatingStatus(4);
                xpVehicleClient.updateVehicleById(vehicle);
                cancelTaxiPassengerVo.setVin(vehicle.getVehicleVin());
                cancelTaxiPassengerVo.setUid(xpOrder.getAppUserId().toString());
                cancelTaxiPassengerVo.setOrderNo(xpOrder.getOrderNumber());
                // TODO
//            cancelTaxiPassengerVo.setAppId();
                userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo);
            }
            // 取消订单
            R<?> r = xpOrderClient.cancelOrder(String.valueOf(xpOrder.getId()));
            if (r.getCode() == R.SUCCESS){
                log.info("取消订单成功");
                // TODO 重新派单
                ReassignOrderDTO reassignOrderDTO = new ReassignOrderDTO();
                reassignOrderDTO.setUid(xpOrder.getAppUserId().toString());
                reassignOrderDTO.setOrderNo(xpOrder.getOrderNumber());
                reassignOrderDTO.setCount(10);
                RealtimePose pose = systemRuntimeStatus.getPose();
                Wgs naviPose = pose.getNavi_pose();
                Double latitude = naviPose.getLatitude();
                Double longitude = naviPose.getLongitude();
                reassignOrderDTO.setLongitude(longitude);
                reassignOrderDTO.setLatitude(latitude);
                reassignOrderClient.reassignOrder(reassignOrderDTO);
            }
        }
    }
    /**
     * XPU向云端请求导航
     *
     * @param msgContent
xp-robotaxi-itinerary-boot/pom.xml
@@ -97,17 +97,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
xp-robotaxi-maintenance-boot/pom.xml
@@ -103,17 +103,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpCheckLocationController.java
@@ -3,6 +3,7 @@
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.DisabledEnum;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.maintenance.service.IXpCheckLocationService;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@@ -24,6 +25,7 @@
@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class XpCheckLocationController {
    private final IXpCheckLocationService xpCheckLocationService;
    private final TokenService tokenService;
    @GetMapping("/api/v1/check-location/list")
    @ApiOperation(value = "获取打卡地址列表", tags = {"管理后台-车队管理"})
@@ -36,4 +38,12 @@
    public R<XpCheckLocation> queryById(@RequestParam Long id) {
        return R.ok(xpCheckLocationService.getById(id));
    }
    @ApiOperation(value = "根据ID查询打卡地址", tags = {"运维APP-排班与考勤"})
    @GetMapping("/api/v1/check-location/queryByUserId")
    public R<XpCheckLocation> queryByUserId() {
        Long id = tokenService.getLoginOperationUser().getId();
        return R.ok(xpCheckLocationService.queryByUserId(id));
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpClockInController.java
@@ -1,28 +1,58 @@
package com.xiaopeng.maintenance.controller;
import com.xiaopeng.api.model.maintenance.XpErrorType;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.maintenance.dto.ClockDTO;
import com.xiaopeng.maintenance.dto.ShiftAttendanceDTO;
import com.xiaopeng.maintenance.dto.VerifyRangeDTO;
import com.xiaopeng.maintenance.service.IXpClockInService;
import com.xiaopeng.maintenance.vo.ShiftAttendanceVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
/**
 * <p>
 * 打卡明细 前端控制器
 * </p>
 * 打卡管理 Controller
 *
 * @author xiaochen
 * @since 2026-02-06
 * @author mitao
 * @date 2026-02-14
 */
@Slf4j
@RestController
@RequestMapping("")
@Api(tags = "打卡管理")
public class XpClockInController {
    @Autowired
    private IXpClockInService xpClockInService;
    @Autowired
    private TokenService tokenService;
    @ApiOperation(value = "校验是否在打卡范围内", tags = "运维APP-排班与考勤")
    @PostMapping("/api/v1/clock/verifyRange")
    public R<Boolean> verifyRange(@RequestBody VerifyRangeDTO dto) {
        return R.ok(xpClockInService.verifyRange(dto));
    }
    private IXpClockInService clockInService;
    @ApiOperation(value = "上班打卡", tags = "运维APP-排班与考勤")
    @PostMapping("/api/v1/clock/clockIn")
    public R<String> clockIn(@RequestBody ClockDTO dto) {
        Long id = tokenService.getLoginOperationUser().getId();
        dto.setOperationsUserId(id);
        return xpClockInService.clockIn(dto);
    }
    /**
     * 检查用户是否上班中(内部接口)
     */
@@ -33,4 +63,14 @@
        Boolean workFlag = clockInService.checkWorkStatus(appUserId);
        return R.ok(workFlag);
    }
    @ApiOperation(value = "下班打卡", tags = "运维APP-排班与考勤")
    @PostMapping("/api/v1/clock/clockOut")
    public R<String> clockOut(@RequestBody ClockDTO dto) {
        Long id = tokenService.getLoginOperationUser().getId();
        dto.setOperationsUserId(id);
        return xpClockInService.clockOut(dto);
}
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpSchedulingDetailController.java
New file
@@ -0,0 +1,44 @@
package com.xiaopeng.maintenance.controller;
import com.xiaopeng.api.model.maintenance.XpErrorType;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.maintenance.dto.ShiftAttendanceDTO;
import com.xiaopeng.maintenance.service.IXpSchedulingDetailService;
import com.xiaopeng.maintenance.vo.ShiftAttendanceVO;
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.util.List;
/**
 * <p>
 * 排班明细 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2026-02-06
 */
@Api(tags = "排班明细")
@RestController
@RequestMapping("")
public class XpSchedulingDetailController {
    @Autowired
    private IXpSchedulingDetailService xpSchedulingDetailService;
    @Autowired
    private TokenService tokenService;
    @ApiOperation(value = "查询个人排班与考勤信息", response = XpErrorType.class, tags = "运维APP-排班与考勤")
    @PostMapping("/api/v1/scheduling/shiftAttendance")
    public R<ShiftAttendanceVO> shiftAttendance(@RequestBody ShiftAttendanceDTO dto) {
        Long id = tokenService.getLoginOperationUser().getId();
        dto.setOperationsUserId(id);
        return R.ok(xpSchedulingDetailService.shiftAttendance(dto));
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpShiftController.java
New file
@@ -0,0 +1,20 @@
package com.xiaopeng.maintenance.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 班次管理 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2026-02-06
 */
@RestController
@RequestMapping("/xp-shift")
public class XpShiftController {
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/dto/ClockDTO.java
New file
@@ -0,0 +1,28 @@
package com.xiaopeng.maintenance.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "运维APP-打卡DTO")
public class ClockDTO implements Serializable {
    @ApiModelProperty(value = "用户经度")
    private Double userLon;
    @ApiModelProperty(value = "用户纬度")
    private Double userLat;
    @ApiModelProperty(value = "班次id 只传上班打卡的情况")
    private Long shiftId;
    @ApiModelProperty(value = "打卡地id")
    private Long locationId;
    @ApiModelProperty(value = "运维用户id 前端忽略")
    private Long operationsUserId;
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/dto/ShiftAttendanceDTO.java
New file
@@ -0,0 +1,21 @@
package com.xiaopeng.maintenance.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDate;
@Data
@ApiModel(value = "运维APP-查询个人排版与考勤dto")
public class ShiftAttendanceDTO implements Serializable {
    @ApiModelProperty(value = "排班时间")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private LocalDate schedulingDate;
    @ApiModelProperty(value = "运维用户id 前端忽略")
    private Long operationsUserId;
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/dto/VerifyRangeDTO.java
New file
@@ -0,0 +1,22 @@
package com.xiaopeng.maintenance.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "运维APP-验证范围DTO")
public class VerifyRangeDTO implements Serializable {
    @ApiModelProperty(value = "打卡地id")
    private Long locationId;
    @ApiModelProperty(value = "用户经度")
    private Double userLon;
    @ApiModelProperty(value = "用户纬度")
    private Double userLat;
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpCheckLocationService.java
@@ -13,4 +13,11 @@
 */
public interface IXpCheckLocationService extends IService<XpCheckLocation> {
    /**
     * 根据用户id查询打卡地址
     *
     * @param id
     * @return
     */
    XpCheckLocation queryByUserId(Long id);
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpClockInService.java
@@ -2,6 +2,9 @@
import com.xiaopeng.api.model.maintenance.XpClockIn;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.maintenance.dto.ClockDTO;
import com.xiaopeng.maintenance.dto.VerifyRangeDTO;
/**
 * <p>
@@ -14,6 +17,29 @@
public interface IXpClockInService extends IService<XpClockIn> {
    /**
     * 验证打卡范围
     *
     * @param dto
     * @return
     */
    Boolean verifyRange(VerifyRangeDTO dto);
    /**
     * 打卡
     *
     * @param dto
     * @return
     */
    R<String> clockIn(ClockDTO dto);
    /**
     * 下班打卡
     *
     * @param dto
     * @return
     */
    R<String> clockOut(ClockDTO dto);
    /**
     * 检查用户是否上班中
     * @param appUserId 用户ID
     * @return true=上班中(只打了上班卡), false=未上班或已下班
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpSchedulingDetailService.java
@@ -2,6 +2,8 @@
import com.xiaopeng.api.model.maintenance.XpSchedulingDetail;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.maintenance.dto.ShiftAttendanceDTO;
import com.xiaopeng.maintenance.vo.ShiftAttendanceVO;
/**
 * <p>
@@ -13,4 +15,10 @@
 */
public interface IXpSchedulingDetailService extends IService<XpSchedulingDetail> {
    /**
     * 查询个人排班与考勤信息
     * @param dto
     * @return
     */
    ShiftAttendanceVO shiftAttendance(ShiftAttendanceDTO dto);
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpCheckLocationServiceImpl.java
@@ -1,10 +1,16 @@
package com.xiaopeng.maintenance.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.maintenance.mapper.XpCheckLocationMapper;
import com.xiaopeng.maintenance.service.IXpCheckLocationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
 * <p>
@@ -16,5 +22,15 @@
 */
@Service
public class XpCheckLocationServiceImpl extends ServiceImpl<XpCheckLocationMapper, XpCheckLocation> implements IXpCheckLocationService {
    @Autowired
    private XpFleetClient xpFleetClient;
    @Override
    public XpCheckLocation queryByUserId(Long id) {
        // 查询运维人员的车队信息
        XpFleet xpFleet = xpFleetClient.getFleetByOperationsUserId(id).getData();
        if (Objects.isNull(xpFleet)) {
            return null;
        }
        return this.getById(xpFleet.getLocationId());
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java
@@ -1,11 +1,30 @@
package com.xiaopeng.maintenance.service.impl;
import com.xiaopeng.api.model.maintenance.XpClockIn;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.xiaopeng.api.model.maintenance.XpClockIn;
import com.xiaopeng.api.model.maintenance.XpShift;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.utils.DateUtils;
import com.xiaopeng.common.core.utils.GeodesyUtil;
import com.xiaopeng.maintenance.dto.ClockDTO;
import com.xiaopeng.maintenance.dto.VerifyRangeDTO;
import com.xiaopeng.maintenance.mapper.XpCheckLocationMapper;
import com.xiaopeng.maintenance.mapper.XpClockInMapper;
import com.xiaopeng.maintenance.mapper.XpShiftMapper;
import com.xiaopeng.maintenance.service.IXpClockInService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -24,6 +43,115 @@
@Service
public class XpClockInServiceImpl extends ServiceImpl<XpClockInMapper, XpClockIn> implements IXpClockInService {
    @Autowired
    private XpCheckLocationMapper xpCheckLocationMapper;
    @Autowired
    private XpShiftMapper xpShiftMapper;
    @Autowired
    private XpClockInMapper xpClockInMapper;
    @Override
    public Boolean verifyRange(VerifyRangeDTO dto) {
        XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId());
        Map<String, Double> distance = GeodesyUtil.getDistance(dto.getUserLon()+","+dto.getUserLat(), xpCheckLocation.getLocationLon()+","+xpCheckLocation.getLocationLat());
        if (null != distance) {
            Double distance1 = distance.get("WGS84");
            if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange())>0) {
                return false;
            }else {
                return true;
            }
        } else {
            log.error("调用高德计算距离出错");
            return false;
        }
    }
    @Override
    public R<String> clockIn(ClockDTO dto) {
        XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId());
        Map<String, Double> distance = GeodesyUtil.getDistance(dto.getUserLon()+","+dto.getUserLat(), xpCheckLocation.getLocationLon()+","+xpCheckLocation.getLocationLat());
        if (null != distance) {
            Double distance1 = distance.get("WGS84");
            if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) {
                return R.fail("不在打卡范围内");
            }
        }else {
            return R.fail("打卡失败");
        }
        // 查询班次
        XpShift xpShift = xpShiftMapper.selectById(dto.getShiftId());
        if (null == xpShift) {
            return R.fail("班次不存在");
        }
        // 开始打卡时间
        String startTime = xpShift.getStartTime();
        LocalDateTime startLocalDateTime = DateUtils.stringToLocalDateTime(LocalDate.now() + " " + startTime + ":00");
        XpClockIn xpClockIn = new XpClockIn();
        xpClockIn.setAppUserId(dto.getOperationsUserId());
        xpClockIn.setClockInTime(LocalDateTime.now());
        xpClockIn.setClockInLon(dto.getUserLon());
        xpClockIn.setClockInLat(dto.getUserLat());
        xpClockIn.setShiftId(dto.getShiftId());
        if (xpClockIn.getClockInTime().isAfter(startLocalDateTime)) {
            xpClockIn.setClockInResult(2);
        }else {
            xpClockIn.setClockInResult(1);
        }
        this.save(xpClockIn);
        return R.ok();
    }
    @Override
    public R<String> clockOut(ClockDTO dto) {
        XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId());
        Map<String, Double> distance = GeodesyUtil.getDistance(dto.getUserLon()+","+dto.getUserLat(), xpCheckLocation.getLocationLon()+","+xpCheckLocation.getLocationLat());
        if (null != distance) {
            Double distance1 = distance.get("WGS84");
            if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) {
                return R.fail("不在打卡范围内");
            }
        }else {
            return R.fail("打卡失败");
        }
        // 查询上班打卡信息
        XpClockIn xpClockIn = xpClockInMapper.selectOne(Wrappers.lambdaQuery(XpClockIn.class)
                .eq(XpClockIn::getAppUserId, dto.getOperationsUserId())
                .likeRight(XpClockIn::getClockInTime, LocalDate.now())
                .last("LIMIT 1"));
        if(Objects.isNull(xpClockIn)){
            return R.fail("请先上班打卡");
        }
        // 查询班次
        XpShift xpShift = xpShiftMapper.selectById(xpClockIn.getShiftId());
        if (null == xpShift) {
            return R.fail("班次不存在");
        }
        // 开始打卡时间
        String endTime = xpShift.getEndTime();
        LocalDateTime endLocalDateTime = DateUtils.stringToLocalDateTime(LocalDate.now() + " " + endTime + ":00");
        xpClockIn.setClockOutTime(LocalDateTime.now());
        xpClockIn.setClockOutLon(dto.getUserLon());
        xpClockIn.setClockOutLat(dto.getUserLat());
        if (xpClockIn.getClockOutTime().isBefore(endLocalDateTime)) {
            xpClockIn.setClockInResult(2);
        }else {
            xpClockIn.setClockInResult(1);
        }
        this.updateById(xpClockIn);
        return R.ok();
    }
    @Override
    public Boolean checkWorkStatus(Long appUserId) {
        if (appUserId == null) {
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpSchedulingDetailServiceImpl.java
@@ -1,10 +1,25 @@
package com.xiaopeng.maintenance.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.feignClient.maintenance.XpCheckLocationClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.xiaopeng.api.model.maintenance.XpClockIn;
import com.xiaopeng.api.model.maintenance.XpSchedulingDetail;
import com.xiaopeng.api.model.maintenance.XpShift;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.maintenance.dto.ShiftAttendanceDTO;
import com.xiaopeng.maintenance.mapper.XpCheckLocationMapper;
import com.xiaopeng.maintenance.mapper.XpClockInMapper;
import com.xiaopeng.maintenance.mapper.XpSchedulingDetailMapper;
import com.xiaopeng.maintenance.mapper.XpShiftMapper;
import com.xiaopeng.maintenance.service.IXpSchedulingDetailService;
import com.xiaopeng.maintenance.vo.ShiftAttendanceVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
 * <p>
@@ -17,4 +32,56 @@
@Service
public class XpSchedulingDetailServiceImpl extends ServiceImpl<XpSchedulingDetailMapper, XpSchedulingDetail> implements IXpSchedulingDetailService {
    @Autowired
    private XpFleetClient xpFleetClient;
    @Autowired
    private XpCheckLocationMapper xpCheckLocationMapper;
    @Autowired
    private XpShiftMapper xpShiftMapper;
    @Autowired
    private XpClockInMapper xpClockInMapper;
    @Override
    public ShiftAttendanceVO shiftAttendance(ShiftAttendanceDTO dto) {
        // 查询用户所属车队
        XpFleet xpFleet = xpFleetClient.getFleetByOperationsUserId(dto.getOperationsUserId()).getData();
        if (Objects.isNull(xpFleet)) {
            return null;
        }
        // 查询打卡地
        XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(xpFleet.getLocationId());
        if (Objects.isNull(xpCheckLocation)) {
            return null;
        }
        // 查询排班明细
        XpSchedulingDetail schedulingDetail = this.getOne(Wrappers.lambdaQuery(XpSchedulingDetail.class)
                .likeRight(XpSchedulingDetail::getPlanTime, dto.getSchedulingDate())
                .eq(XpSchedulingDetail::getFleetId, xpFleet.getId())
                .last("LIMIT 1"));
        if (Objects.isNull(schedulingDetail)) {
            return null;
        }
        // 查询班次
        XpShift xpShift = xpShiftMapper.selectById(schedulingDetail.getShiftId());
        if (Objects.isNull(xpShift)) {
            return null;
        }
        ShiftAttendanceVO shiftAttendanceVO = new ShiftAttendanceVO();
        shiftAttendanceVO.setXpShift(xpShift);
        shiftAttendanceVO.setXpCheckLocation(xpCheckLocation);
        // 查询当前用户打卡信息
        XpClockIn xpClockIn = xpClockInMapper.selectOne(Wrappers.lambdaQuery(XpClockIn.class)
                .eq(XpClockIn::getAppUserId, dto.getOperationsUserId())
                .likeRight(XpClockIn::getClockInTime, dto.getSchedulingDate())
                .last("LIMIT 1"));
        if(Objects.isNull(xpClockIn)){
            shiftAttendanceVO.setClockIn(false);
            shiftAttendanceVO.setClockOut(false);
        }else {
            shiftAttendanceVO.setClockIn(true);
            shiftAttendanceVO.setClockOut(xpClockIn.getClockOutTime() != null);
        }
        return shiftAttendanceVO;
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/vo/ShiftAttendanceVO.java
New file
@@ -0,0 +1,26 @@
package com.xiaopeng.maintenance.vo;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
import com.xiaopeng.api.model.maintenance.XpShift;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "运维APP-排班明细返回")
public class ShiftAttendanceVO implements Serializable {
    @ApiModelProperty(value = "班次信息")
    private XpShift xpShift;
    @ApiModelProperty(value = "打卡地信息")
    private XpCheckLocation xpCheckLocation;
    @ApiModelProperty(value = "是否上班打卡 true false")
    private boolean isClockIn;
    @ApiModelProperty(value = "是否下班打卡 true false")
    private boolean isClockOut;
}
xp-robotaxi-map-boot/pom.xml
@@ -101,17 +101,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/IndexController.java
@@ -1,5 +1,6 @@
package com.xiaopeng.map.controller;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.dto.operation.PointMapQuery;
import com.xiaopeng.api.feignClient.operation.XpPointClient;
import com.xiaopeng.api.model.operation.XpPoint;
@@ -8,6 +9,7 @@
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.map.utils.AmapWalkingRouteUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -15,6 +17,8 @@
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
/**
@@ -30,6 +34,7 @@
    private TokenService tokenService;
    @Autowired
    private XpPointClient pointClient;
    @ApiOperation(value = "地图导航")
    @PostMapping("/api/v1/map")
    public R<List<XpPoint>> pointMap(@RequestBody PointMapQuery query) {
@@ -51,7 +56,6 @@
        return pointClient.queryById(id);
    }
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpMapController.java
@@ -4,6 +4,7 @@
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.vo.map.PointByIpVo;
import com.xiaopeng.api.vo.map.XpDistanceAndDurationVo;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.common.core.constant.CacheConstants;
@@ -11,17 +12,18 @@
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.map.mangodb.model.GeoPoint;
import com.xiaopeng.map.mangodb.service.XpGeoFencingPolygonService;
import com.xiaopeng.map.utils.AmapWalkingRouteUtil;
import com.xiaopeng.map.utils.GDMapGeocodingUtil;
import com.xiaopeng.map.utils.mangoUtils.MongoGeoFenceUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -119,4 +121,16 @@
    /**
     * 根据ip获取点位
     */
    @ApiOperation(value = "根据ip获取点位", tags = {"小程序-站点导航"})
    @GetMapping("/point/getPointByIp")
    public R<PointByIpVo> getPointByIp(HttpServletRequest request) throws IOException {
        String ip = request.getRemoteAddr();
        PointByIpVo pointByIpVo = AmapWalkingRouteUtil.ipLocation(ip);
        return R.ok(pointByIpVo);
    }
}
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceAreaServiceImpl.java
@@ -279,9 +279,8 @@
    @Override
    public WalkTimeVo getWalkTime(WalkTimeDto dto) {
        WalkTimeVo walkTimeVo = new WalkTimeVo();
        String apiKey = "66ae577f63898dae3016d599fda57221";
        // 创建服务实例
        GaodeMapService gaodeService = new GaodeMapService(apiKey);
        GaodeMapService gaodeService = new GaodeMapService();
        AmapWalkingRouteUtil.WalkingRouteResult result = gaodeService.getWalkingRoute(dto.getStartLongitude()+","+dto.getStartLatitude(),dto.getEndLongitude()+","+dto.getEndLatitude());
        if (result.isSuccess()) {
            System.out.println("预计时间: " + result.getFormattedDuration());
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/AmapWalkingRouteUtil.java
@@ -1,18 +1,21 @@
package com.xiaopeng.map.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaopeng.api.dto.operation.LonLatDto;
import com.xiaopeng.api.vo.map.PointByIpVo;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.HttpUtils;
import com.xiaopeng.map.constant.I18nKeyConstant;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -26,14 +29,103 @@
    // 高德地图Web服务API配置
    private static final String AMAP_API_BASE_URL = "https://restapi.amap.com/v3/direction/walking";
    private static final String AMAP_GEOCODING_URL = "https://restapi.amap.com/v3/geocode/geo";
    // 高德API基础URL
    private static final String IP_API_URL = "https://restapi.amap.com/v3/ip";
    private static final String GEOCODE_API_URL = "https://restapi.amap.com/v3/geocode/geo";
    // 你的高德地图API Key(请替换成你自己的)
    private final String apiKey;
    // HTTP客户端
    private final OkHttpClient httpClient;
    private final ObjectMapper objectMapper;
    //    @Value("${map.apikey}")
    private static String apiKey="66ae577f63898dae3016d599fda57221";
    /**
     * IP定位
     */
    public static PointByIpVo ipLocation(String ip) throws IOException {
        PointByIpVo pointByIpVo = new PointByIpVo();
        StringBuilder urlBuilder = new StringBuilder(IP_API_URL)
                .append("?key=").append(apiKey)
                .append("&output=json");
        if (ip != null && !ip.trim().isEmpty()) {
            urlBuilder.append("&ip=").append(ip);
        }
        String response = HttpUtils.sendGet(urlBuilder.toString());
        JSONObject jsonObject = JSONObject.parseObject(response);
        // 城市名称
        String city = jsonObject.getString("city");
        String province = jsonObject.getString("province");
        if(("局域网").equals(province)){
            pointByIpVo.setCity("成都市");
            LonLatDto lonLatDto = new LonLatDto(104.07, 30.660);
            pointByIpVo.setPoint(lonLatDto);
            pointByIpVo.setAdcode("510100");
            return pointByIpVo;
        }
        String adcode = jsonObject.getString("adcode");
        if (city == null || city.trim().isEmpty() || "[]".equals(city)) {
            // 直辖市:adcode前两位是11,12,31,50
            if (adcode != null) {
                String prefix = adcode.substring(0, 2);
                if ("11".equals(prefix)) {
                    city = "北京市";
                }
                if ("12".equals(prefix)) {
                    city = "天津市";
                }
                if ("31".equals(prefix)) {
                    city = "上海市";
                }
                if ("50".equals(prefix)) {
                    city = "重庆市";
                }
                // 如果没有city但有province,使用省份
                city = province;
            }
        }
        LonLatDto lonLatDto = geocodeCity(city);
        pointByIpVo.setCity(city);
        pointByIpVo.setPoint(lonLatDto);
        pointByIpVo.setAdcode(adcode);
        return pointByIpVo;
    }
    private static LonLatDto geocodeCity(String cityName) throws IOException {
        String url = GEOCODE_API_URL + "?key=" +apiKey + "&address=" + cityName + "&output=json&city=" + cityName;
        String response = HttpUtils.sendGet(url);
        System.out.println(response);
        JSONObject jsonObject = JSONObject.parseObject(response);
        // 获取geocodes数组
        JSONArray geocodes = jsonObject.getJSONArray("geocodes");
        if (geocodes == null || geocodes.isEmpty()) {
            return null;
        }
        // 取第一个geocode
        JSONObject firstGeocode = geocodes.getJSONObject(0);
        // 获取location字段
        String lonLat = firstGeocode.getString("location");
        String[] lonLatArray = lonLat.split(",");
        double longitude = Double.parseDouble(lonLatArray[0]);
        double latitude = Double.parseDouble(lonLatArray[1]);
        return new LonLatDto(longitude, latitude);
    }
        public static void main (String[]args) throws IOException {
            geocodeCity("凉山彝族自治州");
        }
    /**
     * 构造函数
     * @param apiKey 高德地图API Key
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/utils/GaodeMapService.java
@@ -8,6 +8,7 @@
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@@ -18,11 +19,9 @@
    private static final String WALKING_API_URL = "https://restapi.amap.com/v3/direction/walking";
    
    // 你的高德地图API Key
    private String apiKey;
//    @Value("${map.apikey}")
    private String apiKey="66ae577f63898dae3016d599fda57221";
    
    public GaodeMapService(String apiKey) {
        this.apiKey = apiKey;
    }
    
    /**
     * 获取步行路径规划
xp-robotaxi-operation-boot/pom.xml
@@ -165,16 +165,25 @@
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.yaml</groupId>-->
<!--            <artifactId>snakeyaml</artifactId>-->
<!--            <version>1.33</version>-->
<!--        </dependency>-->
    </dependencies>
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/constant/I18nKeyConstant.java
@@ -11,6 +11,9 @@
    String NO_CURRENT_REGION = "no.current.region";
    String NO_CURRENT_POINT = "no.current.point";
    String SYSTEM_EXCEPTION = "system.exception";
    String TO_WORK = "to.work";
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpPointService.java
@@ -18,6 +18,7 @@
import com.xiaopeng.api.dto.operation.PointMapQuery;
import com.xiaopeng.operation.vo.OrderDistanceVo;
import java.time.LocalTime;
import java.util.List;
/**
@@ -35,6 +36,14 @@
     * 获取附近上车站点
     */
    NearestVo nearest(@Valid NearestDto dto);
    /**
     * 判断点位是否时间段内
     * @param now
     * @return
     */
    boolean getInTime(LocalTime now,  Integer effectivePeriod, String effectiveTimePeriod);
    List<XpPoint> pointMap(PointMapQuery query);
    List<XpPoint> listAll(PointPageListQuery query);
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPointServiceImpl.java
@@ -3,10 +3,12 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.dto.operation.PointMapQuery;
import com.xiaopeng.api.dto.order.HistoryEndPointsDto;
import com.xiaopeng.api.dto.order.HistoryStartPointsDto;
import com.xiaopeng.api.model.operation.XpPoint;
import com.xiaopeng.api.model.operation.XpSystemSetting;
import com.xiaopeng.api.query.operation.PointPageListQuery;
import com.xiaopeng.api.vo.order.XpHistoryEndPointsVo;
import com.xiaopeng.api.vo.order.XpHistoryStartPointsVo;
import com.xiaopeng.api.vo.order.XpPointsListVo;
@@ -17,17 +19,17 @@
import com.xiaopeng.operation.mapper.XpPointMapper;
import com.xiaopeng.operation.mapper.XpSystemSettingMapper;
import com.xiaopeng.operation.query.PointByNameQuery;
import com.xiaopeng.api.dto.operation.PointMapQuery;
import com.xiaopeng.api.query.operation.PointPageListQuery;
import com.xiaopeng.operation.service.XpPointService;
import com.xiaopeng.operation.utils.TimeRangeChecker;
import com.xiaopeng.operation.utils.holiday.HolidayUtil;
import com.xiaopeng.operation.utils.map.FastDistanceCalculator;
import com.xiaopeng.operation.vo.NearestNextVo;
import com.xiaopeng.operation.vo.NearestVo;
import com.xiaopeng.operation.vo.OrderDistanceVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -51,14 +53,17 @@
    @Resource
    private RedisService redisService;
    @Resource
    private HolidayUtil holidayUtil;
    @Override
    public NearestVo nearest(NearestDto dto) {
        // 获取所有的站点信息
        List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).isNotNull(XpPoint::getElectronicAreaId));
        List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).eq(XpPoint::getPointType,1).isNotNull(XpPoint::getElectronicAreaId));
        NearestVo nearestVo = new NearestVo();
        List<NearestNextVo> nearestNextVos = new ArrayList<>();
        LocalTime now = LocalTime.now();
        // 走缓存
        Object orderDistanceObj = redisService.getCacheObject(CacheConstants.NEAR_POINT_DISTANCE);
        Integer orderDistance;
@@ -70,6 +75,11 @@
        }
        for (XpPoint xpPoint : list) {
            // 判断是否在时间段内
            boolean intime = getInTime(now, xpPoint.getEffectivePeriod(), xpPoint.getEffectiveTimePeriod());
            if (!intime) {
                continue;
            }
            // 计算距离 小于的站点
            double v = FastDistanceCalculator.distanceFast(dto.getLatitude(), dto.getLongitude(), xpPoint.getPointLat(), xpPoint.getPointLon());
            if (v < orderDistance) {
@@ -82,6 +92,8 @@
                nearestNextVo.setAddress(xpPoint.getPointAddress());
                nearestNextVo.setTime(xpPoint.getParkingTimeLimit());
                nearestNextVo.setElectronicAreaId(xpPoint.getElectronicAreaId());
                nearestNextVos.add(nearestNextVo);
            }
        }
@@ -92,6 +104,44 @@
        nearestVo.setTotal(nearestNextVos.size());
        return nearestVo;
    }
    public boolean getInTime(LocalTime now,  Integer effectivePeriod, String effectiveTimePeriod){
        //  2非工作日  0工作日  1节假日 3调休日
        Integer holidayToday = holidayUtil.isHolidayToday();
        if(effectivePeriod==1){
            boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod);
            if (!inTime) {
                return false;
            }
        }else if(effectivePeriod==2){
            if(holidayToday!=0){
                return false;
            }
            boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod);
            if (!inTime) {
                return false;
            }
        }else if(effectivePeriod==3){
            if(holidayToday!=2){
                return false;
            }
            boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod);
            if (!inTime) {
                return false;
            }
        }else if(effectivePeriod==4){
            if(holidayToday!=1){
                return false;
            }
            boolean inTime = TimeRangeChecker.isCurrentTimeInRanges(now,effectiveTimePeriod);
            if (!inTime) {
                return false;
            }
        }
        return true;
    }
    @Override
@@ -153,9 +203,18 @@
            return searchVo;
        }
        LocalTime now = LocalTime.now();
        for (XpPointsListVo xpPointsListVo : list) {
            boolean inTime = getInTime(now, xpPointsListVo.getEffectivePeriod(), xpPointsListVo.getEffectiveTimePeriod());
            if (!inTime) {
                // 不在运营段
                xpPointsListVo.setState(0);
            }
        // 组装数据 并排序
        List<XpPointsListVo> resultList = list.stream().peek(vo -> vo.setDistance(FastDistanceCalculator.distanceFast(query.getLat(), query.getLon(), vo.getLatitude(), vo.getLongitude()))).collect(Collectors.toList());
        }
        // 组装数据 并排序 剔除不在运营段的 数据
        List<XpPointsListVo> resultList = list.stream().peek(vo -> vo.setDistance(FastDistanceCalculator.distanceFast(query.getLat(), query.getLon(), vo.getLatitude(), vo.getLongitude()))).filter(vo -> vo.getState() == 1).collect(Collectors.toList());
        // 可用点位
        List<XpPointsListVo> availablePoints = resultList.stream().filter(e -> query.getElectronicAreaId() != null && e.getElectronicAreaId().equals(query.getElectronicAreaId())).sorted(Comparator.comparing(XpPointsListVo::getDistance)).collect(Collectors.toList());//
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -36,6 +36,7 @@
import com.xiaopeng.operation.mapper.*;
import com.xiaopeng.operation.query.PricingPageListQuery;
import com.xiaopeng.operation.service.IXpGeoQueryEntityService;
import com.xiaopeng.operation.service.XpPointService;
import com.xiaopeng.operation.service.XpPricingService;
import com.xiaopeng.operation.utils.XpPricingUtil;
import com.xiaopeng.operation.utils.holiday.HolidayUtil;
@@ -48,6 +49,7 @@
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
@@ -97,9 +99,6 @@
    @Resource
    private XpPointMapper xpPointMapper;
    @Resource
    private XpWhitelistMapper xpWhitelistMapper;
    @Resource
@@ -113,6 +112,9 @@
    @Autowired
    private XpElectronicFenceMapper xpElectronicFenceMapper;
    @Autowired
    private XpPointService xpPointService;
    // 自定义线程池用于并行执行任务
@@ -202,7 +204,7 @@
//            throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
//        }
        // 2. 计算价格(需要在并行任务前完成,因为优惠券查询需要价格)
        // 2. 计算价格(需要在并行任务前完成,因为优惠券查询需要价格) TODO 判断是动态还是一口价
//        XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice(
//                JSON.parseObject(xpPricing.getBasicPricingJson()),
//                JSON.parseArray(xpPricing.getStartingFareJson()),
@@ -278,7 +280,33 @@
        // 8. 构建响应对象
        RouteAndFareVo routeAndFareVo = new RouteAndFareVo();
//        routeAndFareVo.setRoute(routeVo);
        // MOCK数据
        RouteVo routeVo = new RouteVo();
        routeVo.setDistance(3000);
        routeVo.setDuration(5);
        routeVo.setRouteID("1");
        List<LonLatDto> routePolyline = Arrays.asList(
                // 起点:天府新谷
                new LonLatDto(104.05363, 30.58937),
                // 途经点1:府城大道与交子北二路交叉口附近
                new LonLatDto(104.05821, 30.58745),
                // 途经点2:交子大道与成汉南路交叉口
                new LonLatDto(104.06418, 30.58492),
                // 途经点3:交子大道与万象南路交叉口
                new LonLatDto(104.06975, 30.58321),
                // 途经点4:交子大道与益州大道交叉口
                new LonLatDto(104.07366, 30.58198),
                // 终点:金融城(地铁站附近)
                new LonLatDto(104.076544, 30.581039)
        );
        routeVo.setPolyline(routePolyline);
        routeAndFareVo.setRoute(routeVo);
        routeAndFareVo.setXpPricingCard(xpPricingCardVo);
@@ -370,6 +398,9 @@
    @Override
    public Long saveOrder(XpOrderDto dto, String uid) {
        // 是否在运营区域时间内
        XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreaMapper.selectById(dto.getXpElectronicFenceAreaId());
        XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(xpElectronicFenceArea.getElectronicId());
@@ -388,10 +419,21 @@
        }
        // 获取起点经纬度
        XpPoint xpPoint = xpPointMapper.selectById(dto.getStartPointId());
        XpPoint xpPoint = xpPointService.getById(dto.getStartPointId());
        // 终点
        XpPoint endXpPoint = xpPointMapper.selectById(dto.getStartPointId());
        XpPoint endXpPoint = xpPointService.getById(dto.getStartPointId());
        // 判断点位是否在运营区域内
        LocalTime now = LocalTime.now();
        boolean inTime = xpPointService.getInTime(now, xpPoint.getEffectivePeriod(), xpPoint.getEffectiveTimePeriod());
        if(!inTime){
            throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
        inTime = xpPointService.getInTime(now, endXpPoint.getEffectivePeriod(), endXpPoint.getEffectiveTimePeriod());
        if(!inTime){
            throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
        }
        // 判断当天取消的次数 后台配置 redis 查询
        Object cancelOrder = redisService.getCacheObject("CANCEL_ORDER_" + uid);
@@ -575,6 +617,7 @@
    }
    private Boolean isInOperationTime(String operationStrategy) {
        JSONArray jsonArray = JSONArray.parseArray(operationStrategy);
        Integer holidayToday = holidayUtil.isHolidayToday();
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TimeRangeChecker.java
New file
@@ -0,0 +1,84 @@
package com.xiaopeng.operation.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class TimeRangeChecker {
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
    /**
     * 判断当前时间是否在时间段内
     * @param jsonArrayStr JSON数组字符串,格式如 ["08:00-12:00", "13:30-17:30", "18:00-21:00"]
     * @return true 如果在时间段内,false 如果不在
     */
    public static boolean isCurrentTimeInRanges(LocalTime now,String jsonArrayStr) {
        return isTimeInRanges(jsonArrayStr, now);
    }
    /**
     * 判断指定时间是否在时间段内
     * @param jsonArrayStr JSON数组字符串
     * @param time 要判断的时间
     * @return true 如果在时间段内,false 如果不在
     */
    public static boolean isTimeInRanges(String jsonArrayStr, LocalTime time) {
        if (jsonArrayStr == null || jsonArrayStr.isEmpty()) {
            return false;
        }
        try {
            // 直接解析为JSONArray
            JSONArray timeArray = JSON.parseArray(jsonArrayStr);
            if (timeArray == null || timeArray.isEmpty()) {
                return false;
            }
            // 遍历所有时间段
            for (int i = 0; i < timeArray.size(); i++) {
                String timeRange = timeArray.getString(i);
                if (isTimeInRange(time, timeRange)) {
                    return true;
                }
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 判断时间是否在单个时间段内
     */
    private static boolean isTimeInRange(LocalTime time, String timeRange) {
        if (timeRange == null || !timeRange.contains("-")) {
            return false;
        }
        String[] parts = timeRange.split("-");
        if (parts.length != 2) {
            return false;
        }
        try {
            LocalTime start = LocalTime.parse(parts[0].trim(), TIME_FORMATTER);
            LocalTime end = LocalTime.parse(parts[1].trim(), TIME_FORMATTER);
            // 处理跨天的情况(如 22:00-02:00)
            if (end.isBefore(start)) {
                // 跨天时间段
                return !time.isBefore(start) || !time.isAfter(end);
            } else {
                // 正常时间段
                return !time.isBefore(start) && !time.isAfter(end);
            }
        } catch (Exception e) {
            return false;
        }
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/XpPricingUtil.java
@@ -64,6 +64,86 @@
    /**
     * 计算一口价
     */
    public XpPricingCardVo calculationFixedPrice(String uid,JSONObject rule, JSONArray startingFareJson, double distance, long duration, long wait, Double waitMoney,Integer isDynamicPriceStatus,Long openCityId,String dynamicPricingJson) {
        // 默认费率1
        double priceRate=1;
        if(isDynamicPriceStatus==1){
            // 先判断是否已经调价过了
            Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + openCityId + "_" + uid);
            if(userDynamicPriceObj!=null){
                // 是否调价过了
                priceRate = Double.parseDouble(userDynamicPriceObj.toString());
            }else {
                // 没有调过
                //是否触发动态调价 redis获取
                Object dynamicPriceObj = redisService.getCacheObject("DYNAMIC_PRICE_" + openCityId);
                if(dynamicPriceObj!=null){
                    JSONObject jsonObject = JSONObject.parseObject(dynamicPricingJson);
                    // 触发动态调价
                    // value=时间
                    String timeStr = dynamicPriceObj.toString();
                    // 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率
                    R<Integer> r = xpOrderClient.getOrderNum(new GetOrderNumDto(timeStr,openCityId));
                    if(r.getData()==200){
                        int num = r.getData();
                        // 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_" + openCityId + "_" + uid, priceRate,time.longValue(), TimeUnit.MINUTES);
                        }
                    }
                }
            }
        }
        XpPricingCardVo xpPricingCardVo = new XpPricingCardVo();
        double amount = 0;
        double realDistance = distance / 1000;//实际公里
        double realTime = duration / 60;//实际时间
        double exceedDistance = (realDistance - rule.getDouble("num2")) < 0 ? 0 : realDistance - rule.getDouble("num2");//超出起步里程的公里
        double exceedTime = (realTime - rule.getDouble("num3")) < 0 ? 0 : realTime - rule.getDouble("num3");//超过起步分钟数的时间
        // 起步价
        Double startPrice = rule.getDouble("num1");
        // 超出公里数价格
        Double distancePrice = rule.getDouble("num4") * exceedDistance;
        // 超出时间价格
        Double durationPrice = rule.getDouble("num5") * exceedTime;
        startPrice = new BigDecimal(startPrice).multiply(BigDecimal.valueOf(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue();
        xpPricingCardVo.setStartPrice(startPrice);
        xpPricingCardVo.setDistancePrice(distancePrice);
        xpPricingCardVo.setDurationPrice(durationPrice);
        xpPricingCardVo.setTotalPrice(startPrice + distancePrice + durationPrice);
        // 里程
        xpPricingCardVo.setDistance(realDistance);
        xpPricingCardVo.setDuration(realTime);
        return xpPricingCardVo;
    }
    /**
     *  计算基础价格
     * @param rule 基础规则
     * @param distance 里程
xp-robotaxi-operation-boot/src/main/resources/bootstrap.yml
@@ -2,7 +2,6 @@
  port: 6060
  tomcat:
    basedir: /data/tomcat
# Spring
spring:
  application:
    # 应用名称
@@ -135,6 +134,7 @@
        shared-configs:
          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
---
spring:
  config:
    activate:
@@ -159,4 +159,96 @@
        shared-configs:
          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
---
#---
#spring:
#  shardingsphere:
#    props:
#      sql-show: true
#      sql-simple: true
#    datasource:
#      names: xp0
#      xp0:
#        type: com.alibaba.druid.pool.DruidDataSource
#        driver-class-name: com.mysql.cj.jdbc.Driver
#        url: jdbc:mysql://192.168.110.34:3306/xprt_operation?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
#        username: root
#        password: 123456
#    rules:
#      sharding:
#        tables:
#          sys_oper_log:
#            actual-data-nodes: xp0.sys_oper_log
#            database-strategy:
#              standard:
#                sharding-column: userId
#                sharding-algorithm-name: sys-oper-log-inline
#            key-generate-strategy:
#              column: oper_id
#              key-generator-name: snowflake
#          xp_agreement_app_user:
#            actual-data-nodes: xp0.xp_agreement_app_user
#            database-strategy:
#              standard:
#                sharding-column: app_user_id
#                sharding-algorithm-name: xp-agreement-app-user-inline
#            key-generate-strategy:
#              column: id
#              key-generator-name: snowflake
#          xp_point:
#            actual-data-nodes: xp0.xp_point
#            database-strategy:
#              standard:
#                sharding-column: id
#                sharding-algorithm-name: xp-point-inline
#            key-generate-strategy:
#              column: id
#              key-generator-name: snowflake
#        sharding-algorithms:
#          sys-oper-log-inline:
#            type: INLINE
#            props:
#              algorithm-expression: sys_oper_log
#          xp-agreement-app-user-inline:
#            type: INLINE
#            props:
#              algorithm-expression: xp_agreement_app_user
#          xp-point-inline:
#            type: INLINE
#            props:
#              algorithm-expression: xp_point
#        key-generators:
#          snowflake:
#            type: SNOWFLAKE
#    single-table:
#      tables:
#        - xp0.sys_config
#        - xp0.sys_dict_data
#        - xp0.sys_dict_type
#        - xp0.sys_logininfor
#        - xp0.sys_menu
#        - xp0.xp_acticity
#        - xp0.xp_acticity_coupon
#        - xp0.xp_agreement
#        - xp0.xp_banner
#        - xp0.xp_coupon
#        - xp0.xp_electronic_fence
#        - xp0.xp_electronic_fence_area
#        - xp0.xp_feedback_type
#        - xp0.xp_frequently_asked_questions
#        - xp0.xp_invitation_code
#        - xp0.xp_notification
#        - xp0.xp_open_city
#        - xp0.xp_pricing
#        - xp0.xp_pricing_item
#        - xp0.xp_region
#        - xp0.xp_ride_guide
#        - xp0.xp_route_plan
#        - xp0.xp_system_setting
#        - xp0.xp_trading_strategy
#        - xp0.xp_trading_strategy_city
#        - xp0.xp_user_feedback
#        - xp0.xp_whitelist
#        - xp0.xp_whitelist_operlog
xp-robotaxi-operation-boot/src/main/resources/i18n/messages.properties
@@ -17,3 +17,4 @@
pricing.not.exist=定价不存在
pricing.json.invalid=价格配置JSON格式不正确
pricing.rule.empty=价格规则不能为空
no.current.point=?????????
xp-robotaxi-operation-boot/src/main/resources/i18n/messages_en_US.properties
@@ -17,3 +17,4 @@
pricing.not.exist=Pricing does not exist
pricing.json.invalid=Invalid JSON format for pricing configuration
pricing.rule.empty=Price rule cannot be empty
no.current.point=The current location is not open for service
xp-robotaxi-operation-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -17,3 +17,4 @@
pricing.not.exist=定价不存在
pricing.json.invalid=价格配置JSON格式不正确
pricing.rule.empty=价格规则不能为空
no.current.point=?????????
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpPointMapper.xml
@@ -91,7 +91,7 @@
    <select id="queryPointByName" resultType="com.xiaopeng.api.vo.order.XpPointsListVo">
        select p.id,p.electronic_area_id as electronicAreaId,p.point_name as name,p.point_address as address,p.point_lon as longitude,p.point_lat as latitude
        select p.id,p.electronic_area_id as electronicAreaId,p.point_name as name,p.point_address as address,p.point_lon as longitude,p.point_lat as latitude,p.effective_period,p.effective_time_period
        from xp_electronic_fence_area xefa
        left join xp_point p on p.electronic_area_id = xefa.id
        where p.is_deleted = 0 and p.operation_status =2 and xefa.is_deleted = 0
xp-robotaxi-order-boot/pom.xml
@@ -106,17 +106,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/base/AbstractJob.java
@@ -3,6 +3,7 @@
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.order.service.IXpOrderService;
import com.xiaopeng.order.job.utils.SpringContextsUtil;
import com.xiaopeng.order.service.XpOrderTimeoutLogService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
@@ -17,10 +18,12 @@
    protected IXpOrderService xpOrderService;
    protected XpPushMessageClient xpPushMessageClient;
    protected XpOrderTimeoutLogService xpOrderTimeoutLogService;
    public AbstractJob(){
        this.xpOrderService = SpringContextsUtil.getBean(IXpOrderService.class);
        this.xpPushMessageClient = SpringContextsUtil.getBean(XpPushMessageClient.class);
        this.xpOrderTimeoutLogService = SpringContextsUtil.getBean(XpOrderTimeoutLogService.class);
    }
     
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/jobs/EndTripJob.java
@@ -1,23 +1,15 @@
package com.xiaopeng.order.job.jobs;
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
import com.xiaopeng.api.model.order.XpOrderTimeoutLog;
import com.xiaopeng.order.job.base.AbstractJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * 直播推送定时任务
 * 推送定时任务
 * @author Administrator
 *
 */
@@ -33,7 +25,14 @@
        Long orderId = maps.getLong("id");
        try {
            log.info("开始执行自动结束行程任务");
            // TODO 自动结束行程任务
            XpOrder xpOrder = xpOrderService.getById(orderId);
            xpOrderService.endTrip(xpOrder.getId(), String.valueOf(xpOrder.getAppUserId()));
            log.info("结束执行自动结束行程任务");
            // 添加超时记录
            XpOrderTimeoutLog xpOrderTimeoutLog = new XpOrderTimeoutLog();
            xpOrderTimeoutLog.setOrderId(xpOrder.getId());
            xpOrderTimeoutLog.setUserId(xpOrder.getAppUserId());
            xpOrderTimeoutLogService.save(xpOrderTimeoutLog);
        }catch(Exception e){
            e.printStackTrace();
        }
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/job/jobs/OrderCancelJob.java
@@ -3,6 +3,7 @@
import com.alibaba.fastjson2.JSONObject;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
import com.xiaopeng.order.job.base.AbstractJob;
@@ -35,23 +36,9 @@
            log.info("开始执行订单取消任务");
            XpOrder xpOrder = xpOrderService.getById(orderId);
            if(Objects.nonNull(xpOrder)){
                xpOrder.setStatus(OrderStatusEnum.CANCELED.getCode());
                xpOrderService.updateById(xpOrder);
                log.info("订单取消开始");
                xpOrderService.cancelOrder(String.valueOf(xpOrder.getId()));
                log.info("订单取消状态修改完成");
                // 消息推送
                Map<String, Object> params = new HashMap<>();
                Map<String, Object> content = new HashMap<>();
                content.put("orderId",xpOrder.getId());
                content.put("status",OrderStatusEnum.CANCELED.getCode());
                params.put("type", PushMessageTypeEnum.ORDER_STATUS.getDesc());
                params.put("content", JSONObject.toJSONString(content));
                params.put("timestamp", new Date().getTime()+"");
                String message = JSONObject.toJSONString(params);
                SendMessageDTO sendMessageDTO = new SendMessageDTO();
                sendMessageDTO.setMessage(message);
                sendMessageDTO.setAppUserId(String.valueOf(xpOrder.getAppUserId()));
                xpPushMessageClient.sendStatus(sendMessageDTO);
                log.info("订单取消消息推送完毕");
            }
        }catch(Exception e){
            e.printStackTrace();
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mapper/XpOrderTimeoutLogMapper.java
New file
@@ -0,0 +1,16 @@
package com.xiaopeng.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.model.order.XpOrderTimeoutLog;
/**
 * <p>
 * 订单超时表 Mapper 接口
 * </p>
 *
 * @author xiaochen
 * @since 2026-02-14
 */
public interface XpOrderTimeoutLogMapper extends BaseMapper<XpOrderTimeoutLog> {
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/XpOrderTimeoutLogService.java
New file
@@ -0,0 +1,16 @@
package com.xiaopeng.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.model.order.XpOrderTimeoutLog;
/**
 * <p>
 * 订单超时表 服务类
 * </p>
 *
 * @author xiaochen
 * @since 2026-02-14
 */
public interface XpOrderTimeoutLogService extends IService<XpOrderTimeoutLog> {
}
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderTimeoutLogServiceImpl.java
New file
@@ -0,0 +1,20 @@
package com.xiaopeng.order.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.model.order.XpOrderTimeoutLog;
import com.xiaopeng.order.mapper.XpOrderTimeoutLogMapper;
import com.xiaopeng.order.service.XpOrderTimeoutLogService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 订单超时表 服务实现类
 * </p>
 *
 * @author xiaochen
 * @since 2026-02-14
 */
@Service
public class XpOrderTimeoutLogServiceImpl extends ServiceImpl<XpOrderTimeoutLogMapper, XpOrderTimeoutLog> implements XpOrderTimeoutLogService {
}
xp-robotaxi-push-message-boot/pom.xml
@@ -97,17 +97,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
    </dependencies>
    <build>
xp-robotaxi-vehicle-convoy-boot/pom.xml
@@ -97,17 +97,17 @@
            <artifactId>unified-verification</artifactId>
            <version>1.5.2-v2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.0.0</version>
        </dependency>
        <!-- 使用 BASE 事务时,需要引入此模块 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>
            <version>5.0.0</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 使用 BASE 事务时,需要引入此模块 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.shardingsphere</groupId>-->
<!--            <artifactId>shardingsphere-transaction-base-seata-at</artifactId>-->
<!--            <version>5.0.0</version>-->
<!--        </dependency>-->
        <!-- easypoi -->
        <dependency>
            <groupId>cn.afterturn</groupId>
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java
@@ -1,6 +1,8 @@
package com.xiaopeng.vehicleConvoy.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.api.model.vehicleConvoy.XpFleetOperations;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.vehicleConvoy.model.dto.FleetAddDTO;
@@ -9,6 +11,7 @@
import com.xiaopeng.vehicleConvoy.model.query.FleetUserPageQuery;
import com.xiaopeng.vehicleConvoy.model.query.FleetVehiclePageQuery;
import com.xiaopeng.vehicleConvoy.model.vo.*;
import com.xiaopeng.vehicleConvoy.service.IXpFleetOperationsService;
import com.xiaopeng.vehicleConvoy.service.IXpFleetService;
import io.swagger.annotations.*;
import lombok.RequiredArgsConstructor;
@@ -19,6 +22,7 @@
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
 * <p>
@@ -34,6 +38,7 @@
public class XpFleetController {
    private final IXpFleetService xpFleetService;
    private final IXpFleetOperationsService xpFleetOperationsService;
    /**
     * 根据ID列表批量查询车队信息(内部接口)
@@ -49,6 +54,28 @@
        }
        return R.ok(xpFleetService.listByIds(ids));
    }
    /**
     * 通过运营人员id查询车队信息
     *
     * @param operationsUserId 运营人员id
     * @return 车队信息
     */
    @ApiOperation(value = "通过运维人员id查询车队信息", tags = {"内部接口"})
    @GetMapping("/inner/v1/fleet/getFleetByOperationsUserId")
    public R<XpFleet> getFleetByOperationsUserId(@RequestParam Long operationsUserId) {
        if (Objects.isNull(operationsUserId)) {
            return R.ok();
        }
        // 查询车队人员关联关系
        XpFleetOperations fleetOperations = xpFleetOperationsService.getOne(Wrappers.lambdaQuery(XpFleetOperations.class)
                .eq(XpFleetOperations::getOperationsUserId, operationsUserId)
                .last("LIMIT 1"));
        if (Objects.isNull(fleetOperations)) {
            return R.ok();
        }
        XpFleet xpFleet = xpFleetService.getById(fleetOperations.getFleetId());
        return R.ok(xpFleet);
    }
    /**
     * 添加车队