xuhy
2026-03-11 ef32f6913325bb593d537b4ff433545f73ecb79a
Merge remote-tracking branch 'origin/master'

# Conflicts:
# xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
63个文件已删除
1个文件已添加
34个文件已修改
2709 ■■■■■ 已修改文件
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/constant/I18nKeyConstant.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserCouponController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/model/vo/XpAppUserCouponListVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysRoleServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/resources/i18n/messages.properties 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/resources/i18n/messages_en_US.properties 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/resources/i18n/messages_zh_CN.properties 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/maintenance/XpCheckLocationClientFallbackFactory.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/maintenance/XpCheckLocationClient.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/GeoPoint.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/ResponseMessage.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/StatusCode.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/NavigationWaypoint.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/RTTask.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/TaskStatus.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/TaskType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/WaypointAction.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/WaypointType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/ActionType.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/OrderOperation.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/OrderOperationType.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/OrderType.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/PointType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/TargetPoint.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/Waypoint.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudPushNavigation/RestoreNavigationData.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/ResponseMessage.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/StatusCode.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/NavigationDeviationMessage.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/ResponseMessage.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/RtEventType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/StatusCode.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/GeoPoint.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NaviPosition.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NaviProgressInfo.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NaviStatus.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NavigationInfo.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NavigationInfoUpdateEvent.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/OrderPhase.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/ResponseMessage.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/RtEventType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/StatusCode.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/Waypoint.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/GeoPoint.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderPhase.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStateChangeSource.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStatus.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStatusInfo.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStatusInfoEvent.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/ResponseMessage.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/RtEventType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/RtTripInfo.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/RtTripInfoEvent.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/StatusCode.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/Waypoint.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NaviPosition.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NaviProgressInfo.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NaviStatus.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NavigationInfo.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderPhase.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderStateChangeSource.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderStatus.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderStatusInfo.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/RealtimePose.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/ResponseMessage.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/RtTripInfo.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/StatusCode.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/Wgs.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/GeoPoint.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/OrderPhase.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/RealtimePose.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/Wgs.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/StringUtils.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/utils/map/FastDistanceCalculator.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_en_US.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_zh_CN.properties 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpCheckLocationController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpVehicleOperationsController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpVehicleRepairController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/exportExcel/XpTaskExportExcel.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/schedule/VehicleChargingScheduleTask.java 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpSchedulingDetailServiceImpl.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/resources/mapper/maintenance/XpClockInMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-maintenance-boot/src/main/resources/mapper/maintenance/XpSchedulingDetailMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/OssController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetPageVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/constant/I18nKeyConstant.java
@@ -18,6 +18,8 @@
    String LOGIN_USERNAME_NOT_VALID = "login.username.not.valid";
    String LOGIN_PASSWORD_NOT_VALID = "login.password.not.valid";
    String LOGIN_BLACK_LIST = "login.black.list";
    String ACCOUNT_NOT_EXISTS = "account.not.exists";
    String LOGIN_ACCOUNT_DISABLED = "login.account.disabled";
    //========== 帐号相关 ==========
    String ACCOUNT_PROFILE_UPDATE_ERROR = "account.profile.update.error";
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserCouponController.java
@@ -16,8 +16,10 @@
import com.xiaopeng.api.dto.account.UserResourcesDto;
import com.xiaopeng.api.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.account.XpAppUserClient;
import com.xiaopeng.api.feignClient.operation.XpCouponClient;
import com.xiaopeng.api.feignClient.operation.XpOpenCityClient;
import com.xiaopeng.api.model.account.XpAppUserCoupon;
import com.xiaopeng.api.model.operation.XpCoupon;
import com.xiaopeng.api.vo.account.UserCouponVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.unifiedverification.interceptor.XpClientCheck;
@@ -48,12 +50,14 @@
    private final IXpAppUserCouponService xpAppUserCouponService;
    private final XpOpenCityClient xpOpenCityClient;
    private final UserResourcesClient userResourcesClient;
    private final XpCouponClient xpCouponClient;
    public XpAppUserCouponController(IXpAppUserCouponService xpAppUserCouponService, XpOpenCityClient xpOpenCityClient, UserResourcesClient userResourcesClient) {
    public XpAppUserCouponController(IXpAppUserCouponService xpAppUserCouponService, XpOpenCityClient xpOpenCityClient, UserResourcesClient userResourcesClient, XpCouponClient xpCouponClient) {
        this.xpAppUserCouponService = xpAppUserCouponService;
        this.xpOpenCityClient = xpOpenCityClient;
        this.userResourcesClient = userResourcesClient;
        this.xpCouponClient = xpCouponClient;
    }
@@ -71,7 +75,7 @@
        UserCouponVo userCouponVo = new UserCouponVo();
        // 获取用户所有优惠券
        List<XpAppUserCoupon> couponList = xpAppUserCouponService.list(new LambdaQueryWrapper<XpAppUserCoupon>().eq(XpAppUserCoupon::getStatus, 1).eq(XpAppUserCoupon::getAppUserId,dto.getUserId()).apply("FIND_IN_SET({0}, open_city_id)", dto.getOpenCityId()) );
        List<XpAppUserCoupon> couponList = xpAppUserCouponService.list(new LambdaQueryWrapper<XpAppUserCoupon>().eq(XpAppUserCoupon::getStatus, 1).eq(XpAppUserCoupon::getAppUserId,dto.getUserId()).apply("(FIND_IN_SET({0}, open_city_id) or open_city_id='0')", dto.getOpenCityId()) );
        if(couponList.isEmpty()){
            return R.ok(userCouponVo);
        }
@@ -175,6 +179,11 @@
                    vo.setApplicableCities(r.getData());
                }
            }
            R<XpCoupon> coupon1 = xpCouponClient.getCouponById(coupon.getCouponId());
            if(coupon1!=null){
                XpCoupon xpCoupon = coupon1.getData();
                vo.setCouponDesc(xpCoupon.getCouponDescription());
            }
            return R.ok(vo);
        }
        return R.ok(null);
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/model/vo/XpAppUserCouponListVo.java
@@ -42,4 +42,7 @@
    @ApiModelProperty(value = "时段规则")
    private String timeRule;
    @ApiModelProperty(value = "优惠券说明")
    private String couponDesc;
}
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java
@@ -61,7 +61,7 @@
        // 查询用户信息
        SysUser user = userService.selectUserByUserName(username);
        if (StringUtils.isNull(user)) {
            throw new ServiceException("登录失败,请稍后重试", I18nKeyConstant.LOGIN_FAIL);
            throw new ServiceException("帐号不存在或已被删除", I18nKeyConstant.ACCOUNT_NOT_EXISTS);
        }
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
@@ -105,9 +105,9 @@
            recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_FAIL_STATUS, "对不起,您的账号已被删除");
            throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
        }
        if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
        if (Integer.valueOf("1").equals(user.getStatus())) {
            recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_FAIL_STATUS, "用户已停用,请联系管理员");
            throw new ServiceException("对不起,您的账号:" + username + " 已停用");
            throw new ServiceException("您的账号:" + username + " 已禁用", I18nKeyConstant.LOGIN_ACCOUNT_DISABLED);
        }
        passwordService.validate(user, password, request);
        // recordLogService.recordLogininfor(request, user.getId().intValue(), username, Constants.LOGIN_SUCCESS_STATUS, "登录成功");
xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/SysRoleServiceImpl.java
@@ -209,7 +209,9 @@
        // 构建查询条件
        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(query.getRoleName())) {
            wrapper.like(SysRole::getRoleName, query.getRoleName());
            // 转义 LIKE 特殊字符:% _ \
            String roleName = StringUtils.escapeLike(query.getRoleName());
            wrapper.like(SysRole::getRoleName, roleName);
        }
        wrapper.orderByDesc(SysRole::getCreateTime);
xp-robotaxi-account-boot/src/main/resources/i18n/messages.properties
@@ -7,6 +7,8 @@
login.username.not.valid=用户名不在指定范围
login.password.not.valid=用户密码不在指定范围
login.black.list=很遗憾,访问IP已被列入系统黑名单
account.not.exists=帐号不存在或已被删除
login.account.disabled=您的账号:{0}已禁用
account.profile.update.error=修改个人信息异常,请联系管理员
account.password.update.error=修改密码异常,请联系管理员
account.phone.duplicate=手机号已开通账号
@@ -24,7 +26,7 @@
validation.group.name.notBlank=组名称不能为空
validation.group.remarks.notBlank=备注不能为空
validation.group.type.notNull=分组类型不能为空
validation.group.menuIds.notEmpty=菜单id集合不能为空
validation.group.menuIds.notEmpty=请选择操作权限
validation.group.id.notNull=权限组ID不能为空
group.name.exists=权限组名称已存在
group.not.exists=权限组不存在
xp-robotaxi-account-boot/src/main/resources/i18n/messages_en_US.properties
@@ -7,6 +7,8 @@
login.username.not.valid=The username is not within the specified range
login.password.not.valid=The user password is not within the specified range
login.black.list=Regrettably, the access IP has been blacklisted by the system
account.not.exists=The account does not exist or has been deleted
login.account.disabled=Your account: {0} has been disabled
account.profile.update.error=If there is an abnormality in modifying personal information, please contact the administrator
account.password.update.error=Password modification is abnormal. Please contact the administrator
account.phone.duplicate=The mobile phone number has been activated for an account
@@ -24,7 +26,7 @@
validation.group.name.notBlank=Group name cannot be empty
validation.group.remarks.notBlank=Remarks cannot be empty
validation.group.type.notNull=Group type cannot be null
validation.group.menuIds.notEmpty=Menu IDs cannot be empty
validation.group.menuIds.notEmpty=Please select the operation permission
validation.group.id.notNull=Group permission ID cannot be null
group.name.exists=The group name already exists
group.not.exists=Group is not exist
xp-robotaxi-account-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -7,6 +7,8 @@
login.username.not.valid=用户名不在指定范围
login.password.not.valid=用户密码不在指定范围
login.black.list=很遗憾,访问IP已被列入系统黑名单
account.not.exists=帐号不存在或已被删除
login.account.disabled=您的账号:{0}已禁用
account.profile.update.error=修改个人信息异常,请联系管理员
account.password.update.error=修改密码异常,请联系管理员
account.phone.duplicate=手机号已开通账号
@@ -24,7 +26,7 @@
validation.group.name.notBlank=组名称不能为空
validation.group.remarks.notBlank=备注不能为空
validation.group.type.notNull=分组类型不能为空
validation.group.menuIds.notEmpty=菜单id集合不能为空
validation.group.menuIds.notEmpty=请选择操作权限
validation.group.id.notNull=权限组ID不能为空
group.name.exists=权限组名称已存在
group.not.exists=权限组不存在
xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/maintenance/XpCheckLocationClientFallbackFactory.java
@@ -7,6 +7,8 @@
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * 打卡地址Feign客户端降级工厂
 *
@@ -26,6 +28,12 @@
                log.error("远程调用获取检查点位信息异常:{}", cause.getMessage());
                return R.fail("远程调用获取检查点位信息异常");
            }
            @Override
            public R<List<XpCheckLocation>> listByIds(List<Long> ids) {
                log.error("远程调用批量获取检查点位信息异常:{}", cause.getMessage());
                return R.fail("远程调用批量获取检查点位信息异常");
            }
        };
    }
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/maintenance/XpCheckLocationClient.java
@@ -6,7 +6,11 @@
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
 * 打卡地址管理 Feign Client
@@ -21,4 +25,10 @@
     */
    @GetMapping("/inner/check-location/queryById")
    R<XpCheckLocation> queryById(@RequestParam Long id);
    /**
     * 根据ID列表批量查询打卡地址
     */
    @PostMapping("/inner/check-location/listByIds")
    R<List<XpCheckLocation>> listByIds(@RequestBody List<Long> ids);
}
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/GeoPoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/ResponseMessage.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/StatusCode.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/NavigationWaypoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/RTTask.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/TaskStatus.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/TaskType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/WaypointAction.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudCreateOrder/WaypointType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/ActionType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/OrderOperation.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/OrderOperationType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/OrderType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/PointType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/TargetPoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudOrderOperation/Waypoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/down/cloudPushNavigation/RestoreNavigationData.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/ResponseMessage.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/StatusCode.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/NavigationDeviationMessage.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/ResponseMessage.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/RtEventType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationDeviationEvent/StatusCode.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/GeoPoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NaviPosition.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NaviProgressInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NaviStatus.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NavigationInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/NavigationInfoUpdateEvent.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/OrderPhase.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/ResponseMessage.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/RtEventType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/StatusCode.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/navigationUpdateEvent/Waypoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/GeoPoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderPhase.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStateChangeSource.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStatus.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStatusInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/OrderStatusInfoEvent.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/ResponseMessage.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/RtEventType.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/RtTripInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/RtTripInfoEvent.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/StatusCode.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/orderStateUpdateEvent/Waypoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NaviPosition.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NaviProgressInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NaviStatus.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/NavigationInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderPhase.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderStateChangeSource.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderStatus.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/OrderStatusInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/RealtimePose.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/ResponseMessage.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/RtTripInfo.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/StatusCode.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/systemStatusXpu/Wgs.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/GeoPoint.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/OrderPhase.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/RealtimePose.java
File was deleted
xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/proto/up/xpuNavigationRequest/Wgs.java
File was deleted
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -19,9 +19,6 @@
import com.xiaopeng.api.model.order.XpOrder;import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.SystemEventType;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.RTTask;
import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskStatus;
import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskType;
import com.xiaopeng.api.vo.automaticPilot.protobuf.*;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.api.vo.order.OrderTrajectoryVo;
@@ -66,9 +63,10 @@
    @PostMapping("/inner/pushOrder")
    public R<Boolean> pushOrder(@RequestBody PushOrderDto dto) {
        RtTask.Builder taskBuilder = RtTask.newBuilder();
        SnowFlake snowFlake = new SnowFlake(0, 0);
        snowFlake.withStartTimestamp(1420041600000L);
        taskBuilder.setOrderId(dto.getOrderId().toString());
        taskBuilder.setTaskId("MISSION_"+ IdUtil.fastUUID());
        taskBuilder.setTaskId(snowFlake.nextId() + "");
        if(dto.getOrderId().contains("XU")){
            taskBuilder.setTaskType(OrderType.ORDER_TYPE_CRUISE);
@@ -102,6 +100,7 @@
            pointBuilderEnd.setPoint(geoPointBuilder.build());
            taskBuilder.addPoints(pointBuilderEnd);
            taskBuilder.setSequence(1);
            taskBuilder.addOrderPhase(OrderPhase.ROUTE_TO_CRUISE);
        }else{
            //用户订单
            if(dto.getStage() == 1){
@@ -127,6 +126,7 @@
                pointBuilderEnd.setPoint(geoPointBuilder.build());
                taskBuilder.addPoints(pointBuilderEnd);
                taskBuilder.setSequence(1);
                taskBuilder.addOrderPhase(OrderPhase.ROUTE_TO_PICKUP);
            }else{
                //构建上车点位置信息
                pointBuilderStart.setWaypointId(dto.getOrderId() + "-SD-001");
@@ -150,13 +150,12 @@
                pointBuilderEnd.setPoint(geoPointBuilder.build());
                taskBuilder.addPoints(pointBuilderEnd);
                taskBuilder.setSequence(2);
                taskBuilder.addOrderPhase(OrderPhase.ROUTE_TO_DESTINATION);
            }
        }
        RtTaskList.Builder taskListBuilder = RtTaskList.newBuilder();
        taskListBuilder.addTasks(taskBuilder.build());
        SnowFlake snowFlake = new SnowFlake(0, 0);
        snowFlake.withStartTimestamp(1420041600000L);
        long sequence = snowFlake.nextId();
        MqttPublishDto mqttPublishDto = xpuUtil.cloudCreateOrder(taskListBuilder.build(), dto.getVin(),sequence);
        //先将车辆修改状态,防止在异步通知之前被其他订单选中
xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/KafkaConsumerService.java
@@ -298,7 +298,9 @@
        long sequence = msgContent.getHeader().getSequence();
        responseMessage(sequence, vin);
        //保存车辆实时位置到redis
        redisTemplate.opsForGeo().add("vehiclePosition:" + vin, new Point(longitude, latitude), vin);
        if(0 != longitude && 0 != latitude){
            redisTemplate.opsForGeo().add("vehiclePosition:" + vin, new Point(longitude, latitude), vin);
        }
        VehicleStatus vehicleStatus = systemRuntimeStatus.getVehicleStatus();
        OrderStatusInfo runningOrderStatusInfo = systemRuntimeStatus.getRunningOrderStatusInfo();
@@ -316,44 +318,48 @@
            }
        }
        //保存车辆实时信息到mongodb
        VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
        if (null == vehiclePosition) {
            vehiclePosition = new VehiclePosition();
            vehiclePosition.setVin(vin);
            vehiclePosition.setPoint(new GeoJsonPoint(longitude, latitude));
            vehiclePosition.setCourse(course);
            vehiclePosition.setAlt(alt);
            vehiclePosition.setSpeed(dynamicStatus.getSpeed());
            vehiclePosition.setCurrentElectricity(dynamicStatus.getBmsSocDisp());
            vehiclePosition.setMileageRange(dynamicStatus.getDstBatDisp());
            vehiclePosition.setOrderId(orderId);
            mongoTemplate.save(vehiclePosition);
        }else{
            vehiclePosition.setOrderId(orderId);
            Update update = new Update()
                    .set("point", new GeoJsonPoint(longitude, latitude))
                    .set("course", course)
                    .set("alt", alt)
                    .set("speed", dynamicStatus.getSpeed())
                    .set("currentElectricity", dynamicStatus.getBmsSocDisp())
                    .set("mileageRange", dynamicStatus.getDstBatDisp())
                    .set("hasOrder", hasOrder)
                    .set("orderId", orderId);
            mongoTemplate.updateFirst(new Query(Criteria.where("vin").is(vin)), update, VehiclePosition.class);
        }
        //调用推送服务接口
        if (null != orderId) {
            RtTripInfo rtTripInfo = systemRuntimeStatus.getRtTripInfo();
            PushOrderRouteDTO dto = new PushOrderRouteDTO();
            dto.setOrderId(orderId);
            dto.setRouteType(order_id.contains("XU") ? 2 : 1);
            dto.setLongitude(longitude);
            dto.setLatitude(latitude);
            //TODO 这两个字段现在拿不到
        if(0 != longitude && 0 != latitude){
            VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
            if (null == vehiclePosition) {
                vehiclePosition = new VehiclePosition();
                vehiclePosition.setVin(vin);
                vehiclePosition.setPoint(new GeoJsonPoint(longitude, latitude));
                vehiclePosition.setCourse(course);
                vehiclePosition.setAlt(alt);
                vehiclePosition.setSpeed(dynamicStatus.getSpeed());
                vehiclePosition.setCurrentElectricity(dynamicStatus.getBmsSocDisp());
                vehiclePosition.setMileageRange(dynamicStatus.getDstBatDisp());
                vehiclePosition.setOrderId(orderId);
                mongoTemplate.save(vehiclePosition);
            }else{
                vehiclePosition.setOrderId(orderId);
                Update update = new Update()
                        .set("point", new GeoJsonPoint(longitude, latitude))
                        .set("course", course)
                        .set("alt", alt)
                        .set("speed", dynamicStatus.getSpeed())
                        .set("currentElectricity", dynamicStatus.getBmsSocDisp())
                        .set("mileageRange", dynamicStatus.getDstBatDisp())
                        .set("hasOrder", hasOrder)
                        .set("orderId", orderId);
                mongoTemplate.updateFirst(new Query(Criteria.where("vin").is(vin)), update, VehiclePosition.class);
            }
            //调用推送服务接口
            if (null != orderId) {
                RtTripInfo rtTripInfo = systemRuntimeStatus.getRtTripInfo();
                PushOrderRouteDTO dto = new PushOrderRouteDTO();
                dto.setOrderId(orderId);
                dto.setRouteType(order_id.contains("XU") ? 2 : 1);
                dto.setLongitude(longitude);
                dto.setLatitude(latitude);
                //TODO 这两个字段现在拿不到
//            dto.setDuration(rtTripInfo.getRemaining_time());
//            dto.setDistance(rtTripInfo.getRemaining_distance().intValue());
            xpPushMessageClient.pushOrderRoute(dto);
                xpPushMessageClient.pushOrderRoute(dto);
            }
        }
    }
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/StringUtils.java
@@ -478,4 +478,16 @@
        }
        return sb.toString();
    }
    /**
     * 对字符串进行转义,转义字符为\,被转义的字符包括\、%、_。例如:输入abc%def_ghi\jkl,输出abc\%def\_ghi\\jkl
     * @param value
     * @return
     */
    public static String escapeLike(String value) {
        if (value == null) return null;
        return value.replace("\\", "\\\\")
                .replace("%", "\\%")
                .replace("_", "\\_");
    }
}
xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/utils/map/FastDistanceCalculator.java
@@ -11,9 +11,15 @@
     */
    public static double distanceFast(double lat1, double lon1, 
                                      double lat2, double lon2) {
        double x = (lon2 - lon1) * DEG_TO_RAD * Math.cos((lat1 + lat2) * 0.5 * DEG_TO_RAD);
        double y = (lat2 - lat1) * DEG_TO_RAD;
        return R * Math.sqrt(x * x + y * y)*1000;
        try {
            double x = (lon2 - lon1) * DEG_TO_RAD * Math.cos((lat1 + lat2) * 0.5 * DEG_TO_RAD);
            double y = (lat2 - lat1) * DEG_TO_RAD;
            return R * Math.sqrt(x * x + y * y)*1000;
        }catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }
    /**
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages.properties
@@ -1,5 +1,6 @@
# 中文
city.not.found=获取城市信息失败
no.current.point=该站点已停止运营
no.current.region=当前区域未开通服务
path.not.found=获取导航信息失败
to.work=当前的接送点距离过近。建议步行前往
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_en_US.properties
@@ -1,5 +1,6 @@
# English (US)
city.not.found=city.not.found
no.current.point=This site has ceased operations
no.current.region=no.current.region
path.not.found=Failed to obtain navigation information
to.work=\ The current pick-up and drop-off points are too close to each other. It is recommended to walk there
xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -1,5 +1,6 @@
# 中文
city.not.found=获取城市信息失败
no.current.point=该站点已停止运营
no.current.region=当前区域未开通服务
path.not.found=获取导航信息失败
to.work=当前的接送点距离过近。建议步行前往
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpCheckLocationController.java
@@ -1,5 +1,6 @@
package com.xiaopeng.maintenance.controller;
import cn.hutool.core.collection.CollUtil;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.maintenance.XpCheckLocation;
@@ -16,6 +17,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
@@ -43,6 +45,15 @@
        return R.ok(xpCheckLocationService.getById(id));
    }
    @PostMapping("/inner/check-location/listByIds")
    @ApiOperation(value = "根据ID列表批量查询打卡地址", tags = {"内部接口"})
    public R<List<XpCheckLocation>> listByIds(@RequestBody List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return R.ok(new ArrayList<>());
        }
        return R.ok(xpCheckLocationService.listByIds(ids));
    }
    @ApiOperation(value = "根据ID查询打卡地址", tags = {"运维APP-排班与考勤"})
    @GetMapping("/api/v1/check-location/queryByUserId")
    public R<XpCheckLocation> queryByUserId() {
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpVehicleOperationsController.java
@@ -116,17 +116,17 @@
            xpCruiseOrder.setLastOrderId(r.getData());
            // 找到收车站点
            R<XpCruiseOrder> returnCruisePoint = xpCruisePlanClient.findReturnCruisePoint(xpCruiseOrder);
            XpCruiseOrder returnCruisePoint = xpCruisePlanClient.findReturnCruisePoint(xpCruiseOrder).getData();
            if(returnCruisePoint!=null){
                xpCruiseOrderClient.addCruiseOrder(returnCruisePoint.getData());
                xpCruiseOrderClient.addCruiseOrder(returnCruisePoint);
                // 下发命令
                PushOrderDto pushOrderDto = new PushOrderDto();
                pushOrderDto.setVin(returnCruisePoint.getData().getVehicleVin());
                pushOrderDto.setOrderId(returnCruisePoint.getData().getId());
                pushOrderDto.setStartLatitude(returnCruisePoint.getData().getStartLat());
                pushOrderDto.setStartLongitude(returnCruisePoint.getData().getStartLon());
                pushOrderDto.setEndLatitude(returnCruisePoint.getData().getEndLat());
                pushOrderDto.setEndLongitude(returnCruisePoint.getData().getEndLon());
                pushOrderDto.setVin(returnCruisePoint.getVehicleVin());
                pushOrderDto.setOrderId(returnCruisePoint.getId());
                pushOrderDto.setStartLatitude(returnCruisePoint.getStartLat());
                pushOrderDto.setStartLongitude(returnCruisePoint.getStartLon());
                pushOrderDto.setEndLatitude(returnCruisePoint.getEndLat());
                pushOrderDto.setEndLongitude(returnCruisePoint.getEndLon());
                pushOrderDto.setOrderType(OrderType.CRUISE_ORDER);
                pushOrderDto.setStage(2);
                orderMissionClient.pushOrder(pushOrderDto);
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpVehicleRepairController.java
@@ -9,6 +9,7 @@
import com.xiaopeng.maintenance.dto.XpVehicleRepairStartDTO;
import com.xiaopeng.maintenance.model.query.XpVehicleRepairPageQuery;
import com.xiaopeng.maintenance.model.vo.XpVehicleRepairVO;
import com.xiaopeng.maintenance.schedule.InsuranceInspectionScheduleTask;
import com.xiaopeng.maintenance.service.IXpVehicleRepairService;
import com.xiaopeng.maintenance.vo.RepairStatusVO;
import io.swagger.annotations.Api;
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/exportExcel/XpTaskExportExcel.java
@@ -22,7 +22,7 @@
    @Excel(name = "序号",width = 20)
    private Integer number;
    @Excel(name = "车牌号",width = 20)
    @Excel(name = "车牌",width = 20)
    private String plateNumber;
    @ApiModelProperty(value = "任务类别1常规 2事故")
    @Excel(name = "任务类别",width = 20)
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/schedule/VehicleChargingScheduleTask.java
New file
@@ -0,0 +1,269 @@
package com.xiaopeng.maintenance.schedule;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
import com.xiaopeng.api.model.account.XpOperationsRole;
import com.xiaopeng.api.model.maintenance.XpTask;
import com.xiaopeng.api.model.maintenance.XpTaskRule;
import com.xiaopeng.api.model.maintenance.XpTaskRuleRole;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.maintenance.constant.TaskScheduleConstants;
import com.xiaopeng.maintenance.mapper.XpTaskMapper;
import com.xiaopeng.maintenance.mapper.XpTaskRuleMapper;
import com.xiaopeng.maintenance.mapper.XpTaskRuleRoleMapper;
import com.xiaopeng.maintenance.utils.UUIDUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 运营车辆电量检测定时任务
 * <p>
 * 根据任务规则配置的充电阈值,从MongoDB读取低电量车辆位置数据,
 * 创建充电类型任务并下发给配置角色对应的运维人员。
 * </p>
 *
 * @author mitao
 * @since 2026-03-10
 */
