xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/account/XpOperationsUserNotification.java
@@ -3,10 +3,6 @@ import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; import java.io.Serializable; import com.sun.org.apache.bcel.internal.generic.INEG; import com.xiaopeng.common.core.web.domain.BaseModel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; xp-robotaxi-automatic-pilot-boot/pom.xml
@@ -133,13 +133,27 @@ <artifactId>protobuf-java-util</artifactId> <version>3.19.4</version> </dependency> <dependency> <groupId>com.xiaopeng</groupId> <artifactId>unified-verification</artifactId> <version>1.5.2-v2</version> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine> --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED </argLine> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/config/KafkaConfig.java
@@ -14,6 +14,7 @@ import java.util.HashMap; import java.util.Map; //TODO 这个配置类不生效的 /** * Kafka 配置类 */ xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/config/WebMvcConfigurer.java
New file @@ -0,0 +1,26 @@ package com.xiaopeng.automaticPilot.config; import com.xiaopeng.unifiedverification.interceptor.CheckForClientInterceptor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import javax.annotation.Resource; @Slf4j @Configuration public class WebMvcConfigurer extends WebMvcConfigurerAdapter { @Resource private CheckForClientInterceptor checkForClientInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // 统一验证服务拦截器 registry.addInterceptor(checkForClientInterceptor).addPathPatterns("/vehicle/v1/**"); super.addInterceptors(registry); } } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/KafkaTestController.java
@@ -6,7 +6,6 @@ import org.springframework.kafka.annotation.KafkaListener; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController public class KafkaTestController { @@ -14,9 +13,7 @@ @Autowired private KafkaConsumerService kafkaConsumerService; //TODO 少了factory属性声明 /** * 消费消息 - XPU上报Robotaxi云端的消息topic(高优先级) * @@ -28,6 +25,7 @@ kafkaConsumerService.topicProdRobotaxiXpuHigh(message); } //TODO 少了factory属性声明 /** * 消费消息 - XPU上报Robotaxi云端的消息topic(低优先级) * xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
@@ -34,7 +34,6 @@ @Resource private XpVehicleClient xpVehicleClient; @PostMapping("/inner/pushOrder") public R<Boolean> pushOrder(@RequestBody PushOrderDto dto) { RtTask.Builder taskBuilder = RtTask.newBuilder(); xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/VehicleRtInfoController.java
New file @@ -0,0 +1,40 @@ package com.xiaopeng.automaticPilot.controller; import com.xiaopeng.automaticPilot.dto.XpCommonResponse; import com.xiaopeng.automaticPilot.dto.RobotaxiSmsCodeRequest; import com.xiaopeng.automaticPilot.dto.RobotaxiSmsValidateRequest; import com.xiaopeng.automaticPilot.service.VehicleRtInfoService; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import io.swagger.annotations.Api; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author zhouhn * 2026/2/27 */ @RestController @RequestMapping("/vehicle/v1/robotaxi") @Api(tags = "座舱RTInfo接口") public class VehicleRtInfoController { @Resource private VehicleRtInfoService vehicleRtInfoService; @XpClientCheck(checkToken = false) @PostMapping("/sms/code") public XpCommonResponse<?> getRobotaxiSmsCode(@RequestBody RobotaxiSmsCodeRequest request) { return vehicleRtInfoService.getRobotaxiSmsCode(request); } @XpClientCheck(checkToken = false) @PostMapping("/sms/validate") public XpCommonResponse<?> validateRobotaxiSmsCode(@RequestBody RobotaxiSmsValidateRequest request) { return vehicleRtInfoService.validateRobotaxiSmsCode(request); } } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/dto/RobotaxiSmsCodeRequest.java
New file @@ -0,0 +1,10 @@ package com.xiaopeng.automaticPilot.dto; import lombok.Data; @Data public class RobotaxiSmsCodeRequest { private String smsType; private String vin; private String mobile; } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/dto/RobotaxiSmsValidateRequest.java
New file @@ -0,0 +1,11 @@ package com.xiaopeng.automaticPilot.dto; import lombok.Data; @Data public class RobotaxiSmsValidateRequest { private String smsType; private String vin; private String mobile; private String code; } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/dto/XpCommonResponse.java
New file @@ -0,0 +1,33 @@ package com.xiaopeng.automaticPilot.dto; import lombok.Data; import com.xiaopeng.automaticPilot.enums.RobotaxiErrorEnum; @Data public class XpCommonResponse<T> { private static final int SUCCESS_CODE = 0; private Integer code; private String msg; private T data; public static <T> XpCommonResponse<T> ok(T data) { XpCommonResponse<T> response = new XpCommonResponse<>(); response.setCode(SUCCESS_CODE); response.setMsg("成功"); response.setData(data); return response; } public static <T> XpCommonResponse<T> fail(int code, String msg) { XpCommonResponse<T> response = new XpCommonResponse<>(); response.setCode(code); response.setMsg(msg); response.setData(null); return response; } public static <T> XpCommonResponse<T> fail(RobotaxiErrorEnum errorEnum) { return fail(errorEnum.getCode(), errorEnum.getMessage()); } } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/enums/RobotaxiErrorEnum.java
New file @@ -0,0 +1,18 @@ package com.xiaopeng.automaticPilot.enums; import lombok.AllArgsConstructor; import lombok.Getter; @Getter @AllArgsConstructor public enum RobotaxiErrorEnum { GENERAL_ERROR(20019999, "服务器开小差,请稍后重试"), MOBILE_VIN_MISMATCH(20011000, "安全员手机号和车辆不匹配"), VEHICLE_NOT_READY(20011001, "当前车辆状态未就绪,不能接单"), VERIFY_CODE_ERROR(20011002, "验证码错误"); private final int code; private final String message; } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/VehicleRtInfoService.java
New file @@ -0,0 +1,16 @@ package com.xiaopeng.automaticPilot.service; import com.xiaopeng.automaticPilot.dto.XpCommonResponse; import com.xiaopeng.automaticPilot.dto.RobotaxiSmsCodeRequest; import com.xiaopeng.automaticPilot.dto.RobotaxiSmsValidateRequest; public interface VehicleRtInfoService { XpCommonResponse<?> getRobotaxiSmsCode(RobotaxiSmsCodeRequest request); XpCommonResponse<?> validateRobotaxiSmsCode(RobotaxiSmsValidateRequest request); enum SmsType { ACCEPT_RIDES, STOP_ACCEPT_RIDES } } xp-robotaxi-automatic-pilot-boot/src/main/java/com/xiaopeng/automaticPilot/service/impl/VehicleRtInfoServiceImpl.java
New file @@ -0,0 +1,136 @@ package com.xiaopeng.automaticPilot.service.impl; import com.xiaopeng.automaticPilot.dto.XpCommonResponse; import com.xiaopeng.automaticPilot.dto.RobotaxiSmsCodeRequest; import com.xiaopeng.automaticPilot.dto.RobotaxiSmsValidateRequest; import com.xiaopeng.automaticPilot.service.VehicleRtInfoService; import com.xiaopeng.api.dto.vehicleConvoy.UpdateVehicleStateDto; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.common.core.utils.MsgUtil; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.automaticPilot.enums.RobotaxiErrorEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @Slf4j @Service public class VehicleRtInfoServiceImpl implements VehicleRtInfoService { private static final long CODE_TTL_MINUTES = 5L; @Resource private RedisService redisService; @Resource private XpVehicleClient xpVehicleClient; @Value("#{'${robotaxi.sms.whitelist.mobiles:}'.split(',')}") private List<String> whitelistMobiles; @Override public XpCommonResponse<?> getRobotaxiSmsCode(RobotaxiSmsCodeRequest request) { XpCommonResponse<?> paramCheckError = validateSmsCodeRequest(request); if (paramCheckError != null) { return paramCheckError; } XpCommonResponse<?> vehicleCheckError = validateVehicleReadyIfNeeded(request.getSmsType(), request.getVin()); if (vehicleCheckError != null) { return vehicleCheckError; } String code = MsgUtil.createCode(); log.info("Robotaxi SMS code generated for mobile {}: {}", request.getMobile(), code); // TODO: Call SMS interface to send code when available // sendSms(request.getMobile(), code); String key = buildSmsCodeKey(request.getSmsType(), request.getVin(), request.getMobile()); redisService.setCacheObject(key, code, CODE_TTL_MINUTES, TimeUnit.MINUTES); return XpCommonResponse.ok(Collections.emptyMap()); } @Override public XpCommonResponse<?> validateRobotaxiSmsCode(RobotaxiSmsValidateRequest request) { XpCommonResponse<?> paramCheckError = validateSmsValidateRequest(request); if (paramCheckError != null) { return paramCheckError; } XpCommonResponse<?> vehicleCheckError = validateVehicleReadyIfNeeded(request.getSmsType(), request.getVin()); if (vehicleCheckError != null) { return vehicleCheckError; } boolean isWhitelisted = whitelistMobiles != null && whitelistMobiles.contains(request.getMobile()); String key = buildSmsCodeKey(request.getSmsType(), request.getVin(), request.getMobile()); if (!isWhitelisted) { String cachedCode = redisService.getCacheObject(key); if (StringUtils.isEmpty(cachedCode)) { return XpCommonResponse.fail(RobotaxiErrorEnum.GENERAL_ERROR); } if (!cachedCode.equals(request.getCode())) { return XpCommonResponse.fail(RobotaxiErrorEnum.VERIFY_CODE_ERROR); } redisService.deleteObject(key); } else { redisService.deleteObject(key); } return XpCommonResponse.ok(Collections.emptyMap()); } private XpCommonResponse<?> validateSmsCodeRequest(RobotaxiSmsCodeRequest request) { if (StringUtils.isAnyEmpty(request.getSmsType(), request.getVin(), request.getMobile())) { return XpCommonResponse.fail(RobotaxiErrorEnum.GENERAL_ERROR); } if (!isSupportedSmsType(request.getSmsType())) { return XpCommonResponse.fail(RobotaxiErrorEnum.GENERAL_ERROR); } return null; } private XpCommonResponse<?> validateSmsValidateRequest(RobotaxiSmsValidateRequest request) { if (StringUtils.isAnyEmpty(request.getSmsType(), request.getVin(), request.getMobile(), request.getCode())) { return XpCommonResponse.fail(RobotaxiErrorEnum.GENERAL_ERROR); } if (!isSupportedSmsType(request.getSmsType())) { return XpCommonResponse.fail(RobotaxiErrorEnum.GENERAL_ERROR); } return null; } private boolean isSupportedSmsType(String smsType) { return SmsType.ACCEPT_RIDES.name().equals(smsType) || SmsType.STOP_ACCEPT_RIDES.name().equals(smsType); } private XpCommonResponse<?> validateVehicleReadyIfNeeded(String smsType, String vin) { if (!SmsType.ACCEPT_RIDES.name().equals(smsType)) { return null; } // XpVehicle vehicle = xpVehicleClient.findByVehicleVin(vin).getData(); // if (vehicle == null) { // return XpCommonResponse.fail(RobotaxiErrorEnum.MOBILE_VIN_MISMATCH); // } // Integer status = vehicle.getStatus(); // Integer operatingStatus = vehicle.getOperatingStatus(); // if (status == null || status != 2 || operatingStatus == null || operatingStatus != 1) { // return XpCommonResponse.fail(RobotaxiErrorEnum.VEHICLE_NOT_READY); // } return null; } private String buildSmsCodeKey(String smsType, String vin, String mobile) { return "ROBOTAXI_SMS_CODE:" + smsType + ":" + vin + ":" + mobile; } } xp-robotaxi-automatic-pilot-boot/src/main/resources/application-local.yml
@@ -237,3 +237,8 @@ data-id: seata-server.properties username: nacos password: nacos robotaxi: sms: whitelist: mobiles: 13800138000 xp-robotaxi-common/xp-common-redis/pom.xml
@@ -33,6 +33,17 @@ <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.19.3</version> <exclusions> <exclusion> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-30</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-data-27</artifactId> <version>3.19.3</version> </dependency> </dependencies> </project>