添加车辆实时轨迹接口
This commit is contained in:
@ -137,6 +137,7 @@ security:
|
|||||||
- /facility/matrix/**
|
- /facility/matrix/**
|
||||||
- /hat/device/data
|
- /hat/device/data
|
||||||
- /websocket/ue
|
- /websocket/ue
|
||||||
|
- /websocket/vehicle
|
||||||
|
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
|
|||||||
@ -92,6 +92,11 @@ public class GpsEquipmentController extends BaseController {
|
|||||||
return gpsEquipmentService.queryPageList(bo, pageQuery);
|
return gpsEquipmentService.queryPageList(bo, pageQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询GPS设备详细列表给车辆
|
||||||
|
* @param bo
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@GetMapping("/getListToVehicle")
|
@GetMapping("/getListToVehicle")
|
||||||
public R<List<GpsEquipmentVo>> getListToVehicle(GpsEquipmentBo bo) {
|
public R<List<GpsEquipmentVo>> getListToVehicle(GpsEquipmentBo bo) {
|
||||||
return R.ok(gpsEquipmentService.getListToVehicle(bo));
|
return R.ok(gpsEquipmentService.getListToVehicle(bo));
|
||||||
|
|||||||
@ -87,4 +87,5 @@ public interface IGpsEquipmentSonService extends IService<GpsEquipmentSon>{
|
|||||||
List<GpsEquipmentSonVo> getUeUserListByProjectId(LocalDateTime startOfDay, LocalDateTime now);
|
List<GpsEquipmentSonVo> getUeUserListByProjectId(LocalDateTime startOfDay, LocalDateTime now);
|
||||||
|
|
||||||
List<GpsEquipmentSonVo> getVehicleList(GpsEquipmentSonBo bo);
|
List<GpsEquipmentSonVo> getVehicleList(GpsEquipmentSonBo bo);
|
||||||
|
List<GpsEquipmentSonVo> getNewVehicleList(GpsEquipmentSonBo bo);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import org.dromara.project.domain.vo.project.BusProjectVo;
|
|||||||
import org.dromara.project.service.IBusProjectService;
|
import org.dromara.project.service.IBusProjectService;
|
||||||
import org.dromara.system.domain.vo.SysUserVo;
|
import org.dromara.system.domain.vo.SysUserVo;
|
||||||
import org.dromara.system.service.ISysUserService;
|
import org.dromara.system.service.ISysUserService;
|
||||||
|
import org.dromara.websocket.websocket.service.VehicleWebSocketServer;
|
||||||
import org.redisson.api.DeletedObjectListener;
|
import org.redisson.api.DeletedObjectListener;
|
||||||
import org.redisson.api.ExpiredObjectListener;
|
import org.redisson.api.ExpiredObjectListener;
|
||||||
import org.redisson.api.listener.SetObjectListener;
|
import org.redisson.api.listener.SetObjectListener;
|
||||||
@ -263,7 +264,6 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
|
|||||||
|
|
||||||
gpsEquipmentSonService.insertByBo(gpsEquipmentSon);
|
gpsEquipmentSonService.insertByBo(gpsEquipmentSon);
|
||||||
|
|
||||||
//TODO 后续创建websocket长连接发送信息
|
|
||||||
|
|
||||||
//保存到redis,如果存在则更新存活时间
|
//保存到redis,如果存在则更新存活时间
|
||||||
|
|
||||||
@ -272,14 +272,26 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
|
|||||||
// --------------------------
|
// --------------------------
|
||||||
// 1. 构造需要推送的消息内容(String类型)
|
// 1. 构造需要推送的消息内容(String类型)
|
||||||
//判断是否有连接
|
//判断是否有连接
|
||||||
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
|
if (equipment != null && StringUtils.isNotEmpty(equipment.getModelId())) {
|
||||||
if (onlineCount > 0){
|
int onlineCount = InitOnStartWebSocketServer.getOnlineCount();
|
||||||
if (equipment != null && StringUtils.isNotEmpty(equipment.getModelId())) {
|
if (onlineCount > 0){
|
||||||
String ued = ueStructureJsonMessage(gpsEquipmentSon, equipment.getModelId());
|
String ued = ueStructureJsonMessage(gpsEquipmentSon, equipment.getModelId());
|
||||||
InitOnStartWebSocketServer.sendToAll(ued);
|
InitOnStartWebSocketServer.sendToAll(ued);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//判断车辆轨迹是否有连接
|
||||||
|
if (equipment != null && equipment.getClientType() == 1 && equipment.getUserId() != null) {
|
||||||
|
int onlineCount1 = VehicleWebSocketServer.getOnlineCount();
|
||||||
|
if (onlineCount1 > 0) {
|
||||||
|
String vehicled = vehicleStructureJsonMessage(gpsEquipmentSon);
|
||||||
|
VehicleWebSocketServer.sendToSubscription(gpsEquipmentSon.getUserId()+"-"+gpsEquipmentSon.getTripId(), vehicled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll();
|
Set<Long> sessionsAll = WebSocketSessionHolder.getSessionsAll();
|
||||||
|
|
||||||
if (!sessionsAll.isEmpty()) {
|
if (!sessionsAll.isEmpty()) {
|
||||||
String pushContent = buildPushMessage(gpsEquipmentSon);
|
String pushContent = buildPushMessage(gpsEquipmentSon);
|
||||||
|
|
||||||
@ -348,6 +360,19 @@ public class GpsEquipmentServiceImpl extends ServiceImpl<GpsEquipmentMapper, Gps
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建推送消息内容(String类型)
|
||||||
|
*/
|
||||||
|
private String vehicleStructureJsonMessage(GpsEquipmentSonBo sonBo) {
|
||||||
|
// 构造消息对象(包含关键信息)
|
||||||
|
JSONObject messageObj = new JSONObject();
|
||||||
|
messageObj.put("locLatitude", sonBo.getLocLatitude().toString()); // 消息类型
|
||||||
|
messageObj.put("locLongitude", sonBo.getLocLongitude().toString()); // 消息类型
|
||||||
|
// 转换为String类型返回
|
||||||
|
return messageObj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final int DEVICE_ALIVE_TIMEOUT = 120; // 5分钟
|
private static final int DEVICE_ALIVE_TIMEOUT = 120; // 5分钟
|
||||||
/**
|
/**
|
||||||
* 更新设备存活状态到Redis并添加过期监听
|
* 更新设备存活状态到Redis并添加过期监听
|
||||||
|
|||||||
@ -239,4 +239,12 @@ public class GpsEquipmentSonServiceImpl extends ServiceImpl<GpsEquipmentSonMappe
|
|||||||
.eq(bo.getTripId() != null,GpsEquipmentSon::getTripId,bo.getTripId())
|
.eq(bo.getTripId() != null,GpsEquipmentSon::getTripId,bo.getTripId())
|
||||||
.between(GpsEquipmentSon::getCreateTime,bo.getStartTime(),bo.getEndTime()));
|
.between(GpsEquipmentSon::getCreateTime,bo.getStartTime(),bo.getEndTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GpsEquipmentSonVo> getNewVehicleList(GpsEquipmentSonBo bo) {
|
||||||
|
return baseMapper.selectVoList(new LambdaQueryWrapper<GpsEquipmentSon>()
|
||||||
|
.eq(GpsEquipmentSon::getUserId,bo.getUserId())
|
||||||
|
.eq(GpsEquipmentSon::getTripId,bo.getTripId())
|
||||||
|
.orderByDesc(GpsEquipmentSon::getCreateTime));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
|
|||||||
import org.dromara.common.web.core.BaseController;
|
import org.dromara.common.web.core.BaseController;
|
||||||
import org.dromara.vehicle.domain.bo.VehVehicleInfoBo;
|
import org.dromara.vehicle.domain.bo.VehVehicleInfoBo;
|
||||||
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
|
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
|
||||||
|
import org.dromara.vehicle.domain.vo.VehVehicleTripVo;
|
||||||
import org.dromara.vehicle.service.IVehVehicleInfoService;
|
import org.dromara.vehicle.service.IVehVehicleInfoService;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@ -45,6 +46,17 @@ public class VehVehicleInfoController extends BaseController {
|
|||||||
public TableDataInfo<VehVehicleInfoVo> list(VehVehicleInfoBo bo, PageQuery pageQuery) {
|
public TableDataInfo<VehVehicleInfoVo> list(VehVehicleInfoBo bo, PageQuery pageQuery) {
|
||||||
return vehVehicleInfoService.queryPageList(bo, pageQuery);
|
return vehVehicleInfoService.queryPageList(bo, pageQuery);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取车辆行程信息
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
*/
|
||||||
|
// @SaCheckPermission("vehicle:vehicleInfo:query")
|
||||||
|
@GetMapping("/getTrip/{id}")
|
||||||
|
public R<VehVehicleTripVo> getTripInfo(@NotNull(message = "主键不能为空")
|
||||||
|
@PathVariable Long id) {
|
||||||
|
return R.ok(vehVehicleInfoService.getTripInfo(id));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询车辆信息列表
|
* 查询车辆信息列表
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package org.dromara.vehicle.service;
|
package org.dromara.vehicle.service;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
|
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
|
||||||
import org.dromara.vehicle.domain.bo.VehVehicleInfoBo;
|
import org.dromara.vehicle.domain.bo.VehVehicleInfoBo;
|
||||||
import org.dromara.vehicle.domain.VehVehicleInfo;
|
import org.dromara.vehicle.domain.VehVehicleInfo;
|
||||||
@ -7,6 +8,8 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
|
|||||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import org.dromara.vehicle.domain.vo.VehVehicleTripVo;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -81,4 +84,6 @@ public interface IVehVehicleInfoService extends IService<VehVehicleInfo>{
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int unBindClient(VehVehicleInfoBo bo);
|
int unBindClient(VehVehicleInfoBo bo);
|
||||||
|
|
||||||
|
VehVehicleTripVo getTripInfo(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,4 +107,11 @@ public interface IVehVehicleTripService extends IService<VehVehicleTrip> {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Long getTripId(Long id);
|
Long getTripId(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据车辆id获取最新行程信息
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
VehVehicleTripVo getTripInfoByVehicleId(Long id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,9 +18,12 @@ import org.dromara.gps.service.IGpsEquipmentService;
|
|||||||
import org.dromara.gps.service.IGpsManmachineService;
|
import org.dromara.gps.service.IGpsManmachineService;
|
||||||
import org.dromara.vehicle.domain.VehVehicleInfo;
|
import org.dromara.vehicle.domain.VehVehicleInfo;
|
||||||
import org.dromara.vehicle.domain.bo.VehVehicleInfoBo;
|
import org.dromara.vehicle.domain.bo.VehVehicleInfoBo;
|
||||||
|
import org.dromara.vehicle.domain.enums.VehVehicleInfoStatusEnum;
|
||||||
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
|
import org.dromara.vehicle.domain.vo.VehVehicleInfoVo;
|
||||||
|
import org.dromara.vehicle.domain.vo.VehVehicleTripVo;
|
||||||
import org.dromara.vehicle.mapper.VehVehicleInfoMapper;
|
import org.dromara.vehicle.mapper.VehVehicleInfoMapper;
|
||||||
import org.dromara.vehicle.service.IVehVehicleInfoService;
|
import org.dromara.vehicle.service.IVehVehicleInfoService;
|
||||||
|
import org.dromara.vehicle.service.IVehVehicleTripService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -50,6 +53,10 @@ public class VehVehicleInfoServiceImpl extends ServiceImpl<VehVehicleInfoMapper,
|
|||||||
@Lazy
|
@Lazy
|
||||||
private IGpsManmachineService gpsManmachineService;
|
private IGpsManmachineService gpsManmachineService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Lazy
|
||||||
|
private IVehVehicleTripService vehVehicleTripService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询车辆信息
|
* 查询车辆信息
|
||||||
*
|
*
|
||||||
@ -209,4 +216,22 @@ public class VehVehicleInfoServiceImpl extends ServiceImpl<VehVehicleInfoMapper,
|
|||||||
.eq(GpsManmachine::getUserId, bo.getId()));
|
.eq(GpsManmachine::getUserId, bo.getId()));
|
||||||
return baseMapper.update(new LambdaUpdateWrapper<VehVehicleInfo>().set(VehVehicleInfo::getClientId,null).eq(VehVehicleInfo::getId,bo.getId()));
|
return baseMapper.update(new LambdaUpdateWrapper<VehVehicleInfo>().set(VehVehicleInfo::getClientId,null).eq(VehVehicleInfo::getId,bo.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过车辆id获取车辆的当前行程信息
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public VehVehicleTripVo getTripInfo(Long id) {
|
||||||
|
VehVehicleInfoVo vehVehicleInfoVo = baseMapper.selectVoById(id);
|
||||||
|
if (vehVehicleInfoVo == null) {
|
||||||
|
throw new ServiceException("车辆信息为空");
|
||||||
|
}
|
||||||
|
if (!VehVehicleInfoStatusEnum.IN_USE.getValue().equals(vehVehicleInfoVo.getVehicleStatus())){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vehVehicleTripService.getTripInfoByVehicleId(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -476,6 +476,14 @@ public class VehVehicleTripServiceImpl extends ServiceImpl<VehVehicleTripMapper,
|
|||||||
return baseMapper.getTripId(id);
|
return baseMapper.getTripId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VehVehicleTripVo getTripInfoByVehicleId(Long id) {
|
||||||
|
return baseMapper.selectVoOne(new LambdaQueryWrapper<VehVehicleTrip>()
|
||||||
|
.eq(VehVehicleTrip::getVehicleId, id)
|
||||||
|
.orderByDesc(VehVehicleTrip::getCreateTime)
|
||||||
|
.last("limit 1"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建用户行程及其对应申请的展示列表
|
* 构建用户行程及其对应申请的展示列表
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
package org.dromara.websocket.websocket.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||||
|
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class VehicleVo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 纬度(精确到6位小数)
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "纬度", converter = ExcelDictConvert.class)
|
||||||
|
@ExcelDictFormat(readConverterExp = "精=确到6位小数")
|
||||||
|
private BigDecimal locLatitude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 经度(精确到6位小数)
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "经度", converter = ExcelDictConvert.class)
|
||||||
|
@ExcelDictFormat(readConverterExp = "精=确到6位小数")
|
||||||
|
private BigDecimal locLongitude;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,227 @@
|
|||||||
|
package org.dromara.websocket.websocket.service;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import jakarta.websocket.*;
|
||||||
|
import jakarta.websocket.server.ServerEndpoint;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.bigscreen.service.ProjectBigScreenService;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.gps.domain.bo.GpsEquipmentSonBo;
|
||||||
|
import org.dromara.gps.domain.vo.GpsEquipmentSonVo;
|
||||||
|
import org.dromara.gps.service.IGpsEquipmentSonService;
|
||||||
|
import org.dromara.websocket.websocket.domain.vo.VehicleVo;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 车辆轨迹 WebSocket 服务端(支持订阅消息)
|
||||||
|
* 端点路径:/websocket/vehicle
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@ServerEndpoint("/websocket/vehicle") // 客户端连接时需携带订阅ID参数:ws://xxx/websocket/vehicle?subscriptionId=xxx
|
||||||
|
@Component
|
||||||
|
public class VehicleWebSocketServer {
|
||||||
|
|
||||||
|
// 1. 存储所有在线会话(sessionId -> Session)
|
||||||
|
private static final Map<String, Session> ONLINE_SESSIONS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 2. 核心:订阅ID与会话的映射(subscriptionId -> Session)
|
||||||
|
private static final Map<String, Session> SUBSCRIPTION_SESSIONS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 3. 反向映射:会话ID与订阅ID的映射(用于断开连接时清理订阅关系)
|
||||||
|
private static final Map<String, String> SESSION_TO_SUBSCRIPTION = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 当前会话对应的订阅ID(每个连接实例的专属变量)
|
||||||
|
private String currentSubscriptionId;
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
log.info("✅ 车辆轨迹 WebSocket 服务端已随项目启动初始化!端点路径:/websocket/vehicle");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端连接时触发(解析订阅ID并建立映射关系)
|
||||||
|
*/
|
||||||
|
@OnOpen
|
||||||
|
public void onOpen(Session session) {
|
||||||
|
// 从连接URL的查询参数中获取订阅ID(客户端连接格式:ws://xxx/websocket/vehicle?subscriptionId=123)
|
||||||
|
Map<String, List<String>> params = session.getRequestParameterMap();
|
||||||
|
List<String> subscriptionIds = params.get("subscriptionId");
|
||||||
|
if (subscriptionIds != null && !subscriptionIds.isEmpty()) {
|
||||||
|
this.currentSubscriptionId = subscriptionIds.get(0); // 取第一个订阅ID
|
||||||
|
// 建立映射关系
|
||||||
|
SUBSCRIPTION_SESSIONS.put(currentSubscriptionId, session);
|
||||||
|
SESSION_TO_SUBSCRIPTION.put(session.getId(), currentSubscriptionId);
|
||||||
|
log.info("📌 客户端订阅成功!订阅ID:{},会话ID:{},当前订阅数:{}",
|
||||||
|
currentSubscriptionId, session.getId(), SUBSCRIPTION_SESSIONS.size());
|
||||||
|
} else {
|
||||||
|
log.warn("📌 客户端连接未携带订阅ID!会话ID:{}", session.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储会话到在线列表
|
||||||
|
ONLINE_SESSIONS.put(session.getId(), session);
|
||||||
|
log.info("📌 客户端连接成功!会话ID:{},当前在线数:{}", session.getId(), ONLINE_SESSIONS.size());
|
||||||
|
|
||||||
|
// 异步推送初始化数据(原有逻辑保留)
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
String[] split = currentSubscriptionId.split("-");
|
||||||
|
IGpsEquipmentSonService service = SpringUtils.getBean(IGpsEquipmentSonService.class);
|
||||||
|
GpsEquipmentSonBo bo = new GpsEquipmentSonBo();
|
||||||
|
bo.setUserId(Long.parseLong(split[0]));
|
||||||
|
bo.setTripId(Long.parseLong(split[1]));
|
||||||
|
List<GpsEquipmentSonVo> list = service.getNewVehicleList(bo);
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
session.getBasicRemote().sendText("初始化数据为空");
|
||||||
|
log.warn("会话[{}]未获取到初始化数据", session.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<VehicleVo> vehicleVos = new ArrayList<>();
|
||||||
|
for (GpsEquipmentSonVo ueClient : list) {
|
||||||
|
VehicleVo vo = new VehicleVo();
|
||||||
|
vo.setLocLatitude(ueClient.getLocLatitude());
|
||||||
|
vo.setLocLongitude(ueClient.getLocLongitude());
|
||||||
|
vehicleVos.add(vo);
|
||||||
|
}
|
||||||
|
session.getBasicRemote().sendText(JSONUtil.toJsonStr(vehicleVos));
|
||||||
|
log.info("📤 已向会话[{}]推送初始化数据,长度:{}字节", session.getId(), vehicleVos.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("会话[{}]初始化数据处理失败", session.getId(), e);
|
||||||
|
try {
|
||||||
|
if (session.isOpen()) {
|
||||||
|
session.getBasicRemote().sendText("初始化失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
log.error("会话[{}]推送错误信息失败", session.getId(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收客户端消息
|
||||||
|
*/
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(String message, Session session) {
|
||||||
|
log.info("📥 收到会话[{}](订阅ID:{})消息:{}", session.getId(), currentSubscriptionId, message);
|
||||||
|
// 可选:回复客户端
|
||||||
|
try {
|
||||||
|
session.getBasicRemote().sendText("服务端已收到消息:" + message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("📤 回复会话[{}]失败:{}", session.getId(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端断开连接(清理订阅关系)
|
||||||
|
*/
|
||||||
|
@OnClose
|
||||||
|
public void onClose(Session session, CloseReason reason) {
|
||||||
|
// 1. 移除在线会话
|
||||||
|
ONLINE_SESSIONS.remove(session.getId());
|
||||||
|
// 2. 清理订阅关系
|
||||||
|
String subscriptionId = SESSION_TO_SUBSCRIPTION.get(session.getId());
|
||||||
|
if (subscriptionId != null) {
|
||||||
|
SUBSCRIPTION_SESSIONS.remove(subscriptionId);
|
||||||
|
SESSION_TO_SUBSCRIPTION.remove(session.getId());
|
||||||
|
log.info("🔌 客户端订阅关系已清除!订阅ID:{},会话ID:{}", subscriptionId, session.getId());
|
||||||
|
}
|
||||||
|
log.info("🔌 客户端断开连接!会话ID:{},原因:{},当前在线数:{},当前订阅数:{}",
|
||||||
|
session.getId(), reason.getReasonPhrase(),
|
||||||
|
ONLINE_SESSIONS.size(), SUBSCRIPTION_SESSIONS.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接异常
|
||||||
|
*/
|
||||||
|
@OnError
|
||||||
|
public void onError(Session session, Throwable error) {
|
||||||
|
log.error("⚠️ 会话[{}](订阅ID:{})异常:{}", session.getId(), currentSubscriptionId, error.getMessage(), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------ 订阅消息发送工具方法(供外部调用) ------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向指定订阅ID的客户端发送消息
|
||||||
|
* @param subscriptionId 订阅ID
|
||||||
|
* @param message 消息内容
|
||||||
|
* @return 是否发送成功
|
||||||
|
*/
|
||||||
|
public static boolean sendToSubscription(String subscriptionId, String message) {
|
||||||
|
if (subscriptionId == null || message == null) {
|
||||||
|
log.warn("⚠️ 订阅ID或消息为空,发送失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 从订阅映射中获取目标会话
|
||||||
|
Session session = SUBSCRIPTION_SESSIONS.get(subscriptionId);
|
||||||
|
if (session == null || !session.isOpen()) {
|
||||||
|
log.warn("⚠️ 订阅ID[{}]对应的客户端未连接或已断开", subscriptionId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 发送消息
|
||||||
|
try {
|
||||||
|
session.getBasicRemote().sendText(message);
|
||||||
|
log.info("📤 已向订阅ID[{}]发送消息:{}", subscriptionId, message);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("📤 向订阅ID[{}]发送消息失败", subscriptionId, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向所有订阅客户端广播消息
|
||||||
|
* @param message 消息内容
|
||||||
|
*/
|
||||||
|
public static void broadcastToAllSubscriptions(String message) {
|
||||||
|
if (SUBSCRIPTION_SESSIONS.isEmpty()) {
|
||||||
|
log.warn("⚠️ 无订阅客户端,无需广播消息");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SUBSCRIPTION_SESSIONS.forEach((subscriptionId, session) -> {
|
||||||
|
if (session.isOpen()) {
|
||||||
|
try {
|
||||||
|
session.getBasicRemote().sendText(message);
|
||||||
|
log.info("📤 已向订阅ID[{}]广播消息", subscriptionId);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("📤 向订阅ID[{}]广播消息失败", subscriptionId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前订阅数
|
||||||
|
*/
|
||||||
|
public static int getSubscriptionCount() {
|
||||||
|
return SUBSCRIPTION_SESSIONS.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原有工具方法保留
|
||||||
|
public static void sendToAll(String message) {
|
||||||
|
if (ONLINE_SESSIONS.isEmpty()) {
|
||||||
|
log.warn("⚠️ 无在线客户端,无需发送消息");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ONLINE_SESSIONS.values().forEach(session -> {
|
||||||
|
if (session.isOpen()) {
|
||||||
|
try {
|
||||||
|
session.getBasicRemote().sendText(message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("📤 向会话[{}]发送消息失败:{}", session.getId(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getOnlineCount() {
|
||||||
|
return ONLINE_SESSIONS.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,7 +15,9 @@ import org.dromara.xzd.comprehensive.service.IXzdCsContractChangeService;
|
|||||||
import org.dromara.xzd.comprehensive.service.IXzdCsContractSuspendService;
|
import org.dromara.xzd.comprehensive.service.IXzdCsContractSuspendService;
|
||||||
import org.dromara.xzd.domain.bo.XzdBusinessSealBo;
|
import org.dromara.xzd.domain.bo.XzdBusinessSealBo;
|
||||||
import org.dromara.xzd.domain.vo.XzdBusinessSealVo;
|
import org.dromara.xzd.domain.vo.XzdBusinessSealVo;
|
||||||
|
import org.dromara.xzd.domain.vo.XzdProjectVo;
|
||||||
import org.dromara.xzd.service.IXzdBusinessSealService;
|
import org.dromara.xzd.service.IXzdBusinessSealService;
|
||||||
|
import org.dromara.xzd.service.IXzdProjectService;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@ -55,6 +57,23 @@ public class XzdCsContractInformationController extends BaseController {
|
|||||||
@Lazy
|
@Lazy
|
||||||
private final IXzdCsContractChangeService xzdCsContractChangeService;
|
private final IXzdCsContractChangeService xzdCsContractChangeService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
private final IXzdProjectService xzdProjectService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目信息详细信息
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
*/
|
||||||
|
@SaCheckPermission(value = {"comprehensive:csContractInformation:add","comprehensive:csContractInformation:edit","comprehensive:csContractInformation:list"},mode = SaMode.OR)
|
||||||
|
@GetMapping("/getProject/{id}")
|
||||||
|
public R<XzdProjectVo> getProjectInfo(@NotNull(message = "主键不能为空")
|
||||||
|
@PathVariable Long id) {
|
||||||
|
return R.ok(xzdProjectService.queryById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询综合服务合同变更列表
|
* 查询综合服务合同变更列表
|
||||||
|
|||||||
Reference in New Issue
Block a user