@Component
public class VehicleChargingScheduleTask {
    private static final Logger log = LoggerFactory.getLogger(VehicleChargingScheduleTask.class);
    /**
     * 任务名称
     */
    private static final String TASK_NAME = "运营车辆电量检测定时任务";
    @Autowired
    private XpTaskRuleMapper taskRuleMapper;
    @Autowired
    private XpTaskRuleRoleMapper taskRuleRoleMapper;
    @Autowired
    private XpTaskMapper taskMapper;
    @Autowired
    private XpVehicleClient xpVehicleClient;
    @Autowired
    private XpOperationUserClient xpOperationUserClient;
    @Autowired
    private MongoTemplate mongoTemplate;
    /**
     * 每分钟执行一次电量检测
     */
    @Scheduled(cron = "0 * * * * ?")
    public void executeChargingDetectionTask() {
        log.info("========== 开始执行{}检查 ==========", TASK_NAME);
        // 1. 获取任务规则配置
        XpTaskRule taskRule = getTaskRule();
        if (taskRule == null) {
            log.info("未找到任务规则配置,跳过执行");
            return;
        }
        // 2. 判断充电策略功能是否启用
        if (taskRule.getChargingStrategy() == null || taskRule.getChargingStrategy() != 1) {
            log.info("充电策略未启用,跳过执行");
            return;
        }
        // 3. 获取电量阈值
        BigDecimal thresholdDecimal = taskRule.getChargingStrategyBatteryl();
        if (thresholdDecimal == null) {
            log.warn("充电策略电量阈值未配置,跳过执行");
            return;
        }
        int threshold = thresholdDecimal.intValue();
        if (threshold <= 0) {
            log.warn("充电策略电量阈值不合法:{},跳过执行", thresholdDecimal);
            return;
        }
        // 4. 获取任务接收角色的运维人员
        List<Long> operationUserIds = getOperationUserIdsByRoleType(TaskScheduleConstants.RULE_ROLE_TYPE_CHARGING);
        if (CollectionUtils.isEmpty(operationUserIds)) {
            log.warn("未找到充电策略任务接收角色的运维人员");
            return;
        }
        String belongUserIds = operationUserIds.stream().map(String::valueOf).collect(Collectors.joining(","));
        // 5. 获取所有运营中的车辆,并构建 VIN -> Vehicle 映射
        List<XpVehicle> operatingVehicles = getOperatingVehicles();
        if (CollectionUtils.isEmpty(operatingVehicles)) {
            log.info("没有运营中的车辆,跳过执行");
            return;
        }
        Map<String, XpVehicle> operatingVehicleByVin = operatingVehicles.stream()
                .filter(v -> StringUtils.hasLength(v.getVehicleVin()))
                .collect(Collectors.toMap(XpVehicle::getVehicleVin, v -> v, (a, b) -> a));
        if (operatingVehicleByVin.isEmpty()) {
            log.info("运营车辆VIN为空,跳过执行");
            return;
        }
        // 6. 从MongoDB查询电量低于阈值的车辆位置数据(只查运营车辆VIN)
        List<String> vins = new ArrayList<>(operatingVehicleByVin.keySet());
        Query query = new Query(Criteria.where("vin").in(vins).and("currentElectricity").lt(threshold));
        List<VehiclePosition> lowBatteryPositions = mongoTemplate.find(query, VehiclePosition.class);
        if (CollectionUtils.isEmpty(lowBatteryPositions)) {
            log.info("未找到低电量车辆(阈值={}),跳过执行", threshold);
            return;
        }
        int createdCount = 0;
        int skippedCount = 0;
        for (VehiclePosition position : lowBatteryPositions) {
            if (position == null || !StringUtils.hasLength(position.getVin())) {
                continue;
            }
            XpVehicle vehicle = operatingVehicleByVin.get(position.getVin());
            if (vehicle == null || vehicle.getId() == null) {
                continue;
            }
            // 避免重复创建:若存在未完成的充电任务则跳过
            if (hasUnfinishedTask(vehicle.getId(), TaskScheduleConstants.TASK_TYPE_CHARGING)) {
                skippedCount++;
                continue;
            }
            XpTask task = buildChargingTask(vehicle, position, belongUserIds, taskRule, threshold);
            taskMapper.insert(task);
            createdCount++;
        }
        log.info("========== {}执行完成,阈值={},命中={},创建任务数={},跳过(已存在未完成任务)={} ==========",
                TASK_NAME, threshold, lowBatteryPositions.size(), createdCount, skippedCount);
    }
    private XpTaskRule getTaskRule() {
        return taskRuleMapper.selectOne(new LambdaQueryWrapper<XpTaskRule>().last("limit 1"));
    }
    /**
     * 获取所有运营中的车辆
     */
    private List<XpVehicle> getOperatingVehicles() {
        try {
            R<List<XpVehicle>> result = xpVehicleClient.listVehicle();
            if (result != null && result.getData() != null) {
                return result.getData().stream()
                        .filter(v -> v.getVehicleType() != null && v.getVehicleType() == 2)
                        .filter(v -> v.getOperatingStatus() != null && v.getOperatingStatus() == 1)
                        .collect(Collectors.toList());
            }
        } catch (Exception e) {
            log.error("获取车辆列表失败", e);
        }
        return Collections.emptyList();
    }
    /**
     * 根据角色类型获取运维人员ID列表
     */
    private List<Long> getOperationUserIdsByRoleType(Integer roleType) {
        List<XpTaskRuleRole> ruleRoles = taskRuleRoleMapper.selectList(
                new LambdaQueryWrapper<XpTaskRuleRole>()
                        .eq(XpTaskRuleRole::getRuleType, roleType)
        );
        if (CollectionUtils.isEmpty(ruleRoles)) {
            return Collections.emptyList();
        }
        Set<Long> userIds = new HashSet<>();
        for (XpTaskRuleRole ruleRole : ruleRoles) {
            try {
                R<List<XpOperationsRole>> result = xpOperationUserClient.getXpOperationsRoleByRoleId(ruleRole.getRoleId());
                if (result != null && result.getData() != null) {
                    userIds.addAll(
                            result.getData().stream()
                                    .map(XpOperationsRole::getOperationsUserId)
                                    .filter(Objects::nonNull)
                                    .collect(Collectors.toList())
                    );
                }
            } catch (Exception e) {
                log.error("获取角色[{}]对应的运维人员失败", ruleRole.getRoleId(), e);
            }
        }
        return new ArrayList<>(userIds);
    }
    /**
     * 检查是否已存在未完成的同类型任务
     */
    private boolean hasUnfinishedTask(Long vehicleId, String taskTypeId) {
        XpTask existingTask = taskMapper.selectOne(
                new LambdaQueryWrapper<XpTask>()
                        .eq(XpTask::getVehicleId, vehicleId)
                        .eq(XpTask::getTaskTypeId, taskTypeId)
                        .in(XpTask::getStatus,
                                TaskScheduleConstants.TASK_STATUS_PENDING_ARRIVE,
                                TaskScheduleConstants.TASK_STATUS_PENDING)
                        .last("limit 1")
        );
        return existingTask != null;
    }
    private XpTask buildChargingTask(XpVehicle vehicle,
                                    VehiclePosition position,
                                    String belongUserIds,
                                    XpTaskRule taskRule,
                                    int threshold) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String taskCode = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
        Integer currentElectricity = position.getCurrentElectricity();
        String desc = String.format("系统自动下发的车辆充电任务,当前电量%s%%,阈值%d%%",
                currentElectricity == null ? "-" : String.valueOf(currentElectricity),
                threshold);
        XpTask task = new XpTask();
        task.setTaskCode(taskCode);
        task.setTaskCategory(TaskScheduleConstants.TASK_CATEGORY_ROUTINE);
        task.setTaskTypeId(TaskScheduleConstants.TASK_TYPE_CHARGING);
        task.setTaskUrgencyLevel(taskRule.getChargingStrategyLevel() != null
                ? taskRule.getChargingStrategyLevel()
                : TaskScheduleConstants.URGENCY_LEVEL_P2);
        task.setTaskDescription(desc);
        task.setVehicleId(vehicle.getId());
        if (position.getPoint() != null) {
            task.setVehicleLon(BigDecimal.valueOf(position.getPoint().getX()));
            task.setVehicleLat(BigDecimal.valueOf(position.getPoint().getY()));
        }
        // 常规任务默认限时3天
        task.setEndTime(LocalDateTime.now().plusDays(3));
        task.setStatus(TaskScheduleConstants.TASK_STATUS_PENDING);
        task.setHandleUserId(belongUserIds);
        task.setReportUserId(0L);
        task.setReportTime(LocalDateTime.now());
        task.setCreateBy(TaskScheduleConstants.SCHEDULE_CREATOR);
        return task;
    }
}
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java
@@ -226,23 +226,35 @@
    @Override
    public PageInfo<ClockInStatisticsDto> getClockInStatistics(ClockInStatisticsVo query) {
        PageInfo<ClockInStatisticsDto> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<Long> appUserIds = new ArrayList<>();
        List<Long> appUserIds1 = new ArrayList<>();
        List<Long> appUserIds2 = new ArrayList<>();
        List<Long> appUserIds3 = new ArrayList<>();
        //处理查询条件
        if(StringUtils.isNotEmpty(query.getName())){
            R<List<XpOperationsUser>> listR = xpOperationUserClient.listByName(query.getName());
            if(listR.getCode() == Constants.SUCCESS){
                List<Long> collect = listR.getData().stream().map(XpOperationsUser::getId).collect(Collectors.toList());
                appUserIds.addAll(collect);
                appUserIds1.addAll(collect);
            }
            appUserIds.add(0L);
            appUserIds1.add(0L);
        }
        if(null != query.getRoleId()){
            R<List<XpOperationsRole>> roleByRoleId = xpOperationUserClient.getXpOperationsRoleByRoleId(query.getRoleId());
            if(roleByRoleId.getCode() == Constants.SUCCESS){
                List<Long> collect = roleByRoleId.getData().stream().map(XpOperationsRole::getOperationsUserId).collect(Collectors.toList());
                appUserIds.addAll(collect);
                appUserIds2.addAll(collect);
            }
            appUserIds.add(0L);
            appUserIds2.add(0L);
        }
        if(StringUtils.isNotEmpty(query.getName()) && null != query.getRoleId()){
            for (Long l : appUserIds1) {
                if(appUserIds2.contains(l)){
                    appUserIds3.add(l);
                }
            }
        }else{
            appUserIds3.addAll(appUserIds1);
            appUserIds3.addAll(appUserIds2);
        }
        String startTime = "";
@@ -253,7 +265,7 @@
            endTime = statisticDate.split(" - ")[1].split(" ")[0];
        }
        //查询数据
        List<ClockInStatisticsDto> clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds, startTime, endTime);
        List<ClockInStatisticsDto> clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds3, startTime, endTime);
        List<String> appUserIdList = clockInStatistics.stream().map(ClockInStatisticsDto::getAppUserId).collect(Collectors.toList());
        List<Long> ids = appUserIdList.stream().map(Long::parseLong).collect(Collectors.toList());
        List<XpOperationsUserWithRoleVO> data = xpOperationUserClient.listByIdsWithRole(ids).getData();
