Compare commits
2 Commits
77cc3a6e97
...
020151d506
| Author | SHA1 | Date |
|---|---|---|
|
|
020151d506 | 2 months ago |
|
|
658c85345b | 2 months ago |
4 changed files with 234 additions and 2 deletions
@ -0,0 +1,211 @@ |
|||
package com.project.operation.domain.service; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.project.operation.domain.entity.OperationLogEntity; |
|||
import com.project.operation.mapper.OperationLogMapper; |
|||
import jakarta.annotation.PostConstruct; |
|||
import jakarta.annotation.PreDestroy; |
|||
import org.eclipse.paho.client.mqttv3.MqttClient; |
|||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
|||
import org.eclipse.paho.client.mqttv3.MqttMessage; |
|||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.scheduling.annotation.Scheduled; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
|
|||
import java.net.NetworkInterface; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.text.SimpleDateFormat; |
|||
import java.util.Date; |
|||
import java.util.Enumeration; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Service |
|||
public class MqttLogReporterService { |
|||
|
|||
private static final String BROKER_URL = "tcp://oam.itc-pa.cn:1883"; |
|||
private static final String USERNAME = "ychjasp"; |
|||
private static final String PASSWORD = "821006"; |
|||
|
|||
// 依照您的要求,固定配置参数
|
|||
private static final int PROJECT_ID = 2282; // 项目 ID (int)
|
|||
private static final String MODEL = "ES0450"; // 产品型号 (String)
|
|||
private static final String COMPANY = "BL"; // 公司名称 (固定)
|
|||
private static final String DEVICE_NAME = "8.129.84.155"; // 设备名称 (固定)
|
|||
private static final String PRODUCT_NAME = "智考云"; // 产品线名称 (固定)
|
|||
|
|||
@Autowired |
|||
private OperationLogMapper operationLogMapper; |
|||
|
|||
private MqttClient mqttClient; |
|||
private String macAddress; |
|||
private String pubTopic; |
|||
|
|||
// 用于安全生成嵌套 JSON 的工具
|
|||
private final ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
this.macAddress = getLocalMacAddress(); |
|||
this.pubTopic = "device/" + this.macAddress + "/report/log"; |
|||
connect(); |
|||
} |
|||
|
|||
private void connect() { |
|||
try { |
|||
mqttClient = new MqttClient(BROKER_URL, macAddress, new MemoryPersistence()); |
|||
MqttConnectOptions options = new MqttConnectOptions(); |
|||
options.setUserName(USERNAME); |
|||
options.setPassword(PASSWORD.toCharArray()); |
|||
options.setCleanSession(true); |
|||
options.setAutomaticReconnect(true); |
|||
options.setKeepAliveInterval(20); |
|||
options.setConnectionTimeout(30); |
|||
|
|||
mqttClient.connect(options); |
|||
System.out.println("MQTT 连接成功。ClientID: " + macAddress); |
|||
} catch (Exception e) { |
|||
System.err.println("MQTT 连接失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 定时扫描任务:每 11 秒执行一次。 |
|||
* 只捞取一条未上报(is_reported = 0)的日志,保障上报间隔不小于10秒 |
|||
*/ |
|||
@Scheduled(fixedDelay = 11000) |
|||
public void scanAndReport() { |
|||
if (mqttClient == null || !mqttClient.isConnected()) { |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
// 1. 获取最早的一条未上报数据
|
|||
LambdaQueryWrapper<OperationLogEntity> queryWrapper = new LambdaQueryWrapper<>(); |
|||
queryWrapper.eq(OperationLogEntity::getIsReported, 0) |
|||
.orderByAsc(OperationLogEntity::getId) |
|||
.last("LIMIT 1"); |
|||
|
|||
OperationLogEntity logEntity = operationLogMapper.selectOne(queryWrapper); |
|||
|
|||
// 2. 如果存在未上报数据,将其上报并更新状态
|
|||
if (logEntity != null) { |
|||
String payload = buildJsonPayload(logEntity); |
|||
MqttMessage message = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8)); |
|||
message.setQos(1); |
|||
|
|||
// 3. 发布到 MQTT
|
|||
mqttClient.publish(pubTopic, message); |
|||
|
|||
// 4. 更新状态为 1
|
|||
logEntity.setIsReported(1); |
|||
operationLogMapper.updateById(logEntity); |
|||
|
|||
System.out.println("日志上报成功,ID: " + logEntity.getId() + " 报文内容: " + payload); |
|||
} |
|||
} catch (Exception e) { |
|||
System.err.println("日志定时上报异常: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 严格对照您提供的接口字段及嵌套格式生成 JSON |
|||
*/ |
|||
private String buildJsonPayload(OperationLogEntity logEntity) throws Exception { |
|||
|
|||
// 1. 组装内层的 data 对象
|
|||
Map<String, Object> dataMap = new HashMap<>(); |
|||
|
|||
// 字段: timestamp (设备端事件发生的时间戳,秒级)
|
|||
long timestamp = logEntity.getCreateTime() != null ? logEntity.getCreateTime().getTime() / 1000 : System.currentTimeMillis() / 1000; |
|||
dataMap.put("timestamp", (int) timestamp); |
|||
|
|||
// 字段: log_type (1--异常 2--业务 3--运行 4--配置)
|
|||
// 映射规则:如果是失败(result=1)或有异常信息,归为 1 (异常),否则归为 2 (业务)
|
|||
int logType = 2; |
|||
dataMap.put("log_type", logType); |
|||
|
|||
// 字段: log_level (1--轻微 2--一般 3--严重 4--致命,正常操作发0)
|
|||
int logLevel = 1; |
|||
dataMap.put("log_level", logLevel); |
|||
|
|||
// 字段: log_desc (日志描述,不超过1024字节)
|
|||
String logDesc = String.format("[%s-%s] %s | 方法: %s | 耗时: %dms", |
|||
logEntity.getModule(), |
|||
logEntity.getAction(), |
|||
logEntity.getDescription(), |
|||
logEntity.getMethod(), |
|||
logEntity.getCostTime() |
|||
); |
|||
if (logDesc.length() > 300) { // 预防超长,做个截断保护
|
|||
logDesc = logDesc.substring(0, 300) + "..."; |
|||
} |
|||
dataMap.put("log_desc", logDesc); |
|||
|
|||
// 字段: serial_number (唯一序列号,不超过100字符)
|
|||
// 映射规则:直接使用数据库该条日志的主键 ID 作为唯一标识
|
|||
dataMap.put("serial_number", String.valueOf(logEntity.getId())); |
|||
|
|||
// 字段: product_name (产品线名称,不超过50字符)
|
|||
dataMap.put("product_name", PRODUCT_NAME); |
|||
|
|||
// 字段: model (型号名称)
|
|||
dataMap.put("model", MODEL); |
|||
|
|||
// 字段: log_source (日志维度 1-11)
|
|||
// 映射规则:程序报错归为 7,正常操作归为 4(其他用户操作日志)
|
|||
int logSource = (logType == 1) ? 7 : 1; |
|||
dataMap.put("log_source", logSource); |
|||
|
|||
// 字段: project_id (项目id,必选)
|
|||
dataMap.put("project_id", PROJECT_ID); |
|||
|
|||
// 2. 组装外层对象
|
|||
Map<String, Object> rootMap = new HashMap<>(); |
|||
rootMap.put("company", COMPANY); |
|||
rootMap.put("device_name", DEVICE_NAME); |
|||
rootMap.put("data", dataMap); |
|||
|
|||
// 3. 序列化为标准 JSON
|
|||
return objectMapper.writeValueAsString(rootMap); |
|||
} |
|||
|
|||
private String getLocalMacAddress() { |
|||
try { |
|||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); |
|||
while (interfaces.hasMoreElements()) { |
|||
NetworkInterface ni = interfaces.nextElement(); |
|||
if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) { |
|||
continue; |
|||
} |
|||
byte[] mac = ni.getHardwareAddress(); |
|||
if (mac != null && mac.length == 6) { |
|||
StringBuilder sb = new StringBuilder(); |
|||
for (int i = 0; i < mac.length; i++) { |
|||
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : "")); |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return "00-11-22-33-44-55"; |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void cleanup() { |
|||
try { |
|||
if (mqttClient != null && mqttClient.isConnected()) { |
|||
mqttClient.disconnect(); |
|||
mqttClient.close(); |
|||
} |
|||
} catch (Exception e) { |
|||
// silent
|
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue