xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/WebSocketMessageFallbackFactory.java
New file @@ -0,0 +1,20 @@ package com.xiaopeng.api.factory.pushMessage; import com.xiaopeng.api.dto.pushMessage.AliPushMessageDTO; import com.xiaopeng.api.feignClient.pushMessage.WebSocketMessageClient; import com.xiaopeng.api.vo.TaskVO; import com.xiaopeng.common.core.domain.R; import org.springframework.cloud.openfeign.FallbackFactory; public class WebSocketMessageFallbackFactory implements FallbackFactory<WebSocketMessageClient> { @Override public WebSocketMessageClient create(Throwable cause) { return new WebSocketMessageClient() { @Override public R<String> pushTask(TaskVO dto) { return R.fail("发送失败"); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/WebSocketMessageClient.java
New file @@ -0,0 +1,27 @@ package com.xiaopeng.api.feignClient.pushMessage; import com.xiaopeng.api.factory.pushMessage.WebSocketMessageFallbackFactory; import com.xiaopeng.api.vo.TaskVO; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; /** * 消息Feign客户端 * * @author xiaochen */ @FeignClient(contextId = "WebSocketMessageClient", value = ServiceNameConstantsEnum.XP_PUSH_MESSAGE_SERVICE, fallbackFactory = WebSocketMessageFallbackFactory.class) public interface WebSocketMessageClient { /** * 工单推送 */ @PostMapping("/inner/websocket/push/task") R<String> pushTask(@RequestBody TaskVO dto); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/TaskVO.java
New file @@ -0,0 +1,64 @@ package com.xiaopeng.api.vo; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; /** * <p> * 运维APP 任务中心分页列表VO * </p> * * @author mitao * @since 2026-02-06 */ @Data @ApiModel(value = "任务中心分页列表VO") public class TaskVO implements Serializable { @ApiModelProperty(value = "任务") private Long id; @ApiModelProperty(value = "任务编号") private String taskCode; @ApiModelProperty(value = "任务类别1常规 2事故") private Integer taskCategory; @ApiModelProperty(value = "任务类型id 任务类别为2时对应数据字典,多个逗号分割,为1的时候 枚举1车辆检查 2车辆充电 3车辆整备 4整备洗车 5车辆维修 6车辆保养 7车辆保险到期 8车辆年检到期 9车辆运营资质到期") private String taskTypeId; @ApiModelProperty(value = "任务类型名称 任务类别为2时返回") private String taskTypeName; @ApiModelProperty(value = "任务紧急程度 0=P0 1=P1 2=P2 3=P3") private Integer taskUrgencyLevel; @ApiModelProperty(value = "任务处理时限") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime endTime; @ApiModelProperty(value = "车辆位置") private String vehicleLocation; @ApiModelProperty(value = "车牌号") private String plateNumber; @ApiModelProperty(value = "车辆id") private Long vehicleId; @ApiModelProperty(value = "车辆经度") private BigDecimal vehicleLon; @ApiModelProperty(value = "车辆纬度") private BigDecimal vehicleLat; @ApiModelProperty(value = "状态 0=待到达 1=待处理 2=已完成 3=已取消") private Integer status; @ApiModelProperty(value = "可以操作到达的配置范围,单位米") private Double canArriveRange = 200D; @ApiModelProperty(value = "处理人id") private String handleUserId; } xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpTaskController.java
@@ -8,10 +8,9 @@ import com.xiaopeng.api.model.account.XpOperationsUser; import com.xiaopeng.api.model.maintenance.XpErrorType; import com.xiaopeng.api.model.maintenance.XpTask; import com.xiaopeng.api.model.maintenance.XpTaskHandleDetailRoutine; import com.xiaopeng.api.model.maintenance.XpTaskRule; import com.xiaopeng.api.model.order.XpCruiseOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.TaskVO; import com.xiaopeng.api.vo.account.LoginOperationUser; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.common.core.domain.R; @@ -40,7 +39,6 @@ import org.apache.shardingsphere.transaction.core.TransactionType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.transaction.annotation.Transactional; @@ -101,6 +99,13 @@ return R.ok(pageList); } @ApiOperation(value = "接收工单", tags = "运维APP-任务中心") @PostMapping("/maintenance-app/api/v1/task/accept") public R<?> accept(@RequestBody TaskVO taskVO) { xpTaskService.accept(taskVO); return R.ok(); } @ApiOperation(value = "工单改派", tags = "运维APP-任务中心") @PostMapping("/maintenance-app/api/v1/task/reassign") public R<?> reassign(@RequestParam("taskId") Long taskId, @RequestParam("comment") String comment) { xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/XpTaskService.java
@@ -3,6 +3,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.account.XpOperationsUser; import com.xiaopeng.api.model.maintenance.XpTask; import com.xiaopeng.api.vo.TaskVO; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.maintenance.dto.HandleVehicleAccidentDto; @@ -98,4 +99,6 @@ void reassign(Long taskId, String msg); void saveVehicleAccidentError(Long vehicleI); void accept(TaskVO taskVO); } xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java
@@ -1,5 +1,6 @@ package com.xiaopeng.maintenance.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; @@ -13,6 +14,7 @@ import com.xiaopeng.api.feignClient.account.XpOperationUserClient; import com.xiaopeng.api.feignClient.account.XpOperationUserNotificationClient; import com.xiaopeng.api.feignClient.operation.SmsClient; import com.xiaopeng.api.feignClient.pushMessage.WebSocketMessageClient; import com.xiaopeng.api.feignClient.pushMessage.XpAliPushMessageClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient; @@ -23,6 +25,7 @@ import com.xiaopeng.api.model.account.XpOperationsUserNotification; import com.xiaopeng.api.model.maintenance.*; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.TaskVO; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; import com.xiaopeng.api.vo.maintenance.TaskScheduleConstants; import com.xiaopeng.api.vo.operation.SmsVo; @@ -75,6 +78,7 @@ import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.time.Duration; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; @@ -420,6 +424,10 @@ log.info("事故通知记录保存成功,共 {} 条", notifications.size()); } TaskVO taskVO = new TaskVO(); BeanUtil.copyProperties(xpTask, taskVO); xpWebSocketMessageClient.pushTask(taskVO); Map<Integer, List<XpOperationsUser>> operationUserMap = data.stream() .filter(u -> u.getDeviceType() != null && StringUtils.isNotBlank(u.getPhoneNumber())) .collect(Collectors.groupingBy(XpOperationsUser::getDeviceType)); @@ -428,6 +436,7 @@ } } WebSocketMessageClient xpWebSocketMessageClient; /** * 推送阿里云通知 * @@ -1398,7 +1407,7 @@ task.setHandleUserId(handlerUser); if(handlerUser == null) { task.setStatus(4); noticeAdministrator(task); noticeAdministrator(task);//TODO 需要通知管理员手动处理,考虑再AliPushUtil 中给管理员发送消息 } updateById(task); // 存储事故处理日志 @@ -1462,6 +1471,14 @@ sendAccidentNotificationAsync(task, vehicle.getPlateNumber(), taskRule.getProcessingTimeLimit()); } @Override @Transactional public void accept(TaskVO taskVO) { updateTaskStatus(taskVO.getId(), TaskScheduleConstants.TASK_STATUS_PENDING_ARRIVE, Long.valueOf(taskVO.getHandleUserId())); sendAccidentNotificationAsync(getById(taskVO.getId()), taskVO.getPlateNumber(), (int) Duration.between(LocalDateTime.now(),taskVO.getEndTime()).toMinutes()); } @Value("${robotaxi.sms.app-id:1476153389173952512}") private String smsAppId; @Value("${robotaxi.sms.app-push-config-code:ROBOTAXI_VERIFY_CODE_SMS}") xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/webSocket/controller/WebSocketController.java
@@ -1,6 +1,11 @@ package com.xiaopeng.pushMessage.webSocket.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.xiaopeng.api.dto.pushMessage.SendMessageDTO; import com.xiaopeng.api.vo.TaskVO; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.web.controller.BaseController; import com.xiaopeng.pushMessage.webSocket.service.WebSocketUserService; @@ -10,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; import java.util.List; import java.util.Map; import java.util.Set; @@ -27,6 +33,16 @@ @Autowired private WebSocketUserService webSocketUserService; private ObjectMapper om; @PostConstruct void setupObjectMapper() { // 允许对象忽略json中不存在的属性 om = new ObjectMapper(); om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); om.registerModule(new JavaTimeModule()); } // ==================== 基于XpAppUser的WebSocket接口 ==================== @@ -355,4 +371,18 @@ return R.fail("获取在线用户列表失败: " + e.getMessage()); } } /** * 运维工单app推送 */ @PostMapping("/inner/websocket/push/task") public R<?> pushTask(@RequestBody TaskVO vo) throws JsonProcessingException { String handleUserId = vo.getHandleUserId(); String msg = om.writeValueAsString(vo); webSocketUserService.sendMessageToUser(handleUserId, msg); return R.ok(); } } xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -1099,5 +1099,6 @@ public R<String> generateBluetoothUnlockToken(){ return null; } }