@@ -272,7 +284,8 @@
            }
            Integer shouldClockInDays = clockInStatistic.getShouldClockInDays();
            Integer clockInDays = clockInStatistic.getClockInDays();
            clockInStatistic.setNotClockInDays(shouldClockInDays - clockInDays);
            Integer lateEarly = clockInStatistic.getLateEarly();
            clockInStatistic.setNotClockInDays(shouldClockInDays - clockInDays - lateEarly);
        }
        pageInfo.setRecords(clockInStatistics);
        return pageInfo;
@@ -287,23 +300,35 @@
    @Override
    public void exportClockInStatistics(ClockInStatisticsVo query, HttpServletResponse response) {
        PageInfo<ClockInStatisticsDto> pageInfo = new PageInfo<>(0, 10000);
        List<Long> appUserIds = new ArrayList<>();
        List<Long> appUserIds1 = new ArrayList<>();
        List<Long> appUserIds2 = new ArrayList<>();
        List<Long> appUserIds3 = new ArrayList<>();
        //处理查询条件
        if(StringUtils.isNotEmpty(query.getName())){
            R<List<XpOperationsUser>> listR = xpOperationUserClient.listByName(query.getName());
            if(listR.getCode() == Constants.SUCCESS){
                List<Long> collect = listR.getData().stream().map(XpOperationsUser::getId).collect(Collectors.toList());
                appUserIds.addAll(collect);
                appUserIds1.addAll(collect);
            }
            appUserIds.add(0L);
            appUserIds1.add(0L);
        }
        if(null != query.getRoleId()){
            R<List<XpOperationsRole>> roleByRoleId = xpOperationUserClient.getXpOperationsRoleByRoleId(query.getRoleId());
            if(roleByRoleId.getCode() == Constants.SUCCESS){
                List<Long> collect = roleByRoleId.getData().stream().map(XpOperationsRole::getOperationsUserId).collect(Collectors.toList());
                appUserIds.addAll(collect);
                appUserIds2.addAll(collect);
            }
            appUserIds.add(0L);
            appUserIds2.add(0L);
        }
        if(StringUtils.isNotEmpty(query.getName()) && null != query.getRoleId()){
            for (Long l : appUserIds1) {
                if(appUserIds2.contains(l)){
                    appUserIds3.add(l);
                }
            }
        }else{
            appUserIds3.addAll(appUserIds1);
            appUserIds3.addAll(appUserIds2);
        }
        String startTime = "";
        String endTime = "";
@@ -313,7 +338,7 @@
            endTime = statisticDate.split(" - ")[1].split(" ")[0];
        }
        //查询数据
        List<ClockInStatisticsDto> clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds, startTime, endTime);
        List<ClockInStatisticsDto> clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds3, startTime, endTime);
        List<String> appUserIdList = clockInStatistics.stream().map(ClockInStatisticsDto::getAppUserId).collect(Collectors.toList());
        List<Long> ids = appUserIdList.stream().map(Long::parseLong).collect(Collectors.toList());
        List<XpOperationsUserWithRoleVO> data = xpOperationUserClient.listByIdsWithRole(ids).getData();
@@ -331,7 +356,8 @@
            }
            Integer shouldClockInDays = clockInStatistic.getShouldClockInDays();
            Integer clockInDays = clockInStatistic.getClockInDays();
            clockInStatistic.setNotClockInDays(shouldClockInDays - clockInDays);
            Integer lateEarly = clockInStatistic.getLateEarly();
            clockInStatistic.setNotClockInDays(shouldClockInDays - clockInDays - lateEarly);
        }
        ExcelUtil<ClockInStatisticsDto> excel = new ExcelUtil<ClockInStatisticsDto>(ClockInStatisticsDto.class);
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpSchedulingDetailServiceImpl.java
@@ -33,14 +33,16 @@
import com.xiaopeng.maintenance.service.IXpShiftService;
import com.xiaopeng.maintenance.vo.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -433,10 +435,12 @@
            List<SaveSchedulingDetailDay> days = data.getDays();
            for (SaveSchedulingDetailDay day : days) {
                Long shiftId = day.getShiftId();
                if(null == shiftId){
                    continue;
                }
                LocalDate parse = LocalDate.parse(day.getDayTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                //查询判断数据是否存在
                XpSchedulingDetail schedulingDetail = list.stream().filter(s -> s.getFleetId().equals(fleetId)
                        && s.getShiftId().equals(shiftId)
                        && s.getPlanTime().equals(parse)
                ).findAny().orElse(null);
                //构建存储对象
@@ -479,9 +483,9 @@
                XpShift xpShift = xpShifts.stream().filter(s -> s.getId().equals(shiftId)).findAny().orElse(null);
                if (null != xpShift) {
                    schedulingDetail.setIsAllDay(xpShift.getShiftName().contains("全天"));
                    schedulingDetail.setPlanTime(parse);
                    schedulingDetails.add(schedulingDetail);
                }
                schedulingDetail.setPlanTime(parse);
                schedulingDetails.add(schedulingDetail);
            }
        }
        //批量保存排班明细数据
