package com.xiaopeng.automaticPilot.service.impl;
|
|
import com.xiaopeng.api.feignClient.account.XpOperationUserClient;
|
import com.xiaopeng.api.feignClient.account.XpOperationsRoleClient;
|
import com.xiaopeng.api.feignClient.operation.SmsClient;
|
import com.xiaopeng.api.model.account.XpOperationsRole;
|
import com.xiaopeng.api.model.account.XpOperationsUser;
|
import com.xiaopeng.api.vo.operation.SmsVo;
|
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.domain.R;
|
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 com.xiaopeng.automaticPilot.enums.SmsPushType;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.CollectionUtils;
|
|
import javax.annotation.Resource;
|
import java.util.Collections;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.TimeUnit;
|
|
@Slf4j
|
@Service
|
public class VehicleRtInfoServiceImpl implements VehicleRtInfoService {
|
|
private static final long CODE_TTL_MINUTES = 5L;
|
public static final String ROBOTAXI_VERIFY_CODE_SMS_TPL = "RobotaxiVerifyCodeSmsTpl";
|
public static final String ROBOTAXI_VERIFY_CODE_SMS = "RobotaxiVerifyCodeSms";
|
|
@Resource
|
private RedisService redisService;
|
|
@Resource
|
private XpVehicleClient xpVehicleClient;
|
|
@Resource
|
private SmsClient smsClient;
|
|
@Resource
|
private XpOperationUserClient xpOperationUserClient;
|
|
@Resource
|
private XpOperationsRoleClient xpOperationsRoleClient;
|
|
@Value("${robotaxi.sms.app-id:1476153389173952512}")
|
private String smsAppId;
|
|
@Value("#{'${robotaxi.sms.whitelist.mobiles:}'.split(',')}")
|
private List<String> whitelistMobiles;
|
|
/**
|
* 安全员角色ID,未配置或为空则不做安全员角色校验
|
*/
|
@Value("${robotaxi.sms.safety-officer-role-id:#{null}}")
|
private Long safetyOfficerRoleId;
|
|
@Override
|
public XpCommonResponse<?> getRobotaxiSmsCode(RobotaxiSmsCodeRequest request) {
|
XpCommonResponse<?> paramCheckError = validateSmsCodeRequest(request);
|
if (paramCheckError != null) {
|
return paramCheckError;
|
}
|
|
// 校验用户是否为安全员角色
|
XpCommonResponse<?> roleCheckError = validateSafetyOfficerRole(request.getMobile());
|
if (roleCheckError != null) {
|
return roleCheckError;
|
}
|
|
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);
|
Map<String, String> param = Collections.singletonMap("code", code);
|
SmsVo smsVo = new SmsVo();
|
smsVo.setAppId(smsAppId);
|
smsVo.setParams(param);
|
smsVo.setTemplateCode(ROBOTAXI_VERIFY_CODE_SMS_TPL);
|
smsVo.setAppPushConfigCode(ROBOTAXI_VERIFY_CODE_SMS);
|
smsVo.setReceivers(request.getMobile());
|
smsVo.setPushType(SmsPushType.SMS.getCode());
|
smsClient.pushSms(smsVo);
|
|
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.VERIFY_CODE_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;
|
}
|
|
/**
|
* 校验手机号对应的运维人员是否拥有安全员角色。
|
* 若未配置 safetyOfficerRoleId 则跳过校验。
|
*/
|
private XpCommonResponse<?> validateSafetyOfficerRole(String mobile) {
|
if (safetyOfficerRoleId == null) {
|
return null;
|
}
|
try {
|
R<XpOperationsUser> userResult = xpOperationUserClient.getByExactPhone(mobile);
|
if (userResult == null || userResult.getData() == null) {
|
log.warn("安全员角色校验失败:手机号 {} 未找到对应运维人员", mobile);
|
return XpCommonResponse.fail(RobotaxiErrorEnum.NOT_SAFETY_OFFICER);
|
}
|
|
R<List<XpOperationsRole>> roleRelResult = xpOperationsRoleClient.listByOperationUserId(userResult.getData().getId());
|
if (roleRelResult != null && !CollectionUtils.isEmpty(roleRelResult.getData())) {
|
for (XpOperationsRole operationsRole : roleRelResult.getData()) {
|
if (safetyOfficerRoleId.equals(operationsRole.getRoleId())) {
|
return null; // 校验通过
|
}
|
}
|
}
|
|
log.warn("安全员角色校验失败:运维人员 {} 不具备安全员角色(roleId={})", userResult.getData().getId(), safetyOfficerRoleId);
|
return XpCommonResponse.fail(RobotaxiErrorEnum.NOT_SAFETY_OFFICER);
|
} catch (Exception e) {
|
log.error("安全员角色校验异常,mobile={}", mobile, e);
|
return XpCommonResponse.fail(RobotaxiErrorEnum.GENERAL_ERROR);
|
}
|
}
|
|
private String buildSmsCodeKey(String smsType, String vin, String mobile) {
|
return "ROBOTAXI_SMS_CODE:" + smsType + ":" + vin + ":" + mobile;
|
}
|
}
|