From fe9bba84f5ded2ff0efec46cc706149b1a4073d8 Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期三, 14 十月 2026 13:12:02 +0800
Subject: [PATCH] 添加日志记录
---
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/tools/IotCommandLog.java | 157 +++++++++++++++++++++++++++++++++++++++
ruoyi-service/ruoyi-integration/src/main/resources/logback.xml | 26 ++++++
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/listener/IotMessageListener.java | 29 +++++--
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/produce/IotMessageProduce.java | 5 +
4 files changed, 209 insertions(+), 8 deletions(-)
diff --git a/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/listener/IotMessageListener.java b/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/listener/IotMessageListener.java
index b4a3c23..3038896 100644
--- a/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/listener/IotMessageListener.java
+++ b/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/listener/IotMessageListener.java
@@ -17,6 +17,7 @@
import com.ruoyi.integration.iotda.model.*;
import com.ruoyi.integration.iotda.utils.produce.IotMessageProduce;
import com.ruoyi.integration.iotda.utils.tools.CP56Time2aConverter;
+import com.ruoyi.integration.iotda.utils.tools.IotCommandLog;
import com.ruoyi.integration.iotda.utils.tools.MessageUtil;
import com.ruoyi.integration.iotda.utils.tools.StrategyUtil;
import com.ruoyi.integration.iotda.utils.tools.ChargingMessageUtil;
@@ -84,11 +85,12 @@
content = body.getJSONObject("content");
}catch (Exception e){
String content1 = body.getString("content");
+ // 按对象解析失败时无法识别 service_id(无法判断是否心跳),记录原始报文兜底便于排查
+ IotCommandLog.uplinkUnparsed(deviceNoOf(jsonObject), content1);
String substring = content1.substring(content1.length() - 1);
if(!"}".equals(substring)){
content1 = content1.substring(0, content1.length() - 1) + "}";
}
-// log.info("content1:"+content1);
content = JSON.parseObject(content1);
}
JSONObject header = jsonObject.getJSONObject("notify_data").getJSONObject("header");
@@ -96,10 +98,8 @@
String nodeId = header.getString("node_id"); // 设备编号
String productId = header.getString("product_id"); // 产品id
String service_id = content.getString("service_id");
- if(!SendTagConstant.PING.equals(service_id)){
- log.info("收到IOT发送的消息:{}",jsonObject.toJSONString());
- }
-// log.info("服务id:{}",service_id);
+ // 记录上行指令日志(独立日志文件、含指令中文含义;心跳 ping 不记录)
+ IotCommandLog.uplink(deviceNoOf(jsonObject), service_id, content.toJSONString());
ChargingMessage chargingMessage = new ChargingMessage();
chargingMessage.setServiceId(service_id);
// 设备消息下发
@@ -127,7 +127,6 @@
log.warn("ONLINE消息处理:{} 毫秒", System.currentTimeMillis() - ONLINE);
break;
case SendTagConstant.PING:
- long PING = System.currentTimeMillis();
PingMessage pingMessage = JSON.parseObject(content.toJSONString(),PingMessage.class);
String fullName = pingMessage.getCharging_pile_code() + pingMessage.getCharging_gun_code();
//主动丢弃一次心跳数据,用于降低调华为接口评率,防止被限流
@@ -174,8 +173,6 @@
vo1.setPile_code(pingMessage.getCharging_pile_code());
vo1.setStatus(pingMessage.getCharging_gun_status());
chargingPileClient.updateChargingPileStatus(vo1);
-
- log.warn("PING消息处理:{} 毫秒", System.currentTimeMillis() - PING);
break;
case SendTagConstant.END_CHARGE:
long END_CHARGE = System.currentTimeMillis();
@@ -531,6 +528,22 @@
return AjaxResult.success();
}
+ /**
+ * 从IoTDA推送报文中提取设备编号(node_id 优先,缺失时回退 device_id)
+ */
+ private String deviceNoOf(JSONObject msg) {
+ try {
+ JSONObject header = msg.getJSONObject("notify_data").getJSONObject("header");
+ if (header == null) {
+ return null;
+ }
+ String nodeId = header.getString("node_id");
+ return (nodeId == null || nodeId.isEmpty()) ? header.getString("device_id") : nodeId;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
@ApiOperation(value = "运营平台远程控制启机",tags = {"硬件接口"})
@GetMapping(value = "/platformStartCharging")
diff --git a/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/produce/IotMessageProduce.java b/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/produce/IotMessageProduce.java
index 4ca0ffb..5e4513e 100644
--- a/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/produce/IotMessageProduce.java
+++ b/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/produce/IotMessageProduce.java
@@ -7,6 +7,7 @@
import com.huaweicloud.sdk.iotda.v5.model.DeviceMessageRequest;
import com.ruoyi.common.core.utils.uuid.UUID;
import com.ruoyi.integration.iotda.builder.IotBuilder;
+import com.ruoyi.integration.iotda.utils.tools.IotCommandLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -38,11 +39,15 @@
body.withName(name);
body.withMessage(message);
request.withBody(body);
+ // 记录下行指令日志(独立日志文件、含指令中文含义;心跳应答 pong 不记录)
+ String serviceId = message == null ? null : message.getString("service_id");
+ IotCommandLog.downlink(code, name, serviceId, message);
try {
CreateMessageResponse response = iotBuilder.buildIot().createMessage(request);
return JSON.toJSONString(response);
} catch (Exception e) {
e.printStackTrace();
+ IotCommandLog.downlinkFail(code, name, serviceId, message, e.getMessage());
return e.getMessage();
}
}
diff --git a/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/tools/IotCommandLog.java b/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/tools/IotCommandLog.java
new file mode 100644
index 0000000..cb4a52e
--- /dev/null
+++ b/ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/iotda/utils/tools/IotCommandLog.java
@@ -0,0 +1,157 @@
+package com.ruoyi.integration.iotda.utils.tools;
+
+import com.ruoyi.integration.iotda.constant.SendTagConstant;
+import com.ruoyi.integration.iotda.enums.ServiceIdMenu;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 充电桩指令收发日志
+ * <p>
+ * 1、使用独立 logger(pile-command)输出到独立日志文件,与业务日志分离(见 logback.xml);
+ * 2、日志记录统一打印指令含义,格式:指令=中文含义(service_id);
+ * 3、心跳指令(上行 ping、下行 pong)不记录,避免日志量过大。
+ */
+public class IotCommandLog {
+
+ /** 独立日志 logger,输出到独立日志文件(logback.xml 中 pile-command 配置) */
+ private static final Logger COMMAND_LOG = LoggerFactory.getLogger("pile-command");
+
+ /** 上行指令 service_id -> 中文含义 */
+ private static final Map<String, String> UPLINK_MEANING = new HashMap<>();
+
+ static {
+ UPLINK_MEANING.put(SendTagConstant.ONLINE, "充电桩登录认证");
+ UPLINK_MEANING.put(SendTagConstant.PING, "充电桩心跳包");
+ UPLINK_MEANING.put(SendTagConstant.END_CHARGE, "充电结束");
+ UPLINK_MEANING.put(SendTagConstant.ERROR_MESSAGE, "错误报文");
+ UPLINK_MEANING.put(SendTagConstant.BILLING_MODE_VERIFY, "计费模型验证请求");
+ UPLINK_MEANING.put(SendTagConstant.ACQUISITION_BILLING_MODE, "充电桩计费模型请求");
+ UPLINK_MEANING.put(SendTagConstant.UPLOAD_REAL_TIME_MONITORING_DATA, "上传实时监测数据");
+ UPLINK_MEANING.put(SendTagConstant.CHARGING_HANDSHAKE, "充电握手");
+ UPLINK_MEANING.put(SendTagConstant.PARAMETER_SETTING, "参数配置");
+ UPLINK_MEANING.put(SendTagConstant.BMS_ABORT, "充电阶段BMS中止");
+ UPLINK_MEANING.put(SendTagConstant.MOTOR_ABORT, "充电阶段充电机中止");
+ UPLINK_MEANING.put(SendTagConstant.BMS_DEMAND_AND_CHARGER_EXPORTATION, "充电过程BMS需求、充电机输出");
+ UPLINK_MEANING.put(SendTagConstant.BMS_INFORMATION, "充电过程BMS信息");
+ UPLINK_MEANING.put(SendTagConstant.CHARGING_PILE_STARTS_CHARGING, "充电桩主动申请启动充电");
+ UPLINK_MEANING.put(SendTagConstant.PLATFORM_START_CHARGING_REPLY, "远程启机命令回复");
+ UPLINK_MEANING.put(SendTagConstant.PLATFORM_STOP_CHARGING_REPLY, "远程停机命令回复");
+ UPLINK_MEANING.put(SendTagConstant.TRANSACTION_RECORD, "交易记录");
+ UPLINK_MEANING.put(SendTagConstant.UPDATE_BALANCE_REPLY, "余额更新应答");
+ UPLINK_MEANING.put(SendTagConstant.SYNCHRONIZE_OFFLINE_CARD_REPLY, "卡数据同步应答");
+ UPLINK_MEANING.put(SendTagConstant.CLEAR_OFFLINE_CARD_REPLY, "离线卡数据清除应答");
+ UPLINK_MEANING.put(SendTagConstant.QUERY_OFFLINE_CARD_REPLY, "离线卡数据查询应答");
+ UPLINK_MEANING.put(SendTagConstant.WORKING_PARAMETER_SETTING_REPLY, "充电桩工作参数设置应答");
+ UPLINK_MEANING.put(SendTagConstant.TIMING_SETTING, "对时设置");
+ UPLINK_MEANING.put(SendTagConstant.SETUP_BILLING_MODEL_REPLY, "计费模型应答");
+ UPLINK_MEANING.put(SendTagConstant.GROUND_LOCK_REAL_TIME_DATA, "地锁数据上送");
+ UPLINK_MEANING.put(SendTagConstant.CHARGING_PILE_RETURNS_GROUND_LOCK_DATA, "充电桩返回地锁数据");
+ UPLINK_MEANING.put(SendTagConstant.PLATFORM_RESTART_REPLY, "远程重启应答");
+ UPLINK_MEANING.put(SendTagConstant.PLATFORM_REMOTE_UPDATE_REPLY, "远程更新应答");
+ UPLINK_MEANING.put(SendTagConstant.QR_CODE_DELIVERY_REPLY, "二维码下发应答");
+ UPLINK_MEANING.put(SendTagConstant.SECURITY_DETECTION, "安全监测");
+ UPLINK_MEANING.put(SendTagConstant.GUN_STATUS, "推送充电设备接口状态信息");
+ UPLINK_MEANING.put(SendTagConstant.ORDER_STATUS, "推送充电状态信息");
+ UPLINK_MEANING.put(SendTagConstant.ORDER_INFO, "推送充电订单信息");
+ }
+
+ private IotCommandLog() {
+ }
+
+ /**
+ * 记录上行指令(设备 -> 平台)
+ * 心跳(ping)不记录
+ *
+ * @param deviceNo 设备编号
+ * @param serviceId 指令 service_id
+ * @param payload 报文内容
+ */
+ public static void uplink(String deviceNo, String serviceId, Object payload) {
+ if (SendTagConstant.PING.equals(serviceId)) {
+ // 心跳不记录日志
+ return;
+ }
+ COMMAND_LOG.info("[上行] 设备={} 指令={} 报文={}", deviceNo, describeUplink(serviceId), payload);
+ }
+
+ /**
+ * 记录下行指令(平台 -> 设备)
+ * 心跳应答(pong)不记录
+ *
+ * @param deviceNo 设备编号
+ * @param name 下发名称
+ * @param serviceId 指令 service_id
+ * @param payload 报文内容
+ */
+ public static void downlink(String deviceNo, String name, String serviceId, Object payload) {
+ if (isPong(serviceId)) {
+ // 心跳应答不记录日志
+ return;
+ }
+ COMMAND_LOG.info("[下行] 设备={} 指令={} 报文={}", deviceNo, describeDownlink(serviceId, name), payload);
+ }
+
+ /**
+ * 记录下行指令下发失败
+ * 心跳应答(pong)不记录
+ *
+ * @param deviceNo 设备编号
+ * @param name 下发名称
+ * @param serviceId 指令 service_id
+ * @param payload 报文内容
+ * @param error 失败原因
+ */
+ public static void downlinkFail(String deviceNo, String name, String serviceId, Object payload, String error) {
+ if (isPong(serviceId)) {
+ // 心跳应答不记录日志
+ return;
+ }
+ COMMAND_LOG.error("[下行失败] 设备={} 指令={} 报文={} 错误={}", deviceNo, describeDownlink(serviceId, name), payload, error);
+ }
+
+ /**
+ * 记录无法解析的上行原始报文(兜底)
+ * 报文解析失败时无法识别 service_id,不能判断是否为心跳,仍记录原始内容便于排查
+ *
+ * @param deviceNo 设备编号
+ * @param rawPayload 原始报文内容
+ */
+ public static void uplinkUnparsed(String deviceNo, Object rawPayload) {
+ COMMAND_LOG.info("[上行-原始报文] 设备={} 报文={}", deviceNo, rawPayload);
+ }
+
+ /** 是否心跳应答(pong) */
+ private static boolean isPong(String serviceId) {
+ return ServiceIdMenu.PONG.getValue().equals(serviceId);
+ }
+
+ /** 上行指令含义:中文含义(service_id) */
+ private static String describeUplink(String serviceId) {
+ String meaning = UPLINK_MEANING.get(serviceId);
+ if (meaning == null || meaning.isEmpty()) {
+ meaning = "未识别指令";
+ }
+ return meaning + "(" + serviceId + ")";
+ }
+
+ /** 下行指令含义:中文含义(service_id) */
+ private static String describeDownlink(String serviceId, String name) {
+ String meaning = null;
+ if (serviceId != null) {
+ for (ServiceIdMenu menu : ServiceIdMenu.values()) {
+ if (menu.getValue().equals(serviceId)) {
+ meaning = menu.getKey();
+ break;
+ }
+ }
+ }
+ if (meaning == null || meaning.isEmpty()) {
+ meaning = (name == null || name.isEmpty()) ? "未识别指令" : name;
+ }
+ return meaning + "(" + serviceId + ")";
+ }
+}
diff --git a/ruoyi-service/ruoyi-integration/src/main/resources/logback.xml b/ruoyi-service/ruoyi-integration/src/main/resources/logback.xml
index bf1d78e..9c2b931 100644
--- a/ruoyi-service/ruoyi-integration/src/main/resources/logback.xml
+++ b/ruoyi-service/ruoyi-integration/src/main/resources/logback.xml
@@ -163,6 +163,32 @@
</filter>
</appender>
+ <!-- 2.6 充电桩指令收发独立日志,独立文件输出(心跳不记录,见 IotCommandLog) -->
+ <appender name="PILE_COMMAND_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <!-- 正在记录的日志文档的路径及文档名 -->
+ <file>${log.path}/pile-command.log</file>
+ <!--日志文档输出格式-->
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+ <charset>UTF-8</charset> <!-- 设置字符集 -->
+ </encoder>
+ <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <!-- 日志归档 -->
+ <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/pile-command-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+ <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+ <maxFileSize>100MB</maxFileSize>
+ </timeBasedFileNamingAndTriggeringPolicy>
+ <!--日志文档保留天数-->
+ <maxHistory>30</maxHistory>
+ </rollingPolicy>
+ </appender>
+
+ <!-- 充电桩指令收发日志:独立文件输出,不向 root 传递(additivity=false),避免污染业务日志 -->
+ <logger name="pile-command" level="INFO" additivity="false">
+ <appender-ref ref="PILE_COMMAND_FILE" />
+ </logger>
+
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、
以及指定<appender>。<logger>仅有一个name属性,
--
Gitblit v1.7.1