@@ -571,9 +575,17 @@
        LocalDate parse1 = LocalDate.parse(month + "-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        int dayOfMonth = parse1.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
        HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
        HSSFCellStyle cellStyle = hssfWorkbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        HSSFSheet hssfSheet = hssfWorkbook.createSheet();
        hssfSheet.setColumnWidth(0, 6 * 256);
        hssfSheet.setDefaultRowHeightInPoints(20f);
        HSSFCellStyle cellStyle1 = hssfWorkbook.createCellStyle();
        cellStyle1.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.index);
        cellStyle1.setAlignment(HorizontalAlignment.CENTER);
        cellStyle1.setVerticalAlignment(VerticalAlignment.CENTER);
        //添加第一行标题
        HSSFRow hssfRow = hssfSheet.createRow(0);
@@ -584,13 +596,14 @@
        //设置表头样式,合并单元格
        CellRangeAddress region = new CellRangeAddress(0, 1, 0, 0);
        hssfSheet.addMergedRegion(region);
        region = new CellRangeAddress(0, 1, dayOfMonth + 1, totalCell);
        region = new CellRangeAddress(0, 1, dayOfMonth + 1, totalCell - 1);
        hssfSheet.addMergedRegion(region);
        //设置单元格数据
        for (int i = 0; i < totalCell; i++) {
            HSSFCell cell = hssfRow.createCell(i);
            cell.setCellStyle(cellStyle1);
            if(0 == i){
                cell.setCellValue("车队");
            }else if(i <= dayOfMonth){
@@ -605,6 +618,7 @@
        //设置单元格数据
        for (int i = 0; i < totalCell; i++) {
            HSSFCell cell = hssfRow1.createCell(i);
            cell.setCellStyle(cellStyle1);
            if(i > 0 && i <= dayOfMonth){
                LocalDate parse = LocalDate.parse(month + "-" + String.format("%02d", i), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                String week = "";
@@ -638,7 +652,9 @@
        int rowNum = 2;
        for (SchedulingDetailDto schedulingDetailDto : schedulingDetailDtos) {
            HSSFRow row = hssfSheet.createRow(rowNum);
            row.createCell(0).setCellValue(schedulingDetailDto.getFleetName());
            HSSFCell cell = row.createCell(0);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(schedulingDetailDto.getFleetName());
            String days = schedulingDetailDto.getDays();
            if (StringUtils.isNotBlank(days)) {
@@ -654,17 +670,30 @@
                        }
                    }
                    row.createCell(i).setCellValue(shiftName);
                    HSSFCell cell1 = row.createCell(i);
                    cell1.setCellValue(shiftName);
                }
                for (int i = 0; i < xpShifts.size(); i++) {
                    String shiftName = xpShifts.get(i).getShiftName();
                    long allDay = list.stream().filter(s -> s.contains(shiftName)).count();
                    row.createCell(dayOfMonth + 1 + i).setCellValue(allDay + allDay);
                    HSSFCell cell1 = row.createCell(dayOfMonth + 1 + i);
                    cell1.setCellStyle(cellStyle);
                    cell1.setCellValue(shiftName + " " + allDay);
                }
            }
            rowNum++;
        }
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("排班计划管理.xls", "UTF-8"));
            ServletOutputStream outputStream = response.getOutputStream();
            hssfWorkbook.write(outputStream);
            outputStream.flush();
            outputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java
@@ -193,6 +193,7 @@
        xpTask.setTaskTypeId(temp.toString());
        xpTask.setTaskUrgencyLevel(dto.getTaskUrgencyLevel());
        xpTask.setTaskDescription(dto.getTaskDescription());
        xpTask.setTaskImage(dto.getTaskImage());
        XpVehicle vehicle = xpVehicleClient.queryById(xpTask.getVehicleId()).getData();
//        if (vehicle!=null){
//            VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicle.getVehicleVin())), VehiclePosition.class);
@@ -1471,15 +1472,12 @@
                res.setReportUserPhone("111111");
                res.setReportUserAvatar("[]");
            }else{
                List<Long> reportUserIds = new ArrayList<>();
                reportUserIds.add(task.getReportUserId());
                List<SysUser> sysUsers = sysUserClient.listByIds(reportUserIds).getData();
                if (sysUsers != null && !sysUsers.isEmpty()) {
                    SysUser sysUser = sysUsers.get(0);
                    res.setReportUserId(sysUser.getId());
                    res.setReportUserName(sysUser.getNickName());
                    res.setReportUserPhone(sysUser.getPhoneNumber());
                    res.setReportUserAvatar(sysUser.getAvatar());
                XpOperationsUser data = xpOperationUserClient.queryById(task.getReportUserId()).getData();
                if (data != null ) {
                    res.setReportUserId(data.getId());
                    res.setReportUserName(data.getNickName());
                    res.setReportUserPhone(data.getPhoneNumber());
                    res.setReportUserAvatar(data.getAvatar());
                }
            }
xp-robotaxi-maintenance-boot/src/main/resources/mapper/maintenance/XpClockInMapper.xml
@@ -50,8 +50,8 @@
        ifnull(sum(aa.num2), 0) as lateEarly,
        ifnull(dd.num1, 0) as shouldClockInDays
        from (
            select app_user_id, count(clock_in_result) as num1, 0 as num2 from xp_clock_in where is_deleted = 0 and
            clock_in_result = 1
            select app_user_id, count(id) as num1, 0 as num2 from xp_clock_in where is_deleted = 0 and
            clock_in_result = 1 and clock_out_result = 1
            <if test="null != appUserIds and appUserIds.size() > 0">
                and app_user_id in
                <foreach item="item" collection="appUserIds" separator="," open="(" close=")" index="index">
@@ -63,8 +63,8 @@
            </if>
            group by app_user_id
            union all
            select app_user_id, 0 as num1, count(clock_in_result) as num2 from xp_clock_in where is_deleted = 0 and
            clock_in_result != 1
            select app_user_id, 0 as num1, count(id) as num2 from xp_clock_in where is_deleted = 0 and
            (clock_in_result != 1 or clock_out_result != 1)
            <if test="null != appUserIds and appUserIds.size() > 0">
                and app_user_id in
                <foreach item="item" collection="appUserIds" separator="," open="(" close=")" index="index">
xp-robotaxi-maintenance-boot/src/main/resources/mapper/maintenance/XpSchedulingDetailMapper.xml
@@ -15,7 +15,7 @@
            a.fleet_id, GROUP_CONCAT(concat(DATE_FORMAT(a.plan_time, '%d' ), '_', a.shift_id, '_', b.shift_name, '_', b.shift_color)) as days
            from xp_scheduling_detail a
            left join xp_shift b on (a.shift_id = b.id)
            where a.is_deleted = 0 and DATE_FORMAT(a.plan_time, '%Y-%m') = #{month}
            where a.is_deleted = 0 and b.is_deleted = 0 and DATE_FORMAT(a.plan_time, '%Y-%m') = #{month}
            group by a.fleet_id
        ) AS bb ON ( aa.id = bb.fleet_id)
        WHERE aa.is_deleted = 0
xp-robotaxi-map-boot/pom.xml
@@ -237,6 +237,11 @@
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.18.5</version>
        </dependency>
        <dependency>
            <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>1.19.0</version> <!-- 请使用最新版本 -->
        </dependency>
    </dependencies>
    <build>
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
@@ -31,6 +31,10 @@
import com.xiaopeng.map.utils.PolygonOverlapService;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pinyin4j.PinyinHelper;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.Polygon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -40,6 +44,7 @@
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.awt.geom.Point2D;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -126,6 +131,11 @@
        // 检查新区域与现有运营区域是否有重叠
        for (XpElectronicFenceArea newArea : newElectronicFenceAreas) {
            //处理围栏闭合
            String[] split = newArea.getFencePoints().split(",");
            if(!split[0].equals(split[split.length - 1])){
                newArea.setFencePoints(newArea.getFencePoints() + "," + split[0]);
            }
            if (newArea.getAreaType() == 1) { // 只检查运营区域
                for (XpElectronicFenceArea existingArea : existingOperationAreas) {
                    if (polygonOverlapService.checkPolygonOverlap(
@@ -165,8 +175,14 @@
        //根据经纬度获取当前城市code
        XpElectronicFenceArea xpElectronicFenceArea = dto.getElectronicFenceAreas().get(0);
        String[] split = xpElectronicFenceArea.getFencePoints().split(",")[0].split("_");
        Map<String, String> geocode = gdMapGeocodingUtil.geocode(split[0], split[1]);
        String[] split = xpElectronicFenceArea.getFencePoints().split(",");
        Coordinate[] coordinates = new Coordinate[split.length + 1];
        for (int i = 0; i < split.length; i++) {
            String[] split1 = split[i].split("_");
            coordinates[i] = new Coordinate(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]));
        }
        org.locationtech.jts.geom.Point point = calculateCentroid(coordinates);
        Map<String, String> geocode = gdMapGeocodingUtil.geocode(point.getX() + "", point.getY() + "");
        if(null == geocode){
            return R.fail("经纬度转换城市code失败");
        }
@@ -304,6 +320,11 @@
            // 检查更新后的区域与现有运营区域是否有重叠
            for (XpElectronicFenceArea updatedArea : updatedElectronicFenceAreas) {
                //处理围栏闭合
                String[] split = updatedArea.getFencePoints().split(",");
                if(!split[0].equals(split[split.length - 1])){
                    updatedArea.setFencePoints(updatedArea.getFencePoints() + "," + split[0]);
                }
                if (updatedArea.getAreaType() == 1) { // 只检查运营区域
                    for (XpElectronicFenceArea existingArea : existingOperationAreas) {
                        if (polygonOverlapService.checkPolygonOverlap(
@@ -382,8 +403,14 @@
            }
            //根据经纬度获取当前城市code
            XpElectronicFenceArea electronicFenceArea = dto.getElectronicFenceAreas().get(0);
            String[] split = electronicFenceArea.getFencePoints().split(",")[0].split("_");
            Map<String, String> geocode = gdMapGeocodingUtil.geocode(split[0], split[1]);
            String[] split = electronicFenceArea.getFencePoints().split(",");
            Coordinate[] coordinates = new Coordinate[split.length];
            for (int i = 0; i < split.length; i++) {
                String[] split1 = split[i].split("_");
                coordinates[i] = new Coordinate(Double.parseDouble(split1[0]), Double.parseDouble(split1[1]));
            }
            org.locationtech.jts.geom.Point point = calculateCentroid(coordinates);
            Map<String, String> geocode = gdMapGeocodingUtil.geocode(point.getX() + "", point.getY() + "");
            if(null == geocode){
                return R.fail("经纬度转换城市code失败");
            }
@@ -696,4 +723,22 @@
    }
    /**
     * 计算围栏中心点
     * @param coordinates
     * @return
     */
    private org.locationtech.jts.geom.Point calculateCentroid(Coordinate[] coordinates) {
        GeometryFactory factory = new GeometryFactory();
        // 创建线性环 (LinearRing)
        LinearRing ring = factory.createLinearRing(coordinates);
        // 创建多边形 (Polygon)
        Polygon polygon = factory.createPolygon(ring);
        // 计算质心 (Centroid)
        org.locationtech.jts.geom.Point centroid = polygon.getCentroid();
        System.out.println("中心点经度: " + centroid.getX());
        System.out.println("中心点纬度: " + centroid.getY());
        return centroid;
    }
}
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/OssController.java
@@ -40,17 +40,17 @@
            long fileSize = file.getSize();
            String fileId = UUID.randomUUID().toString();
            //上传文件
            aliOssService.uploadFile(file.getBytes(), fileId);
            String downloadUrl = aliOssService.getDownloadUrl(fileId, fileName,true);
            FileUploadVO fileUploadVO = new FileUploadVO();
            fileUploadVO.setId(fileId);
            fileUploadVO.setName(fileName);
            fileUploadVO.setUrl(downloadUrl);
//            aliOssService.uploadFile(file.getBytes(), fileId);
//            String downloadUrl = aliOssService.getDownloadUrl(fileId, fileName,true);
//            FileUploadVO fileUploadVO = new FileUploadVO();
//            fileUploadVO.setId(UUID.randomUUID().toString());
//            fileUploadVO.setName("测试.jpg");
//            fileUploadVO.setUrl("https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/552a3a31-e4dd-4a28-8748-fd8b87dd3055.png");
//            fileUploadVO.setId(fileId);
//            fileUploadVO.setName(fileName);
//            fileUploadVO.setUrl(downloadUrl);
            FileUploadVO fileUploadVO = new FileUploadVO();
            fileUploadVO.setId(UUID.randomUUID().toString());
            fileUploadVO.setName("测试.jpg");
            fileUploadVO.setUrl("https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/552a3a31-e4dd-4a28-8748-fd8b87dd3055.png");
            return R.ok(fileUploadVO);
        } catch (Exception e) {
            System.err.println("上传文件失败"+e);
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
@@ -7,6 +7,7 @@
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.core.util.PhoneUtil;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.api.model.operation.XpWhitelistOperlog;
@@ -49,8 +50,14 @@
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -200,51 +207,76 @@
        ImportParams params = new ImportParams();
//        params.setTitleRows(1); // 标题行数
        params.setHeadRows(1); //表头行数
        InputStream inputStream = null;
//        List<CustomerImportFailedData> failedData = new ArrayList<>();
        List<WhitelistImportExcel> whitelistImportExcels;
        try {
            inputStream = file.getInputStream();
            whitelistImportExcels = ExcelImportUtil.importExcel(inputStream,
                    WhitelistImportExcel.class, params);
            try (InputStream inputStream = file.getInputStream()) {
                whitelistImportExcels = ExcelImportUtil.importExcel(inputStream,
                        WhitelistImportExcel.class, params);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("白名单导入失败:{}", e.getMessage());
            throw new ServiceException("白名单导入失败!");
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                throw new ServiceException(e.getMessage());
            }
        }
        if (CollectionUtils.isEmpty(whitelistImportExcels)) {
            throw new ServiceException("白名单数据为空!");
        }
        // 校验手机号白名单是否有重复的
        long count = whitelistImportExcels.stream().map(WhitelistImportExcel::getAccount)
                .distinct().count();
        if (count != whitelistImportExcels.size()) {
            return AjaxResult.error("白名单手机号存在重复!");
        }
        Map<String, Long> codeMap = xpWhitelistService.list().stream()
                .collect(Collectors.toMap(
                        XpWhitelist::getAccount,
                        XpWhitelist::getId,
                        (existing, replacement) -> replacement // 如果冲突,用新的覆盖
                ));
        JSONObject result = new JSONObject();
        List<XpWhitelist> addList = new ArrayList<>();
        for (WhitelistImportExcel whitelistImportExcel : whitelistImportExcels) {
            // 手机号正则校验
            boolean isPhone = PhoneUtil.isPhone(whitelistImportExcel.getAccount());
            if (!isPhone) {
                result.append("白名单:[", whitelistImportExcel.getAccount() + "]手机号格式错误");
        int sampleLimitPerReason = 20;
        int totalCount = whitelistImportExcels.size();
        Map<String, Long> reasonCountMap = new HashMap<>();
        Map<String, LinkedHashSet<String>> reasonSampleMap = new HashMap<>();
        // 先做基础校验 + 文件内去重(允许部分成功)
        Set<String> seenAccounts = new HashSet<>();
        List<WhitelistImportExcel> candidates = new ArrayList<>();
        for (int i = 0; i < whitelistImportExcels.size(); i++) {
            WhitelistImportExcel row = whitelistImportExcels.get(i);
            String account = trimToNull(row.getAccount());
            String nickName = trimToNull(row.getNickName());
            row.setAccount(account);
            row.setNickName(nickName);
            int excelRowNumber = i + params.getHeadRows() + 1;
            if (!StringUtils.hasLength(account)) {
                recordFail(reasonCountMap, reasonSampleMap, "账号为空", "第" + excelRowNumber + "行", sampleLimitPerReason);
                continue;
            }
            Long id = codeMap.get(whitelistImportExcel.getAccount());
            if (id != null) {
                result.append("白名单:[", whitelistImportExcel.getAccount() + "]已存在");
            if (!PhoneUtil.isPhone(account)) {
                recordFail(reasonCountMap, reasonSampleMap, "账号格式不正确", account, sampleLimitPerReason);
                continue;
            }
            if (!seenAccounts.add(account)) {
                recordFail(reasonCountMap, reasonSampleMap, "文件内账号重复", account, sampleLimitPerReason);
                continue;
            }
            candidates.add(row);
        }
        // 批量查询数据库已存在账号(避免全表扫描)
        Set<String> existsAccounts = Collections.emptySet();
        if (!CollectionUtils.isEmpty(candidates)) {
            Set<String> candidateAccounts = candidates.stream()
                    .map(WhitelistImportExcel::getAccount)
                    .filter(StringUtils::hasLength)
                    .collect(Collectors.toSet());
            if (!CollectionUtils.isEmpty(candidateAccounts)) {
                existsAccounts = xpWhitelistService.lambdaQuery()
                        .select(XpWhitelist::getAccount)
                        .in(XpWhitelist::getAccount, candidateAccounts)
                        .list()
                        .stream()
                        .map(XpWhitelist::getAccount)
                        .filter(StringUtils::hasLength)
                        .collect(Collectors.toSet());
            }
        }
        List<XpWhitelist> addList = new ArrayList<>();
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        for (WhitelistImportExcel whitelistImportExcel : candidates) {
            if (existsAccounts.contains(whitelistImportExcel.getAccount())) {
                recordFail(reasonCountMap, reasonSampleMap, "账号已存在", whitelistImportExcel.getAccount(), sampleLimitPerReason);
                continue;
            }
            XpWhitelist xpWhitelist = new XpWhitelist();
@@ -253,25 +285,120 @@
            xpWhitelist.setStatus(true);
            xpWhitelist.setIsPriorityDispatch(true);
            xpWhitelist.setWhitelistSource(1);
            xpWhitelist.setCreateBy(tokenService.getLoginUser().getSysUser().getNickName());
            xpWhitelist.setCreateBy(sysUser.getNickName());
            addList.add(xpWhitelist);
        }
        if (!result.isEmpty()) {
            return AjaxResult.success(result.toString());
        int successCount = addList.size();
        int failCount = totalCount - successCount;
        String message;
        if (failCount == 0) {
            message = "成功添加" + successCount + "条白名单记录";
        } else if (successCount == 0) {
            message = "所有帐号添加失败,请检查格式或唯一性";
        } else {
            String reasonDetail = buildReasonDetail(reasonCountMap, reasonSampleMap);
            message = "成功添加" + successCount + "条," + failCount + "条添加失败:原因" + reasonDetail;
        }
        if (successCount == 0) {
            return AjaxResult.error(message, message);
        }
        xpWhitelistService.saveBatch(addList);
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("共计导入"+addList.size()+"条数据");
        StringBuilder operationContent = new StringBuilder("白名单导入:成功添加")
                .append(successCount).append("条,失败").append(failCount).append("条");
        if (failCount > 0) {
            operationContent.append(";原因").append(buildReasonSummary(reasonCountMap));
        }
        String opContent = truncate(operationContent.toString(), 200);
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(4);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        xpWhitelistOperlog.setOperationContent(operationContent.toString());
        xpWhitelistOperlog.setCreateBy(tokenService.getLoginUser().getSysUser().getNickName());
        xpWhitelistOperlog.setOperationContent(opContent);
        xpWhitelistOperlog.setCreateBy(sysUser.getNickName());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        return AjaxResult.success();
        return AjaxResult.success(message, message);
    }
    private static String trimToNull(String value) {
        if (!StringUtils.hasLength(value)) {
            return null;
        }
        String trimmed = value.trim();
        return StringUtils.hasLength(trimmed) ? trimmed : null;
    }
    private static void incReason(Map<String, Long> reasonCountMap, String reason) {
        reasonCountMap.merge(reason, 1L, Long::sum);
    }
    private static void recordFail(Map<String, Long> reasonCountMap,
                                   Map<String, LinkedHashSet<String>> reasonSampleMap,
                                   String reason,
                                   String sample,
                                   int sampleLimitPerReason) {
        incReason(reasonCountMap, reason);
        if (!StringUtils.hasLength(sample) || sampleLimitPerReason <= 0) {
            return;
        }
        LinkedHashSet<String> samples = reasonSampleMap.computeIfAbsent(reason, k -> new LinkedHashSet<>());
        if (samples.size() >= sampleLimitPerReason) {
            return;
        }
        samples.add(sample);
    }
    private static String buildReasonSummary(Map<String, Long> reasonCountMap) {
        if (reasonCountMap == null || reasonCountMap.isEmpty()) {
            return "请检查格式或唯一性";
        }
        return reasonCountMap.entrySet().stream()
                .sorted(Comparator.<Map.Entry<String, Long>>comparingLong(e -> e.getValue() == null ? 0L : e.getValue())
                        .reversed()
                        .thenComparing(Map.Entry::getKey))
                .map(e -> e.getKey() + "(" + (e.getValue() == null ? 0L : e.getValue()) + ")")
                .collect(Collectors.joining(","));
    }
    private static String buildReasonDetail(Map<String, Long> reasonCountMap,
                                           Map<String, LinkedHashSet<String>> reasonSampleMap) {
        if (reasonCountMap == null || reasonCountMap.isEmpty()) {
            return "请检查格式或唯一性";
        }
        Map<String, LinkedHashSet<String>> safeSamples = reasonSampleMap == null ? Collections.emptyMap() : reasonSampleMap;
        return reasonCountMap.entrySet().stream()
                .sorted(Comparator.<Map.Entry<String, Long>>comparingLong(e -> e.getValue() == null ? 0L : e.getValue())
                        .reversed()
                        .thenComparing(Map.Entry::getKey))
                .map(entry -> {
                    String reason = entry.getKey();
                    long count = entry.getValue() == null ? 0L : entry.getValue();
                    LinkedHashSet<String> samples = safeSamples.getOrDefault(reason, new LinkedHashSet<>());
                    String samplePart = "";
                    if (!samples.isEmpty()) {
                        long remaining = count - samples.size();
                        String suffix = remaining > 0 ? "等" + remaining + "个" : "";
                        samplePart = "[" + String.join(",", samples) + (StringUtils.hasLength(suffix) ? ("," + suffix) : "") + "]";
                    }
                    return reason + "(" + count + ")" + samplePart;
                })
                .collect(Collectors.joining(";"));
    }
    private static String truncate(String value, int maxLength) {
        if (value == null || maxLength <= 0) {
            return value;
        }
        if (value.length() <= maxLength) {
            return value;
        }
        return value.substring(0, maxLength);
    }
    @ApiOperation(value = "白名单导出")
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -648,9 +648,8 @@
     */
    private void closeOrder(XpOrder order) {
        // 更新订单状态为已完成(10)
        order.setStatus(10);
        this.updateById(order);
        this.lambdaUpdate().set(XpOrder::getStatus, 10)
                .eq(XpOrder::getId, order.getId()).update();
        log.info("订单关闭成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername());
    }
@@ -1101,7 +1100,10 @@
            throw new ServiceException("订单状态错误", I18nKeyConstant.ORDER_STATUS_ERROR);
        }
        redisService.removeByPattern(order.getQueueName(),order.getOrderNumber());
        try {
            redisService.removeByPattern(order.getQueueName(),order.getOrderNumber());
        }catch (Exception e){
        }
        //判断还原车辆状态
        if(null == order.getVehicleId()){
            Object cacheObject1 = redisService.getCacheObject("CREATE_ORDER:" + order.getId().toString());
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -343,7 +343,7 @@
            res.put("name", "测试.png");
            return res.toJSONString();
        } catch (Exception e) {
            log.error("广告物料整体项目结算表导入图片上传oss异常", e);
            log.error("在库车辆导入图片上传oss异常", e);
        }
        return null;
    }
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/model/vo/FleetPageVO.java
@@ -31,6 +31,9 @@
    @ApiModelProperty(value = "关联运营区域")
    private String electronicName;
    @ApiModelProperty(value = "打卡地名称")
    private String locationName;
    @ApiModelProperty(value = "负责人电话")
    private String phoneNumber;
xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java
@@ -328,8 +328,8 @@
                .distinct()
                .collect(Collectors.toList());
        List<Long> electronicIds = resultPage.getRecords().stream()
                .map(XpFleet::getElectronicId)
        List<Long> locationIds = resultPage.getRecords().stream()
                .map(XpFleet::getLocationId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
@@ -344,13 +344,19 @@
            }
        }
        // 5. 批量查询围栏信息并转换为Map
        Map<Long, XpElectronicFence> fenceMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(electronicIds)) {
            R<List<XpElectronicFence>> fencesResult = xpElectronicClient.listByIds(electronicIds);
            if (fencesResult.getData() != null) {
                fenceMap = fencesResult.getData().stream()
                        .collect(Collectors.toMap(XpElectronicFence::getId, fence -> fence));
        // 5. 批量查询打卡地信息并转换为Map
        Map<Long, String> locationNameMap = Collections.emptyMap();
        if (!CollectionUtils.isEmpty(locationIds)) {
            R<List<XpCheckLocation>> locationsResult = xpCheckLocationClient.listByIds(locationIds);
            if (locationsResult != null && locationsResult.getData() != null) {
                locationNameMap = locationsResult.getData().stream()
                        .filter(Objects::nonNull)
                        .filter(location -> location.getId() != null && location.getLocationAddress() != null)
                        .collect(Collectors.toMap(
                                XpCheckLocation::getId,
                                XpCheckLocation::getLocationAddress,
                                (a, b) -> a
                        ));
            }
        }
@@ -379,12 +385,9 @@
                }
            }
            // 从Map中获取围栏名称
            if (fleet.getElectronicId() != null) {
                XpElectronicFence fence = fenceMap.get(fleet.getElectronicId());
                if (fence != null) {
                    vo.setElectronicName(fence.getElectronicName());
                }
            // 从Map中获取打卡地名称
            if (fleet.getLocationId() != null) {
                vo.setLocationName(locationNameMap.get(fleet.getLocationId()));
            }
            records.add(vo);