websocket日志
This commit is contained in:
parent
c4a32f3acb
commit
5cd7f39d6e
|
|
@ -0,0 +1,5 @@
|
|||
package com.ruoyi.device.domain.impl.tuohengmqtt.callback;
|
||||
|
||||
public interface IAirportFlyControlDataCallback {
|
||||
void onAirportFlyControlData(String deviceSn, String payload, String topic);
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.ruoyi.device.domain.impl.machine.mqtt.MqttCallbackRegistry;
|
|||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IDroneRealTimeCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IHeartbeatMessageCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IRealTimeBasicCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlDataCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengEventsCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengOsdCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengRealTimeDataCallback;
|
||||
|
|
@ -34,6 +35,7 @@ public class TuohengMqttMessageHandler {
|
|||
private final List<IRealTimeBasicCallback> realTimeBasicCallbacks = new ArrayList<>();
|
||||
private final List<IDroneRealTimeCallback> droneRealTimeCallbacks = new ArrayList<>();
|
||||
private final List<IHeartbeatMessageCallback> heartbeatMessageCallbacks = new ArrayList<>();
|
||||
private final List<com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlDataCallback> airportFlyControlDataCallbacks = new ArrayList<>();
|
||||
|
||||
private static final Pattern TUOHENG_SN_PATTERN = Pattern.compile("^TH[0-9A-Z]+");
|
||||
|
||||
|
|
@ -87,6 +89,13 @@ public class TuohengMqttMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
public void registerAirportFlyControlDataCallback(IAirportFlyControlDataCallback callback) {
|
||||
if (callback != null && !airportFlyControlDataCallbacks.contains(callback)) {
|
||||
airportFlyControlDataCallbacks.add(callback);
|
||||
log.info("注册机场飞行控制数据回调: {}", callback.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
public void handleMessage(String topic, String payload) {
|
||||
try {
|
||||
log.debug("收到MQTT消息 - Topic: {}", topic);
|
||||
|
|
@ -149,6 +158,9 @@ public class TuohengMqttMessageHandler {
|
|||
case "events":
|
||||
handleEventsData(deviceSn, payload);
|
||||
break;
|
||||
case "control/data":
|
||||
handleAirportFlyControlData(deviceSn, payload, topic);
|
||||
break;
|
||||
default:
|
||||
log.debug("未知消息类型: {}", messageType);
|
||||
}
|
||||
|
|
@ -254,6 +266,22 @@ public class TuohengMqttMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleAirportFlyControlData(String deviceSn, String payload, String topic) {
|
||||
try {
|
||||
log.debug("处理机场飞行控制数据 - 设备SN: {}, Topic: {}", deviceSn, topic);
|
||||
|
||||
for (com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlDataCallback callback : airportFlyControlDataCallbacks) {
|
||||
try {
|
||||
callback.onAirportFlyControlData(deviceSn, payload, topic);
|
||||
} catch (Exception e) {
|
||||
log.error("机场飞行控制数据回调执行失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理机场飞行控制数据失败 - SN: {}, Error: {}", deviceSn, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String extractDeviceSnFromTopic(String topic) {
|
||||
if (topic == null) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ public class DroneRealTimeData {
|
|||
@JsonProperty("code")
|
||||
private Integer code;
|
||||
|
||||
@JsonProperty("messageID")
|
||||
private String messageID;
|
||||
|
||||
@JsonProperty("data")
|
||||
private DroneInfo data;
|
||||
|
||||
|
|
@ -142,5 +145,8 @@ public class DroneRealTimeData {
|
|||
|
||||
@JsonProperty("flowRate")
|
||||
private String flowRate;
|
||||
|
||||
@JsonProperty("jiancha")
|
||||
private String jiancha;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package com.ruoyi.device.mapper;
|
||||
|
||||
import com.ruoyi.device.mapper.entity.FlightLogEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 飞行日志表Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Mapper
|
||||
public interface FlightLogMapper
|
||||
{
|
||||
/**
|
||||
* 根据日志主键查询飞行日志
|
||||
*
|
||||
* @param logId 日志主键
|
||||
* @return 飞行日志信息
|
||||
*/
|
||||
FlightLogEntity selectFlightLogByLogId(Long logId);
|
||||
|
||||
/**
|
||||
* 根据飞行ID查询飞行日志列表
|
||||
*
|
||||
* @param flightId 飞行ID
|
||||
* @return 飞行日志集合
|
||||
*/
|
||||
List<FlightLogEntity> selectFlightLogListByFlightId(Long flightId);
|
||||
|
||||
/**
|
||||
* 新增飞行日志
|
||||
*
|
||||
* @param flightLog 飞行日志信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insertFlightLog(FlightLogEntity flightLog);
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.ruoyi.device.mapper;
|
||||
|
||||
import com.ruoyi.device.mapper.entity.FlightEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 飞行表Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Mapper
|
||||
public interface FlightMapper
|
||||
{
|
||||
/**
|
||||
* 根据飞行主键查询飞行记录
|
||||
*
|
||||
* @param flightId 飞行主键
|
||||
* @return 飞行信息
|
||||
*/
|
||||
FlightEntity selectFlightByFlightId(Long flightId);
|
||||
|
||||
/**
|
||||
* 根据设备SN查询最新的飞行记录
|
||||
*
|
||||
* @param deviceSn 设备SN号
|
||||
* @return 飞行信息
|
||||
*/
|
||||
FlightEntity selectLatestFlightByDeviceSn(String deviceSn);
|
||||
|
||||
/**
|
||||
* 根据设备SN和状态查询飞行记录
|
||||
*
|
||||
* @param deviceSn 设备SN号
|
||||
* @param status 状态
|
||||
* @return 飞行信息
|
||||
*/
|
||||
FlightEntity selectFlightByDeviceSnAndStatus(@Param("deviceSn") String deviceSn, @Param("status") String status);
|
||||
|
||||
/**
|
||||
* 新增飞行记录
|
||||
*
|
||||
* @param flight 飞行信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insertFlight(FlightEntity flight);
|
||||
|
||||
/**
|
||||
* 修改飞行记录
|
||||
*
|
||||
* @param flight 飞行信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int updateFlight(FlightEntity flight);
|
||||
|
||||
/**
|
||||
* 更新飞行状态
|
||||
*
|
||||
* @param flightId 飞行主键
|
||||
* @param status 状态
|
||||
* @return 影响行数
|
||||
*/
|
||||
int updateFlightStatus(@Param("flightId") Long flightId, @Param("status") String status);
|
||||
|
||||
/**
|
||||
* 更新返航时间
|
||||
*
|
||||
* @param flightId 飞行主键
|
||||
* @return 影响行数
|
||||
*/
|
||||
int updateReturnTime(Long flightId);
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.ruoyi.device.mapper;
|
||||
|
||||
import com.ruoyi.device.mapper.entity.PreCheckLogEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自检日志表Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Mapper
|
||||
public interface PreCheckLogMapper
|
||||
{
|
||||
/**
|
||||
* 根据日志主键查询自检日志
|
||||
*
|
||||
* @param logId 日志主键
|
||||
* @return 自检日志信息
|
||||
*/
|
||||
PreCheckLogEntity selectPreCheckLogByLogId(Long logId);
|
||||
|
||||
/**
|
||||
* 根据飞行ID查询自检日志列表
|
||||
*
|
||||
* @param flightId 飞行ID
|
||||
* @return 自检日志集合
|
||||
*/
|
||||
List<PreCheckLogEntity> selectPreCheckLogListByFlightId(Long flightId);
|
||||
|
||||
/**
|
||||
* 新增自检日志
|
||||
*
|
||||
* @param preCheckLog 自检日志信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insertPreCheckLog(PreCheckLogEntity preCheckLog);
|
||||
|
||||
/**
|
||||
* 批量新增自检日志
|
||||
*
|
||||
* @param preCheckLogList 自检日志集合
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insertPreCheckLogBatch(List<PreCheckLogEntity> preCheckLogList);
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.ruoyi.device.mapper.entity;
|
||||
|
||||
import com.ruoyi.common.core.web.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 飞行表实体对象 device_flight
|
||||
* Mapper 层实体,对应数据库表
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
public class FlightEntity extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 飞行主键 */
|
||||
private Long flightId;
|
||||
|
||||
/** 无人机SN */
|
||||
private String deviceSn;
|
||||
|
||||
/** 外部飞行ID (来自MQTT的taskId) */
|
||||
private String flightIdExternal;
|
||||
|
||||
/** 状态:自检中、飞行中、已返航 */
|
||||
private String status;
|
||||
|
||||
/** 返航时间 */
|
||||
private java.util.Date returnTime;
|
||||
|
||||
public Long getFlightId()
|
||||
{
|
||||
return flightId;
|
||||
}
|
||||
|
||||
public void setFlightId(Long flightId)
|
||||
{
|
||||
this.flightId = flightId;
|
||||
}
|
||||
|
||||
public String getDeviceSn()
|
||||
{
|
||||
return deviceSn;
|
||||
}
|
||||
|
||||
public void setDeviceSn(String deviceSn)
|
||||
{
|
||||
this.deviceSn = deviceSn;
|
||||
}
|
||||
|
||||
public String getFlightIdExternal()
|
||||
{
|
||||
return flightIdExternal;
|
||||
}
|
||||
|
||||
public void setFlightIdExternal(String flightIdExternal)
|
||||
{
|
||||
this.flightIdExternal = flightIdExternal;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public java.util.Date getReturnTime()
|
||||
{
|
||||
return returnTime;
|
||||
}
|
||||
|
||||
public void setReturnTime(java.util.Date returnTime)
|
||||
{
|
||||
this.returnTime = returnTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "FlightEntity{" +
|
||||
"flightId=" + flightId +
|
||||
", deviceSn='" + deviceSn + '\'' +
|
||||
", flightIdExternal='" + flightIdExternal + '\'' +
|
||||
", status='" + status + '\'' +
|
||||
", returnTime=" + returnTime +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.ruoyi.device.mapper.entity;
|
||||
|
||||
import com.ruoyi.common.core.web.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 飞行日志表实体对象 device_flight_log
|
||||
* Mapper 层实体,对应数据库表
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
public class FlightLogEntity extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 飞行日志主键 */
|
||||
private Long logId;
|
||||
|
||||
/** 关联飞行表ID */
|
||||
private Long flightId;
|
||||
|
||||
/** 日志内容 */
|
||||
private String logContent;
|
||||
|
||||
public Long getLogId()
|
||||
{
|
||||
return logId;
|
||||
}
|
||||
|
||||
public void setLogId(Long logId)
|
||||
{
|
||||
this.logId = logId;
|
||||
}
|
||||
|
||||
public Long getFlightId()
|
||||
{
|
||||
return flightId;
|
||||
}
|
||||
|
||||
public void setFlightId(Long flightId)
|
||||
{
|
||||
this.flightId = flightId;
|
||||
}
|
||||
|
||||
public String getLogContent()
|
||||
{
|
||||
return logContent;
|
||||
}
|
||||
|
||||
public void setLogContent(String logContent)
|
||||
{
|
||||
this.logContent = logContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "FlightLogEntity{" +
|
||||
"logId=" + logId +
|
||||
", flightId=" + flightId +
|
||||
", logContent='" + logContent + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.ruoyi.device.mapper.entity;
|
||||
|
||||
import com.ruoyi.common.core.web.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 自检日志表实体对象 device_pre_check_log
|
||||
* Mapper 层实体,对应数据库表
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
public class PreCheckLogEntity extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 自检日志主键 */
|
||||
private Long logId;
|
||||
|
||||
/** 关联飞行表ID */
|
||||
private Long flightId;
|
||||
|
||||
/** 日志内容 (JSON字符串) */
|
||||
private String logContent;
|
||||
|
||||
/** 是否成功 (true=成功, false=失败) */
|
||||
private Boolean success;
|
||||
|
||||
public Long getLogId()
|
||||
{
|
||||
return logId;
|
||||
}
|
||||
|
||||
public void setLogId(Long logId)
|
||||
{
|
||||
this.logId = logId;
|
||||
}
|
||||
|
||||
public Long getFlightId()
|
||||
{
|
||||
return flightId;
|
||||
}
|
||||
|
||||
public void setFlightId(Long flightId)
|
||||
{
|
||||
this.flightId = flightId;
|
||||
}
|
||||
|
||||
public String getLogContent()
|
||||
{
|
||||
return logContent;
|
||||
}
|
||||
|
||||
public void setLogContent(String logContent)
|
||||
{
|
||||
this.logContent = logContent;
|
||||
}
|
||||
|
||||
public Boolean getSuccess()
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(Boolean success)
|
||||
{
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "PreCheckLogEntity{" +
|
||||
"logId=" + logId +
|
||||
", flightId=" + flightId +
|
||||
", logContent='" + logContent + '\'' +
|
||||
", success=" + success +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package com.ruoyi.device.service;
|
||||
|
||||
import com.ruoyi.device.mapper.entity.FlightEntity;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 飞行服务接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
public interface FlightService
|
||||
{
|
||||
/**
|
||||
* 获取或创建当前正在进行的飞行记录
|
||||
* 用于MQTT回调,如果最新记录已返航则创建新记录
|
||||
*
|
||||
* @param deviceSn 设备SN号
|
||||
* @return 飞行记录
|
||||
*/
|
||||
FlightEntity getOrCreateCurrentFlight(String deviceSn);
|
||||
|
||||
/**
|
||||
* 获取或创建飞行记录(通过messageID匹配)
|
||||
* 如果存在相同messageID的飞行记录,返回该记录
|
||||
* 如果不存在或messageID不同,创建新记录
|
||||
*
|
||||
* @param deviceSn 设备SN号
|
||||
* @param messageId 消息ID(对应flightIdExternal)
|
||||
* @return 飞行记录
|
||||
*/
|
||||
FlightEntity getOrCreateFlightByMessageId(String deviceSn, String messageId);
|
||||
|
||||
/**
|
||||
* 获取最新的飞行记录(包括已返航的)
|
||||
* 用于WebSocket推送
|
||||
*
|
||||
* @param deviceSn 设备SN号
|
||||
* @return 飞行记录
|
||||
*/
|
||||
FlightEntity getLatestFlight(String deviceSn);
|
||||
|
||||
/**
|
||||
* 创建新的飞行记录
|
||||
*
|
||||
* @param deviceSn 设备SN号
|
||||
* @return 飞行记录
|
||||
*/
|
||||
FlightEntity createFlight(String deviceSn);
|
||||
|
||||
/**
|
||||
* 更新飞行ID(外部ID)
|
||||
*
|
||||
* @param flightId 飞行ID
|
||||
* @param flightIdExternal 外部飞行ID
|
||||
*/
|
||||
void updateFlightIdExternal(Long flightId, String flightIdExternal);
|
||||
|
||||
/**
|
||||
* 更新飞行状态
|
||||
*
|
||||
* @param flightId 飞行ID
|
||||
* @param status 状态:自检中、飞行中、已返航
|
||||
*/
|
||||
void updateFlightStatus(Long flightId, String status);
|
||||
|
||||
/**
|
||||
* 更新返航时间
|
||||
*
|
||||
* @param flightId 飞行ID
|
||||
*/
|
||||
void updateReturnTime(Long flightId);
|
||||
|
||||
/**
|
||||
* 获取飞行记录和日志(用于WebSocket推送)
|
||||
*
|
||||
* @param deviceSn 设备SN号
|
||||
* @return 飞行记录和日志
|
||||
*/
|
||||
Map<String, Object> getLatestFlightWithLogs(String deviceSn);
|
||||
|
||||
/**
|
||||
* 保存自检日志
|
||||
*
|
||||
* @param logEntity 自检日志实体
|
||||
*/
|
||||
void insertPreCheckLog(com.ruoyi.device.mapper.entity.PreCheckLogEntity logEntity);
|
||||
|
||||
/**
|
||||
* 保存飞行日志
|
||||
*
|
||||
* @param logEntity 飞行日志实体
|
||||
*/
|
||||
void insertFlightLog(com.ruoyi.device.mapper.entity.FlightLogEntity logEntity);
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package com.ruoyi.device.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlDataCallback;
|
||||
import com.ruoyi.device.mapper.entity.FlightEntity;
|
||||
import com.ruoyi.device.mapper.entity.FlightLogEntity;
|
||||
import com.ruoyi.device.service.FlightService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 飞行事件回调处理器
|
||||
* 处理飞行事件日志(如起飞、返航等)
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FlightEventCallback implements IAirportFlyControlDataCallback {
|
||||
|
||||
@Autowired
|
||||
private FlightService flightService;
|
||||
|
||||
@Override
|
||||
public void onAirportFlyControlData(String deviceSn, String payload, String topic) {
|
||||
try {
|
||||
JSONObject data = JSONObject.parseObject(payload);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String msg = data.getString("msg");
|
||||
String messageID = data.getString("messageID");
|
||||
|
||||
String action = data.getString("action");
|
||||
if (action == null || action.isEmpty()) {
|
||||
JSONObject dataObj = data.getJSONObject("data");
|
||||
if (dataObj != null) {
|
||||
action = dataObj.getString("action");
|
||||
}
|
||||
}
|
||||
|
||||
FlightEntity flight;
|
||||
if (messageID != null && !messageID.isEmpty()) {
|
||||
flight = handleFlightIdExternal(deviceSn, messageID);
|
||||
} else {
|
||||
flight = flightService.getOrCreateCurrentFlight(deviceSn);
|
||||
}
|
||||
|
||||
if (msg != null && !msg.isEmpty()) {
|
||||
handleFlightLog(deviceSn, msg, flight);
|
||||
handleFlightStatus(deviceSn, action, data, flight);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理飞行事件失败: deviceSn={}, topic={}, error={}", deviceSn, topic, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private FlightEntity handleFlightIdExternal(String deviceSn, String messageId) {
|
||||
if (messageId != null && !messageId.isEmpty()) {
|
||||
return flightService.getOrCreateFlightByMessageId(deviceSn, messageId);
|
||||
} else {
|
||||
return flightService.getOrCreateCurrentFlight(deviceSn);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFlightLog(String deviceSn, String message, FlightEntity flight) {
|
||||
if (flight == null) {
|
||||
log.warn("飞行记录为空,无法保存飞行日志: deviceSn={}, message={}", deviceSn, message);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
FlightLogEntity logEntity = new FlightLogEntity();
|
||||
logEntity.setFlightId(flight.getFlightId());
|
||||
logEntity.setLogContent(message);
|
||||
flightService.insertFlightLog(logEntity);
|
||||
log.info("保存飞行日志: deviceSn={}, flightId={}, message={}",
|
||||
deviceSn, flight.getFlightId(), message);
|
||||
} catch (Exception e) {
|
||||
log.error("保存飞行日志失败: deviceSn={}, message={}, error={}",
|
||||
deviceSn, message, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFlightStatus(String deviceSn, String action, JSONObject data, FlightEntity flight) {
|
||||
if (flight == null) {
|
||||
log.warn("飞行记录为空,无法更新状态: deviceSn={}, action={}", deviceSn, action);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String msg = data.getString("msg");
|
||||
String dataContent = data.getString("data");
|
||||
|
||||
if ((msg != null && msg.contains("起飞成功")) || (dataContent != null && dataContent.contains("起飞成功"))) {
|
||||
flightService.updateFlightStatus(flight.getFlightId(), "飞行中");
|
||||
log.info("飞行状态更新: deviceSn={}, status=飞行中", deviceSn);
|
||||
} else if ((msg != null && msg.contains("返航成功")) || (dataContent != null && dataContent.contains("返航成功")) ||
|
||||
(dataContent != null && dataContent.contains("任务飞行完成"))) {
|
||||
flightService.updateFlightStatus(flight.getFlightId(), "已返航");
|
||||
log.info("飞行状态更新: deviceSn={}, status=已返航", deviceSn);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("更新飞行状态失败: deviceSn={}, action={}, error={}",
|
||||
deviceSn, action, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package com.ruoyi.device.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IDroneRealTimeCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.model.DroneRealTimeData;
|
||||
import com.ruoyi.device.mapper.entity.FlightEntity;
|
||||
import com.ruoyi.device.mapper.entity.FlightLogEntity;
|
||||
import com.ruoyi.device.mapper.entity.PreCheckLogEntity;
|
||||
import com.ruoyi.device.service.FlightService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 飞行日志回调处理器
|
||||
* 处理自检日志和飞行事件日志
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FlightLogCallback implements IDroneRealTimeCallback {
|
||||
|
||||
@Autowired
|
||||
private FlightService flightService;
|
||||
|
||||
@Override
|
||||
public void onDroneRealTimeData(String deviceSn, DroneRealTimeData data) {
|
||||
if (data == null || data.getData() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DroneRealTimeData.DroneInfo droneInfo = data.getData();
|
||||
|
||||
try {
|
||||
if (droneInfo.getJiancha() != null && !droneInfo.getJiancha().isEmpty()) {
|
||||
handlePreCheckLog(deviceSn, data.getMessageID(), droneInfo.getJiancha());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理自检日志失败: deviceSn={}, error={}", deviceSn, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePreCheckLog(String deviceSn, String messageID, String jianchaJson) {
|
||||
try {
|
||||
FlightEntity flight;
|
||||
|
||||
if (messageID != null && !messageID.isEmpty()) {
|
||||
flight = flightService.getOrCreateFlightByMessageId(deviceSn, messageID);
|
||||
} else {
|
||||
flight = flightService.getOrCreateCurrentFlight(deviceSn);
|
||||
}
|
||||
|
||||
JSONArray checkItems = JSON.parseArray(jianchaJson);
|
||||
if (checkItems != null && !checkItems.isEmpty()) {
|
||||
for (int i = 0; i < checkItems.size(); i++) {
|
||||
JSONObject item = checkItems.getJSONObject(i);
|
||||
PreCheckLogEntity logEntity = new PreCheckLogEntity();
|
||||
logEntity.setFlightId(flight.getFlightId());
|
||||
|
||||
String check = item.getString("check");
|
||||
String value = item.getString("value");
|
||||
Boolean result = item.getBoolean("result");
|
||||
|
||||
String statusText = result != null && result ? "自检成功" : "自检失败";
|
||||
String logContent = check + " " + value + " " + statusText;
|
||||
|
||||
logEntity.setLogContent(logContent);
|
||||
logEntity.setSuccess(result != null ? result : false);
|
||||
flightService.insertPreCheckLog(logEntity);
|
||||
}
|
||||
log.info("保存自检日志: deviceSn={}, flightId={}, 检查项数量={}",
|
||||
deviceSn, flight.getFlightId(), checkItems.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("保存自检日志失败: deviceSn={}, jiancha={}, error={}",
|
||||
deviceSn, jianchaJson, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
package com.ruoyi.device.service.impl;
|
||||
|
||||
import com.ruoyi.device.mapper.FlightLogMapper;
|
||||
import com.ruoyi.device.mapper.FlightMapper;
|
||||
import com.ruoyi.device.mapper.PreCheckLogMapper;
|
||||
import com.ruoyi.device.mapper.entity.FlightEntity;
|
||||
import com.ruoyi.device.mapper.entity.FlightLogEntity;
|
||||
import com.ruoyi.device.mapper.entity.PreCheckLogEntity;
|
||||
import com.ruoyi.device.service.FlightService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 飞行服务实现类
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FlightServiceImpl implements FlightService
|
||||
{
|
||||
@Autowired
|
||||
private FlightMapper flightMapper;
|
||||
|
||||
@Autowired
|
||||
private PreCheckLogMapper preCheckLogMapper;
|
||||
|
||||
@Autowired
|
||||
private FlightLogMapper flightLogMapper;
|
||||
|
||||
@Override
|
||||
public FlightEntity getOrCreateCurrentFlight(String deviceSn) {
|
||||
FlightEntity flight = flightMapper.selectLatestFlightByDeviceSn(deviceSn);
|
||||
|
||||
if (flight == null || "已返航".equals(flight.getStatus())) {
|
||||
flight = createFlight(deviceSn);
|
||||
}
|
||||
|
||||
return flight;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public FlightEntity getOrCreateFlightByMessageId(String deviceSn, String messageId) {
|
||||
FlightEntity flight = flightMapper.selectLatestFlightByDeviceSn(deviceSn);
|
||||
|
||||
if (flight == null) {
|
||||
flight = createFlight(deviceSn);
|
||||
if (messageId != null && !messageId.isEmpty()) {
|
||||
updateFlightIdExternal(flight.getFlightId(), messageId);
|
||||
}
|
||||
return flight;
|
||||
}
|
||||
|
||||
String existingFlightIdExternal = flight.getFlightIdExternal();
|
||||
if (existingFlightIdExternal == null || existingFlightIdExternal.isEmpty()) {
|
||||
if (messageId != null && !messageId.isEmpty()) {
|
||||
updateFlightIdExternal(flight.getFlightId(), messageId);
|
||||
}
|
||||
return flight;
|
||||
}
|
||||
|
||||
if (existingFlightIdExternal.equals(messageId)) {
|
||||
return flight;
|
||||
}
|
||||
|
||||
flight = createFlight(deviceSn);
|
||||
if (messageId != null && !messageId.isEmpty()) {
|
||||
updateFlightIdExternal(flight.getFlightId(), messageId);
|
||||
}
|
||||
log.info("messageId不同,创建新飞行 - deviceSn={}, flightId={}, oldMessageId={}, newMessageId={}",
|
||||
deviceSn, flight.getFlightId(), existingFlightIdExternal, messageId);
|
||||
return flight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlightEntity getLatestFlight(String deviceSn) {
|
||||
return flightMapper.selectLatestFlightByDeviceSn(deviceSn);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public FlightEntity createFlight(String deviceSn) {
|
||||
FlightEntity flight = new FlightEntity();
|
||||
flight.setDeviceSn(deviceSn);
|
||||
flight.setStatus("自检中");
|
||||
flightMapper.insertFlight(flight);
|
||||
log.info("创建新的飞行记录: deviceSn={}, flightId={}", deviceSn, flight.getFlightId());
|
||||
return flight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFlightIdExternal(Long flightId, String flightIdExternal) {
|
||||
FlightEntity flight = new FlightEntity();
|
||||
flight.setFlightId(flightId);
|
||||
flight.setFlightIdExternal(flightIdExternal);
|
||||
flightMapper.updateFlight(flight);
|
||||
log.info("更新飞行ID: flightId={}, flightIdExternal={}", flightId, flightIdExternal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFlightStatus(Long flightId, String status) {
|
||||
flightMapper.updateFlightStatus(flightId, status);
|
||||
log.info("更新飞行状态: flightId={}, status={}", flightId, status);
|
||||
|
||||
if ("已返航".equals(status)) {
|
||||
flightMapper.updateReturnTime(flightId);
|
||||
log.info("更新返航时间: flightId={}", flightId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateReturnTime(Long flightId) {
|
||||
flightMapper.updateReturnTime(flightId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getLatestFlightWithLogs(String deviceSn) {
|
||||
FlightEntity flight = flightMapper.selectLatestFlightByDeviceSn(deviceSn);
|
||||
if (flight == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("flightId", flight.getFlightId());
|
||||
result.put("deviceSn", flight.getDeviceSn());
|
||||
result.put("flightIdExternal", flight.getFlightIdExternal());
|
||||
result.put("status", flight.getStatus());
|
||||
result.put("returnTime", flight.getReturnTime());
|
||||
result.put("createTime", flight.getCreateTime());
|
||||
|
||||
List<PreCheckLogEntity> preCheckLogs = preCheckLogMapper.selectPreCheckLogListByFlightId(flight.getFlightId());
|
||||
result.put("preCheckLogs", preCheckLogs);
|
||||
|
||||
List<FlightLogEntity> flightLogs = flightLogMapper.selectFlightLogListByFlightId(flight.getFlightId());
|
||||
result.put("flightLogs", flightLogs);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertPreCheckLog(PreCheckLogEntity logEntity) {
|
||||
preCheckLogMapper.insertPreCheckLog(logEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertFlightLog(FlightLogEntity logEntity) {
|
||||
flightLogMapper.insertFlightLog(logEntity);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.ruoyi.device.service.impl;
|
||||
|
||||
import com.ruoyi.device.domain.impl.machine.state.DroneState;
|
||||
import com.ruoyi.device.domain.impl.machine.state.MachineStates;
|
||||
import com.ruoyi.device.domain.impl.machine.statemachine.StateChangeListener;
|
||||
import com.ruoyi.device.mapper.entity.FlightEntity;
|
||||
import com.ruoyi.device.service.FlightService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 飞行状态变化监听器
|
||||
* 监听状态机的状态变化,更新飞行表中的状态
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FlightStateChangeListener implements StateChangeListener {
|
||||
|
||||
@Autowired
|
||||
private FlightService flightService;
|
||||
|
||||
@Autowired
|
||||
private com.ruoyi.device.domain.impl.machine.statemachine.MachineStateManager stateManager;
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onApplicationReady() {
|
||||
stateManager.registerStateChangeListener("flight-state-changer", this);
|
||||
log.info("飞行状态变化监听器已注册");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChange(String sn, MachineStates newStates) {
|
||||
try {
|
||||
DroneState droneState = newStates.getDroneState();
|
||||
if (droneState == DroneState.UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlightEntity flight = flightService.getOrCreateCurrentFlight(sn);
|
||||
if (flight == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String currentStatus = flight.getStatus();
|
||||
String newStatus = mapDroneStateToFlightStatus(droneState);
|
||||
|
||||
if (!currentStatus.equals(newStatus)) {
|
||||
flightService.updateFlightStatus(flight.getFlightId(), newStatus);
|
||||
log.info("状态变化更新飞行状态: sn={}, droneState={}, flightStatus={}",
|
||||
sn, droneState, newStatus);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("状态变化监听器处理失败: sn={}, error={}", sn, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String mapDroneStateToFlightStatus(DroneState droneState) {
|
||||
switch (droneState) {
|
||||
case ONLINE:
|
||||
return "自检中";
|
||||
case FLYING:
|
||||
return "飞行中";
|
||||
case ARRIVED:
|
||||
return "已返航";
|
||||
case RETURNING:
|
||||
return "已返航";
|
||||
case UNKNOWN:
|
||||
default:
|
||||
return "自检中";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import com.ruoyi.device.domain.impl.machine.state.AirportState;
|
|||
import com.ruoyi.device.domain.impl.machine.state.MachineStates;
|
||||
import com.ruoyi.device.domain.impl.machine.statemachine.MachineStateManager;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IRealTimeBasicCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.IAirportFlyControlDataCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengEventsCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengOsdCallback;
|
||||
import com.ruoyi.device.domain.impl.tuohengmqtt.callback.ITuohengRealTimeDataCallback;
|
||||
|
|
@ -183,6 +184,10 @@ public class TuohengService {
|
|||
}
|
||||
});
|
||||
|
||||
handler.registerDroneRealTimeCallback(new FlightLogCallback());
|
||||
|
||||
handler.registerAirportFlyControlDataCallback(new FlightEventCallback());
|
||||
|
||||
log.info("TuohengService 初始化完成,已注册所有回调");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
package com.ruoyi.device.websocket;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.device.mapper.entity.FlightLogEntity;
|
||||
import com.ruoyi.device.service.FlightService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* 飞行日志WebSocket
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ServerEndpoint("/websocket/flightLog/{deviceSn}")
|
||||
public class FlightLogWebSocket {
|
||||
|
||||
private Session session;
|
||||
|
||||
private String deviceSn;
|
||||
|
||||
private static final CopyOnWriteArraySet<FlightLogWebSocket> sessions = new CopyOnWriteArraySet<>();
|
||||
|
||||
private static final Map<String, FlightLogWebSocket> sessionMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private FlightService flightService;
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("deviceSn") String deviceSn) {
|
||||
this.session = session;
|
||||
this.deviceSn = deviceSn;
|
||||
sessions.add(this);
|
||||
sessionMap.put(session.getId(), this);
|
||||
log.info("飞行日志WebSocket连接建立: sessionId={}, deviceSn={}", session.getId(), deviceSn);
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose() {
|
||||
sessions.remove(this);
|
||||
if (session != null) {
|
||||
sessionMap.remove(session.getId());
|
||||
log.info("飞行日志WebSocket连接关闭: sessionId={}, deviceSn={}", session.getId(), deviceSn);
|
||||
} else {
|
||||
log.info("飞行日志WebSocket连接关闭: session为null");
|
||||
}
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(String message) {
|
||||
log.info("收到飞行日志WebSocket消息: sessionId={}, message={}", session.getId(), message);
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error) {
|
||||
log.error("飞行日志WebSocket错误: sessionId={}, deviceSn={}, error={}",
|
||||
session.getId(), deviceSn, error.getMessage(), error);
|
||||
}
|
||||
|
||||
private void sendMessage(String message) {
|
||||
try {
|
||||
if (session != null && session.isOpen()) {
|
||||
session.getBasicRemote().sendText(message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送飞行日志WebSocket消息失败: deviceSn={}, error={}", deviceSn, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 3000)
|
||||
public void broadcast() {
|
||||
for (FlightLogWebSocket ws : sessions) {
|
||||
Map<String, Object> flightData = flightService.getLatestFlightWithLogs(ws.deviceSn);
|
||||
if (flightData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String status = (String) flightData.get("status");
|
||||
List<FlightLogEntity> logs = (List<FlightLogEntity>) flightData.get("flightLogs");
|
||||
|
||||
Map<String, Object> response = new ConcurrentHashMap<>();
|
||||
response.put("status", status);
|
||||
response.put("logs", logs);
|
||||
|
||||
ws.sendMessage(JSON.toJSONString(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package com.ruoyi.device.websocket;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.device.mapper.entity.PreCheckLogEntity;
|
||||
import com.ruoyi.device.service.FlightService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* 自检日志WebSocket
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-02-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ServerEndpoint("/websocket/preCheckLog/{deviceSn}")
|
||||
public class PreCheckLogWebSocket {
|
||||
|
||||
private Session session;
|
||||
|
||||
private String deviceSn;
|
||||
|
||||
private static final CopyOnWriteArraySet<PreCheckLogWebSocket> sessions = new CopyOnWriteArraySet<>();
|
||||
|
||||
private static final Map<String, PreCheckLogWebSocket> sessionMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private FlightService flightService;
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("deviceSn") String deviceSn) {
|
||||
this.session = session;
|
||||
this.deviceSn = deviceSn;
|
||||
sessions.add(this);
|
||||
sessionMap.put(session.getId(), this);
|
||||
log.info("自检日志WebSocket连接建立: sessionId={}, deviceSn={}", session.getId(), deviceSn);
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose() {
|
||||
sessions.remove(this);
|
||||
if (session != null) {
|
||||
sessionMap.remove(session.getId());
|
||||
log.info("自检日志WebSocket连接关闭: sessionId={}, deviceSn={}", session.getId(), deviceSn);
|
||||
} else {
|
||||
log.info("自检日志WebSocket连接关闭: session为null");
|
||||
}
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(String message) {
|
||||
log.info("收到自检日志WebSocket消息: sessionId={}, message={}", session.getId(), message);
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error) {
|
||||
log.error("自检日志WebSocket错误: sessionId={}, deviceSn={}, error={}",
|
||||
session.getId(), deviceSn, error.getMessage(), error);
|
||||
}
|
||||
|
||||
private void sendMessage(String message) {
|
||||
try {
|
||||
if (session != null && session.isOpen()) {
|
||||
session.getBasicRemote().sendText(message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送自检日志WebSocket消息失败: deviceSn={}, error={}", deviceSn, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 3000)
|
||||
public void broadcast() {
|
||||
for (PreCheckLogWebSocket ws : sessions) {
|
||||
Map<String, Object> flightData = flightService.getLatestFlightWithLogs(ws.deviceSn);
|
||||
if (flightData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String status = (String) flightData.get("status");
|
||||
List<PreCheckLogEntity> logs = (List<PreCheckLogEntity>) flightData.get("preCheckLogs");
|
||||
|
||||
Map<String, Object> response = new ConcurrentHashMap<>();
|
||||
response.put("status", status);
|
||||
response.put("logs", logs);
|
||||
|
||||
ws.sendMessage(JSON.toJSONString(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
-- ============================================================
|
||||
-- Flyway Migration Script
|
||||
-- Version: V3
|
||||
-- Description: Create flight log tables (flight, pre_check_log, flight_log)
|
||||
-- Author: ruoyi
|
||||
-- Date: 2026-02-25
|
||||
-- ============================================================
|
||||
|
||||
-- 创建飞行表
|
||||
CREATE TABLE IF NOT EXISTS device_flight (
|
||||
flight_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '飞行主键',
|
||||
device_sn VARCHAR(100) NOT NULL COMMENT '无人机SN',
|
||||
flight_id_external VARCHAR(100) COMMENT '外部飞行ID (来自MQTT的taskId)',
|
||||
status VARCHAR(20) COMMENT '状态:自检中、飞行中、已返航',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
return_time DATETIME COMMENT '返航时间',
|
||||
create_by VARCHAR(64) COMMENT '创建者',
|
||||
update_by VARCHAR(64) COMMENT '更新者',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
PRIMARY KEY (flight_id),
|
||||
KEY idx_flight_device_sn (device_sn),
|
||||
KEY idx_flight_id_external (flight_id_external),
|
||||
KEY idx_flight_status (status),
|
||||
KEY idx_flight_create_time (create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='飞行表';
|
||||
|
||||
-- 创建自检日志表
|
||||
CREATE TABLE IF NOT EXISTS device_pre_check_log (
|
||||
log_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '自检日志主键',
|
||||
flight_id BIGINT NOT NULL COMMENT '关联飞行表ID',
|
||||
log_content TEXT COMMENT '日志内容 (JSON字符串)',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (log_id),
|
||||
KEY idx_pre_check_flight_id (flight_id),
|
||||
KEY idx_pre_check_create_time (create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='自检日志表';
|
||||
|
||||
-- 创建飞行日志表
|
||||
CREATE TABLE IF NOT EXISTS device_flight_log (
|
||||
log_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '飞行日志主键',
|
||||
flight_id BIGINT NOT NULL COMMENT '关联飞行表ID',
|
||||
log_content TEXT COMMENT '日志内容',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (log_id),
|
||||
KEY idx_flight_log_flight_id (flight_id),
|
||||
KEY idx_flight_log_create_time (create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='飞行日志表';
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
-- ============================================================
|
||||
-- Flyway Migration Script
|
||||
-- Version: V6
|
||||
-- Description: Add success field to device_pre_check_log table
|
||||
-- Author: ruoyi
|
||||
-- Date: 2026-02-25
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE device_pre_check_log
|
||||
ADD COLUMN success TINYINT(1) DEFAULT 0 COMMENT '是否成功 (1=成功, 0=失败)'
|
||||
AFTER log_content;
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.device.mapper.FlightLogMapper">
|
||||
|
||||
<resultMap type="com.ruoyi.device.mapper.entity.FlightLogEntity" id="FlightLogResult">
|
||||
<result property="logId" column="log_id" />
|
||||
<result property="flightId" column="flight_id" />
|
||||
<result property="logContent" column="log_content" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectFlightLogVo">
|
||||
select log_id, flight_id, log_content,
|
||||
create_by, create_time, update_by, update_time, remark
|
||||
from device_flight_log
|
||||
</sql>
|
||||
|
||||
<select id="selectFlightLogByLogId" parameterType="Long" resultMap="FlightLogResult">
|
||||
<include refid="selectFlightLogVo"/>
|
||||
where log_id = #{logId}
|
||||
</select>
|
||||
|
||||
<select id="selectFlightLogListByFlightId" parameterType="Long" resultMap="FlightLogResult">
|
||||
<include refid="selectFlightLogVo"/>
|
||||
where flight_id = #{flightId}
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<insert id="insertFlightLog" parameterType="com.ruoyi.device.mapper.entity.FlightLogEntity" useGeneratedKeys="true" keyProperty="logId" keyColumn="log_id">
|
||||
insert into device_flight_log
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="flightId != null">flight_id,</if>
|
||||
<if test="logContent != null and logContent != ''">log_content,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
create_time
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="flightId != null">#{flightId},</if>
|
||||
<if test="logContent != null and logContent != ''">#{logContent},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
now()
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.device.mapper.FlightMapper">
|
||||
|
||||
<resultMap type="com.ruoyi.device.mapper.entity.FlightEntity" id="FlightResult">
|
||||
<result property="flightId" column="flight_id" />
|
||||
<result property="deviceSn" column="device_sn" />
|
||||
<result property="flightIdExternal" column="flight_id_external" />
|
||||
<result property="status" column="status" />
|
||||
<result property="returnTime" column="return_time" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectFlightVo">
|
||||
select flight_id, device_sn, flight_id_external, status, return_time,
|
||||
create_by, create_time, update_by, update_time, remark
|
||||
from device_flight
|
||||
</sql>
|
||||
|
||||
<select id="selectFlightByFlightId" parameterType="Long" resultMap="FlightResult">
|
||||
<include refid="selectFlightVo"/>
|
||||
where flight_id = #{flightId}
|
||||
</select>
|
||||
|
||||
<select id="selectLatestFlightByDeviceSn" parameterType="String" resultMap="FlightResult">
|
||||
<include refid="selectFlightVo"/>
|
||||
where device_sn = #{deviceSn}
|
||||
order by create_time desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectFlightByDeviceSnAndStatus" resultMap="FlightResult">
|
||||
<include refid="selectFlightVo"/>
|
||||
where device_sn = #{deviceSn}
|
||||
and status = #{status}
|
||||
order by create_time desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<insert id="insertFlight" parameterType="com.ruoyi.device.mapper.entity.FlightEntity" useGeneratedKeys="true" keyProperty="flightId" keyColumn="flight_id">
|
||||
insert into device_flight
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="deviceSn != null and deviceSn != ''">device_sn,</if>
|
||||
<if test="flightIdExternal != null and flightIdExternal != ''">flight_id_external,</if>
|
||||
<if test="status != null and status != ''">status,</if>
|
||||
<if test="returnTime != null">return_time,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
create_time
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="deviceSn != null and deviceSn != ''">#{deviceSn},</if>
|
||||
<if test="flightIdExternal != null and flightIdExternal != ''">#{flightIdExternal},</if>
|
||||
<if test="status != null and status != ''">#{status},</if>
|
||||
<if test="returnTime != null">#{returnTime},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
now()
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateFlight" parameterType="com.ruoyi.device.mapper.entity.FlightEntity">
|
||||
update device_flight
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="flightIdExternal != null and flightIdExternal != ''">flight_id_external = #{flightIdExternal},</if>
|
||||
<if test="status != null and status != ''">status = #{status},</if>
|
||||
<if test="returnTime != null">return_time = #{returnTime},</if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
update_time = now()
|
||||
</trim>
|
||||
where flight_id = #{flightId}
|
||||
</update>
|
||||
|
||||
<update id="updateFlightStatus">
|
||||
update device_flight
|
||||
set status = #{status},
|
||||
update_time = now()
|
||||
where flight_id = #{flightId}
|
||||
</update>
|
||||
|
||||
<update id="updateReturnTime">
|
||||
update device_flight
|
||||
set return_time = now(),
|
||||
update_time = now()
|
||||
where flight_id = #{flightId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.device.mapper.PreCheckLogMapper">
|
||||
|
||||
<resultMap type="com.ruoyi.device.mapper.entity.PreCheckLogEntity" id="PreCheckLogResult">
|
||||
<result property="logId" column="log_id" />
|
||||
<result property="flightId" column="flight_id" />
|
||||
<result property="logContent" column="log_content" />
|
||||
<result property="success" column="success" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPreCheckLogVo">
|
||||
select log_id, flight_id, log_content, success,
|
||||
create_by, create_time, update_by, update_time, remark
|
||||
from device_pre_check_log
|
||||
</sql>
|
||||
|
||||
<select id="selectPreCheckLogByLogId" parameterType="Long" resultMap="PreCheckLogResult">
|
||||
<include refid="selectPreCheckLogVo"/>
|
||||
where log_id = #{logId}
|
||||
</select>
|
||||
|
||||
<select id="selectPreCheckLogListByFlightId" parameterType="Long" resultMap="PreCheckLogResult">
|
||||
<include refid="selectPreCheckLogVo"/>
|
||||
where flight_id = #{flightId}
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<insert id="insertPreCheckLog" parameterType="com.ruoyi.device.mapper.entity.PreCheckLogEntity" useGeneratedKeys="true" keyProperty="logId" keyColumn="log_id">
|
||||
insert into device_pre_check_log
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="flightId != null">flight_id,</if>
|
||||
<if test="logContent != null and logContent != ''">log_content,</if>
|
||||
<if test="success != null">success,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
create_time
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="flightId != null">#{flightId},</if>
|
||||
<if test="logContent != null and logContent != ''">#{logContent},</if>
|
||||
<if test="success != null">#{success},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
now()
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<insert id="insertPreCheckLogBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="logId" keyColumn="log_id">
|
||||
insert into device_pre_check_log (flight_id, log_content, success, create_time)
|
||||
values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.flightId}, #{item.logContent}, #{item.success}, now())
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue