设备心跳调整 app新增增设备时绑定联网设备

This commit is contained in:
yuhaiming
2026-06-13 10:51:45 +08:00
parent c7f9df980a
commit c74363afa4
65 changed files with 1622 additions and 592 deletions

View File

@@ -1,36 +0,0 @@
# ==========================================
# Water 项目环境变量模板
# 复制此文件为 .env 并填入真实值
# .env 文件已加入 .gitignore不会提交到仓库
# ==========================================
# --- 数据库 ---
DB_USERNAME=root
DB_PASSWORD=123456
# --- Redis ---
REDIS_PASSWORD=
# --- Sa-Token JWT ---
# 生产环境建议使用 openssl rand -base64 64 生成
SA_TOKEN_JWT_SECRET_KEY=abcdefghijklmnopqrstuvwxyz
# --- MQTT Broker ---
MQTT_ENABLED=true
MQTT_BROKER_URL=ssl://service.reinkun.com:8883
MQTT_USERNAME=admin
MQTT_PASSWORD=yukun_mqtt
# --- 邮件 (SMTP) ---
MAIL_PASSWORD=XZVDXjRdVrjiX8N3
# --- 短信 (阿里云 SMS) ---
SMS_ACCESS_KEY_ID=LTAI5t7xHqxFKG9DwMgurvrn
SMS_ACCESS_KEY_SECRET=nNFjR8YwNJUcMrrtdc4pkpPsHmYtfp
# --- SnailJob 调度中心 ---
SNAIL_JOB_TOKEN=SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT
# --- 第三方登录 (JustAuth) ---
# Gitee / QQ / 微信 等 OAuth 凭据请通过 application-*.yml 配置
# 或使用 JUSTAUTH_ 前缀的环境变量覆盖

12
.idea/compiler.xml generated
View File

@@ -25,18 +25,6 @@
<entry name="$PROJECT_DIR$/../../repository/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" /> <entry name="$PROJECT_DIR$/../../repository/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/mapstruct/tools/gem/gem-api/1.0.0.Alpha3/gem-api-1.0.0.Alpha3.jar" /> <entry name="$PROJECT_DIR$/../../repository/org/mapstruct/tools/gem/gem-api/1.0.0.Alpha3/gem-api-1.0.0.Alpha3.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/projectlombok/lombok-mapstruct-binding/0.2.0/lombok-mapstruct-binding-0.2.0.jar" /> <entry name="$PROJECT_DIR$/../../repository/org/projectlombok/lombok-mapstruct-binding/0.2.0/lombok-mapstruct-binding-0.2.0.jar" />
<entry name="$PROJECT_DIR$/../../repository/com/github/therapi/therapi-runtime-javadoc-scribe/0.15.0/therapi-runtime-javadoc-scribe-0.15.0.jar" />
<entry name="$PROJECT_DIR$/../../repository/com/github/therapi/therapi-runtime-javadoc/0.15.0/therapi-runtime-javadoc-0.15.0.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/springframework/boot/spring-boot-configuration-processor/3.5.12/spring-boot-configuration-processor-3.5.12.jar" />
<entry name="$PROJECT_DIR$/../../repository/io/github/linpeilie/mapstruct-plus-processor/1.5.0/mapstruct-plus-processor-1.5.0.jar" />
<entry name="$PROJECT_DIR$/../../repository/io/github/linpeilie/mapstruct-plus/1.5.0/mapstruct-plus-1.5.0.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/mapstruct/mapstruct/1.6.3/mapstruct-1.6.3.jar" />
<entry name="$PROJECT_DIR$/../../repository/io/github/linpeilie/mapstruct-plus-object-convert/1.5.0/mapstruct-plus-object-convert-1.5.0.jar" />
<entry name="$PROJECT_DIR$/../../repository/cn/easii/tutelary-repackage-javapoet/1.0.5/tutelary-repackage-javapoet-1.0.5.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/mapstruct/tools/gem/gem-api/1.0.0.Alpha3/gem-api-1.0.0.Alpha3.jar" />
<entry name="$PROJECT_DIR$/../../repository/org/projectlombok/lombok-mapstruct-binding/0.2.0/lombok-mapstruct-binding-0.2.0.jar" />
</processorPath> </processorPath>
<module name="water-app" /> <module name="water-app" />
<module name="water-common-excel" /> <module name="water-common-excel" />

View File

@@ -0,0 +1,17 @@
# 2026-06-11 工作日志
### 14. DeviceDataHandler 和 DeviceRegisterHandler 补充设备在线状态刷新
**问题**:之前只有 ACK 应答会调用 `refreshDeviceOnline()` 续期 Redis 中的设备在线状态。但设备只要上报电量或注册就证明在线,这两个场景没刷新在线缓存。后果:设备只上报数据不发 ACK 时5 分钟后重试逻辑误判它为离线。
**修复**
- `DeviceDataHandler.handle()` → 收到 `/publish/power` 后调用 `refreshDeviceOnline()`
- `DeviceRegisterHandler.handle()` → 注册后调用 `refreshDeviceOnline()`
- 两个 Handler 各自注入 `MqttProperties`,避免注入 `MqttCommandAckService` 引入新的循环依赖
**在线判定覆盖**(修复后):
| 触发方式 | 是否刷新在线状态 |
|---|---|
| 设备上报电量 `/publish/power` | ✅ 新增 |
| 设备注册 `/publish/register` | ✅ 新增 |
| ACK 应答 `/publish/ack` | ✅ 原有 |

View File

@@ -0,0 +1,23 @@
# 2026-06-12 工作日志
### 15. 修复设备断电后数据库状态未更新为离线
**问题**:设备断电/断网后Redis 中的在线 Key`mqtt:device:status:{deviceNo}`)会在 TTL默认 300s到期后自动消失但数据库 `app_device.status` 字段没有同步更新,始终显示在线。
**方案**:三层联动
1. **定时任务扫库(新增 `DeviceOfflineCheckTask`**:每 90s 扫描 DB 中 `status=1` 的设备,检查 Redis Key 是否存在,不存在则批量更新 `status=0`(离线)。扫描间隔可通过 `mqtt.offline-check.interval-ms` 配置项覆盖。
2. **上线时同步写库**
- `DeviceDataHandler.handle()`:收到电量上报时,`AppDeviceBo.setStatus("1")` 写库 + 刷新 Redis Key
- `DeviceRegisterHandler.buildRegisterDevice()`:注册时 `device.setStatus("1")`
3. **修复注册默认状态**`registerByMqtt()` 中注册默认值从 `status=2` 改为 `status=1`(在线)
**状态值定义**(来自 `AppDeviceBo` 注释):
- `0`:离线
- `1`:在线
- `2`:到期
- `3`:故障
**新增文件**`water-app/src/main/java/org/dromara/app/task/DeviceOfflineCheckTask.java`

View File

@@ -1,91 +1,327 @@
2026-06-04 07:06:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] 2026-06-13 08:59:11 [master:housekeeper] WARN com.zaxxer.hikari.pool.HikariPool - master - Thread starvation or clock leap detected (housekeeper delta=14h57m13s457ms410µs100ns).
2026-06-04 07:06:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 08:59:32 [MQTT Ping: water-server-8081] ERROR o.e.p.c.mqttv3.internal.ClientState - water-server-8081: Timed out as no write activity, keepAlive=60,000,000,000 lastOutboundActivity=150,167,329,517,400 lastInboundActivity=150,167,356,610,400 time=204,053,810,500,400 lastPing=150,167,329,537,400
2026-06-04 07:06:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[12]毫秒 2026-06-13 08:59:32 [MQTT Ping: water-server-8081] WARN org.dromara.mqtt.MqttClientManager - [MQTT] 连接已断开Timed out while waiting to write messages to the server
2026-06-04 07:06:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[36]毫秒 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已重新连接ssl://service.reinkun.com:8883
2026-06-04 07:06:57 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2062185771665858562],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/finish/schedule 服务质量等级=1
2026-06-04 07:06:57 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2062185771665858562],耗时:[13]毫秒 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/register 服务质量等级=1
2026-06-04 07:06:59 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2062185771665858562],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/power 服务质量等级=1
2026-06-04 07:06:59 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2062185771665858562],耗时:[5]毫秒 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/ack 服务质量等级=1
2026-06-04 07:07:01 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[DELETE /app/v1/deleteDevice/2062185771665858562],参数类型[json],参数:[{"clientId":"32324a04c0175c8d61ac8a282553aea1"}] 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/finish/key 服务质量等级=1
2026-06-04 07:07:01 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[DELETE /app/v1/deleteDevice/2062185771665858562],耗时:[10]毫秒 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/error 服务质量等级=1
2026-06-04 07:07:03 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 08:59:34 [mqtt-consumer-7] WARN o.d.a.handler.ScheduleFinishHandler - [MQTT] 排程完成上报未找到设备绑定的排程 时间=2026-06-13 08:59:34 设备编号=01
2026-06-04 07:07:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] 2026-06-13 08:59:34 [mqtt-consumer-4] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 08:59:34 设备编号=01 消息体={
2026-06-04 07:07:03 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[5]毫秒 "deviceName": "Waterer_01",
2026-06-04 07:07:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[22]毫秒 "powerLevel": 50,
2026-06-04 07:07:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/schedulelist],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] "charging": 0
2026-06-04 07:07:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/schedulelist],耗时:[12]毫秒 }
2026-06-04 07:07:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/schedulelist],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["2"]}] 2026-06-13 08:59:34 [mqtt-consumer-7] INFO o.d.a.handler.ScheduleFinishHandler - [MQTT] 浇水排程上报已保存 时间=2026-06-13 08:59:34 设备编号=01 消息体={
2026-06-04 07:07:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/schedulelist],耗时:[15]毫秒 "deviceNo": "01",
2026-06-04 07:07:11 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "startWeek": 5,
2026-06-04 07:07:11 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[4]毫秒 "startTime": "2026-06-11 8:20",
2026-06-04 07:07:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/dataStatistics],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "durationMin": 20,
2026-06-04 07:07:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/dataStatistics],耗时:[8]毫秒 "triggerType": "mqtt on",
2026-06-04 07:07:14 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/waterLogList],参数类型[param],参数:[{"param[endTime]":[""],"param[beginTime]":[""],"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] "triggerON":"mqtt on",
2026-06-04 07:07:14 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/waterLogList],耗时:[16]毫秒 "triggerOFF":"mqtt off"
2026-06-04 07:07:16 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] }
2026-06-04 07:07:16 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[4]毫秒 2026-06-13 08:59:34 [mqtt-consumer-8] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 08:59:34 设备编号=01 消息体={
2026-06-04 07:07:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "deviceNo": "01",
2026-06-04 07:07:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[6]毫秒 "deviceName": "Waterer_01",
2026-06-04 07:07:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "powerLevel": 63,
2026-06-04 07:07:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[6]毫秒 "deviceEm": "11667796A-Y43-2606",
2026-06-04 07:38:43 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "deviceSn": "260601",
2026-06-04 07:38:43 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] "deviceMac": "dc:da:0c:fa:28:9c",
2026-06-04 07:38:43 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[11]毫秒 "version": "1.0"
2026-06-04 07:38:43 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[28]毫秒 }
2026-06-04 07:38:46 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2059509304398151682],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 09:00:02 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],参数
2026-06-04 07:38:46 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2059509304398151682],耗时:[7]毫秒 2026-06-13 09:00:02 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[19]毫秒
2026-06-04 07:38:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 09:00:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],参数
2026-06-04 07:38:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[4]毫秒 2026-06-13 09:00:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-04 08:11:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:00:52 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 08:11:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:00:52 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[9]毫秒
2026-06-04 08:11:32 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2060265891827957762],无参数 2026-06-13 09:01:28 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:11:32 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2060265891827957762],耗时:[8]毫秒 2026-06-13 09:01:28 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 08:11:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:02:04 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 08:11:34 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒 2026-06-13 09:02:04 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 08:11:35 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2060266873861324801],无参数 2026-06-13 09:02:06 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:11:35 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2060266873861324801],耗时:[3]毫秒 2026-06-13 09:02:06 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-04 08:11:37 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:05:12 [schedule-pool-10] INFO o.d.app.task.DeviceOfflineCheckTask - [设备离线] 检测到 1 台设备离线,已更新数据库状态,设备编号:[01]
2026-06-04 08:11:37 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-04 08:11:38 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2060266816596492289],无参数 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[3]毫秒
2026-06-04 08:11:38 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2060266816596492289],耗时:[2]毫秒 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:11:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[PUT /app/v1/switchDevice],参数类型[json],参数:[{"id":"2060266816596492289","workStatus":"1","durationMin":"60"}] 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 08:11:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[PUT /app/v1/switchDevice],耗时:[13]毫秒 2026-06-13 09:06:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:12:04 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:06:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-04 08:12:04 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-04 08:21:57 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[8]毫秒
2026-06-04 08:21:57 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:22:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-04 08:22:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒 2026-06-13 09:08:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:27:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 08:27:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[29]毫秒 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"dc:da:0c:fa:28:9c"}]
2026-06-04 09:53:05 [MQTT Rec: water-server] WARN org.dromara.mqtt.MqttClientManager - [MQTT] 连接断开: 已断开连接 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[2]毫秒
2026-06-04 10:08:12 [MQTT Rec: water-server] WARN org.dromara.mqtt.MqttClientManager - [MQTT] 连接断开: 已断开连接 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 10:13:27 [MQTT Rec: water-server] WARN org.dromara.mqtt.MqttClientManager - [MQTT] 连接断开: 已断开连接 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-04 10:28:49 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:28:49 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[49]毫秒 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 10:31:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 10:31:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[27]毫秒 2026-06-13 09:08:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-04 10:44:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:49 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:44:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[23]毫秒 2026-06-13 09:08:49 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 10:50:27 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 10:50:27 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:08:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-04 10:52:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:52:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[25]毫秒 2026-06-13 09:08:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[3]毫秒
2026-06-04 10:52:47 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 10:52:47 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:08:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-04 10:53:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:09:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:53:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒 2026-06-13 09:09:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 11:05:23 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:09:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 11:05:23 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒 2026-06-13 09:09:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 11:08:20 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数 2026-06-13 09:11:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-04 11:08:20 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:11:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-04 11:17:17 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:12:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 11:17:17 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[21]毫秒 2026-06-13 09:12:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-04 11:20:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:12:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 11:20:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:12:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[3]毫秒
2026-06-04 11:21:29 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:13:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 11:21:29 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:13:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-04 11:22:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数 2026-06-13 09:15:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-04 11:22:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:15:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-04 11:23:26 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:15:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-04 11:23:26 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:15:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[5]毫秒
2026-06-13 09:15:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/scheduleDeviceList/2062827585485807618],无参数
2026-06-13 09:15:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/scheduleDeviceList/2062827585485807618],耗时:[7]毫秒
2026-06-13 09:15:36 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addScheduleDevice],参数类型[JSON],参数:[{"scheduleId":"2062827585485807618","deviceNos":["1779176626954"]}]
2026-06-13 09:15:36 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addScheduleDevice],耗时:[20]毫秒
2026-06-13 09:15:37 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:37 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[11]毫秒
2026-06-13 09:15:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[4]毫秒
2026-06-13 09:15:46 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:46 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[6]毫秒
2026-06-13 09:15:50 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:15:50 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-13 09:15:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[6]毫秒
2026-06-13 09:17:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:17:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 09:17:47 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:17:47 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[8]毫秒
2026-06-13 09:17:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:17:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[9]毫秒
2026-06-13 09:17:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:17:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-13 09:24:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:24:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 09:24:17 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:24:17 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:26:05 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:26:05 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:26:07 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:26:07 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:27:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:27:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:27:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:27:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[22]毫秒
2026-06-13 09:46:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:46:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[26]毫秒
2026-06-13 09:46:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:46:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[16]毫秒
2026-06-13 09:46:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["fuck"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:46:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[20]毫秒
2026-06-13 09:46:58 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:46:58 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 09:47:10 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["fuck"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:47:10 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-13 09:47:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:47:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:49:16 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:49:16 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 09:49:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:49:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 09:49:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["666"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:49:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[12]毫秒
2026-06-13 09:49:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:49:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 09:51:11 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/waterLogList],参数类型[表单参数],参数:[{"pageNum":["1"]}]
2026-06-13 09:51:11 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/waterLogList],耗时:[34]毫秒
2026-06-13 09:51:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:51:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:51:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:51:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 09:51:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:51:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 09:51:33 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 09:51:33 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[7]毫秒
2026-06-13 09:53:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:53:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 09:53:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:53:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"dc:da:0c:fa:28:9c"}]
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[4]毫秒
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 09:54:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:54:21 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"dc:da:0c:fa:28:9e"}]
2026-06-13 09:54:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[1]毫秒
2026-06-13 09:54:21 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:54:46 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:46 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[4]毫秒
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 09:55:12 [mqtt-consumer-16] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 09:55:12 设备编号=01 消息体={
"deviceNo": "01",
"deviceName": "Waterer_01",
"powerLevel": 48,
"deviceEm": "11667796A-Y43-2606",
"deviceSn": "260601",
"deviceMac": "dc:da:0c:fa:28:9c",
"version": "1.0"
}
2026-06-13 09:55:12 [mqtt-consumer-13] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 09:55:12 设备编号=01 消息体={
"deviceName": "Waterer_01",
"powerLevel": 48,
"charging": 0
}
2026-06-13 09:55:12 [mqtt-consumer-14] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=2ab3a6e58d1b4a89919d218fda63b3f7 状态=null
2026-06-13 09:55:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addDevice],参数类型[JSON],参数:[{"deviceName":"浇水设备007","wifiName":"研发部","wifiPassword":"ckkj123456","macAddress":"DC:DA:0C:FA:28:9E","deviceNo":"01"}]
2026-06-13 09:55:38 [XNIO-1 task-4] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 消息已发布 主题=/01/subscriber/cmd 消息体={"commandType":"bindDevice","deviceNo":"01","cmd":-1,"commandId":"73669072eb924e9f9772d80fb9be57f8","bindStatus":true}
2026-06-13 09:55:38 [XNIO-1 task-4] INFO o.d.a.s.i.DeviceCommandServiceImpl - [命令] 命令已下发 设备编号=01 命令类型=bindDevice 命令编号=73669072eb924e9f9772d80fb9be57f8
2026-06-13 09:55:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addDevice],耗时:[39]毫秒
2026-06-13 09:55:38 [mqtt-consumer-11] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=73669072eb924e9f9772d80fb9be57f8 状态=null
2026-06-13 09:55:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 09:55:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-13 09:55:59 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:56:00 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[1]毫秒
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 09:57:15 [mqtt-consumer-8] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 09:57:15 设备编号=01 消息体={
"deviceNo": "01",
"deviceName": "Waterer_01",
"powerLevel": 48,
"deviceEm": "11667796A-Y43-2606",
"deviceSn": "260601",
"deviceMac": "dc:da:0c:fa:28:9c",
"version": "1.0"
}
2026-06-13 09:57:15 [mqtt-consumer-8] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 09:57:15 设备编号=01 消息体={
"deviceName": "Waterer_01",
"powerLevel": 48,
"charging": 0
}
2026-06-13 09:57:15 [mqtt-consumer-12] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=2ab3a6e58d1b4a89919d218fda63b3f7 状态=null
2026-06-13 09:57:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addDevice],参数类型[JSON],参数:[{"deviceName":"智能浇水","wifiName":"测试机","wifiPassword":"66668888","macAddress":"DC:DA:0C:FA:28:9E","deviceNo":"01"}]
2026-06-13 09:57:39 [XNIO-1 task-4] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 消息已发布 主题=/01/subscriber/cmd 消息体={"commandType":"bindDevice","deviceNo":"01","cmd":-1,"commandId":"a783262582fd41fcb09d59d55a07c9e3","bindStatus":true}
2026-06-13 09:57:39 [XNIO-1 task-4] INFO o.d.a.s.i.DeviceCommandServiceImpl - [命令] 命令已下发 设备编号=01 命令类型=bindDevice 命令编号=a783262582fd41fcb09d59d55a07c9e3
2026-06-13 09:57:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addDevice],耗时:[35]毫秒
2026-06-13 09:57:40 [mqtt-consumer-3] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=a783262582fd41fcb09d59d55a07c9e3 状态=null
2026-06-13 09:57:40 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 09:57:40 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-13 09:59:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:59:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:59:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:59:58 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:02:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:02:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:02:11 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 10:02:11 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[3]毫秒
2026-06-13 10:02:12 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:02:12 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-13 10:03:42 [schedule-pool-5] INFO o.d.app.task.DeviceOfflineCheckTask - [设备离线] 检测到 1 台设备离线,已更新数据库状态,设备编号:[01]
2026-06-13 10:04:26 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:04:26 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:11:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 10:11:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-13 10:12:03 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:12:03 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 10:12:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:12:23 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["fuck"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:23 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[13]毫秒
2026-06-13 10:12:28 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:28 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 10:12:36 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:36 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[7]毫秒
2026-06-13 10:12:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["66"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 10:12:42 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:42 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:16:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:16:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[7]毫秒
2026-06-13 10:16:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:16:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:16:46 [mqtt-consumer-1] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 10:16:46 设备编号=01 消息体={
"deviceNo": "01",
"deviceName": "Waterer_01",
"powerLevel": 48,
"deviceEm": "11667796A-Y43-2606",
"deviceSn": "260601",
"deviceMac": "dc:da:0c:fa:28:9c",
"version": "1.0"
}
2026-06-13 10:16:46 [mqtt-consumer-16] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 10:16:46 设备编号=01 消息体={
"deviceName": "Waterer_01",
"powerLevel": 48,
"charging": 0
}
2026-06-13 10:16:47 [mqtt-consumer-16] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=2ab3a6e58d1b4a89919d218fda63b3f7 状态=null
2026-06-13 10:16:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addDevice],参数类型[JSON],参数:[{"deviceName":"还是浇水设备","wifiName":"研发部","wifiPassword":"ckkj123456","macAddress":"DC:DA:0C:FA:28:9E","deviceNo":"01"}]
2026-06-13 10:16:57 [XNIO-1 task-4] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 消息已发布 主题=/01/subscriber/cmd 消息体={"commandType":"bindDevice","deviceNo":"01","cmd":-1,"commandId":"00119ae0fc844c1c88d61052e1ded66b","bindStatus":true}
2026-06-13 10:16:57 [XNIO-1 task-4] INFO o.d.a.s.i.DeviceCommandServiceImpl - [命令] 命令已下发 设备编号=01 命令类型=bindDevice 命令编号=00119ae0fc844c1c88d61052e1ded66b
2026-06-13 10:16:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addDevice],耗时:[36]毫秒
2026-06-13 10:16:57 [mqtt-consumer-6] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=00119ae0fc844c1c88d61052e1ded66b 状态=null
2026-06-13 10:16:58 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 10:16:58 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[5]毫秒
2026-06-13 10:17:04 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:17:04 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:18:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:18:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:23:12 [schedule-pool-4] INFO o.d.app.task.DeviceOfflineCheckTask - [设备离线] 检测到 1 台设备离线,已更新数据库状态,设备编号:[01]
2026-06-13 10:34:56 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:34:56 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[22]毫秒
2026-06-13 10:34:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:34:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:37:22 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:37:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:37:22 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-13 10:37:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:38:02 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/waterLogList],参数类型[表单参数],参数:[{"pageNum":["1"]}]
2026-06-13 10:38:02 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/waterLogList],耗时:[14]毫秒
2026-06-13 10:38:06 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:38:06 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/waterLogList],参数类型[表单参数],参数:[{"pageNum":["1"]}]
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/waterLogList],耗时:[12]毫秒
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:38:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:38:13 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/getUserInfo],无参数
2026-06-13 10:38:13 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[9]毫秒
2026-06-13 10:38:14 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:14 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:38:16 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/getUserInfo],无参数
2026-06-13 10:38:16 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[6]毫秒
2026-06-13 10:38:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:19 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/getUserInfo],无参数
2026-06-13 10:38:19 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[5]毫秒
2026-06-13 10:38:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:38:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒

View File

@@ -1,6 +1 @@
2026-06-03 08:07:41 [MQTT Ping: water-server] ERROR o.e.p.c.mqttv3.internal.ClientState - water-server: Timed out as no write activity, keepAlive=60,000,000,000 lastOutboundActivity=1,242,491,392,259,400 lastInboundActivity=1,242,491,421,357,900 time=1,293,117,233,745,900 lastPing=1,242,491,392,263,200 2026-06-13 08:59:32 [MQTT Ping: water-server-8081] ERROR o.e.p.c.mqttv3.internal.ClientState - water-server-8081: Timed out as no write activity, keepAlive=60,000,000,000 lastOutboundActivity=150,167,329,517,400 lastInboundActivity=150,167,356,610,400 time=204,053,810,500,400 lastPing=150,167,329,537,400
2026-06-03 11:22:23 [XNIO-1 task-2] ERROR o.d.c.s.h.SaTokenExceptionHandler - 请求地址'/app/v1/deviceList',认证失败'token 已被冻结eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJhcHBfdXNlcjoyMDU2NTQzNTA3NTE0MDk3NjY1Iiwicm5TdHIiOiI4QnM4c2RiWkdOMEFPRTVQUHZUeTFCYWtzV0ozQVVHaSIsImNsaWVudGlkIjoiNDI4YTgzMTBjZDQ0Mjc1N2FlNjk5ZGY1ZDg5NGYwNTEiLCJ0ZW5hbnRJZCI6IjAwMDAwMCIsInVzZXJJZCI6MjA1NjU0MzUwNzUxNDA5NzY2NX0.kKJvH80xg1I8iFL0-v4_aMCEGsuwYm87T19UQ7cu3l4',无法访问系统资源
2026-06-03 11:23:01 [XNIO-1 task-2] ERROR o.d.c.w.h.GlobalExceptionHandler - 访问过于频繁,请稍候再试
2026-06-03 11:45:09 [XNIO-1 task-2] ERROR o.d.c.s.h.SaTokenExceptionHandler - 请求地址'/app/v1/deviceList',认证失败'token 已被冻结eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJhcHBfdXNlcjoyMDU2NTQzNTA3NTE0MDk3NjY1Iiwicm5TdHIiOiJ2alU3OVV0dWhsYjc4azhhUDNRazM4YnJ3QVJ4MGt6MCIsImNsaWVudGlkIjoiNDI4YTgzMTBjZDQ0Mjc1N2FlNjk5ZGY1ZDg5NGYwNTEiLCJ0ZW5hbnRJZCI6IjAwMDAwMCIsInVzZXJJZCI6MjA1NjU0MzUwNzUxNDA5NzY2NSwidXNlck5hbWUiOiIxMzMwNTM3NjA1NCJ9.WkM2XneftUNQbnIm69pDDEAzocsvHZlkv6e5SRS4_48',无法访问系统资源
2026-06-03 17:44:04 [XNIO-1 task-3] ERROR o.d.c.w.h.GlobalExceptionHandler - 参数校验异常
2026-06-03 22:52:30 [XNIO-1 task-2] ERROR o.d.c.w.h.GlobalExceptionHandler - 密码输入错误1次

View File

@@ -1,88 +1,323 @@
2026-06-04 07:06:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已重新连接ssl://service.reinkun.com:8883
2026-06-04 07:06:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/finish/schedule 服务质量等级=1
2026-06-04 07:06:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[12]毫秒 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/register 服务质量等级=1
2026-06-04 07:06:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[36]毫秒 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/power 服务质量等级=1
2026-06-04 07:06:57 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2062185771665858562],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/ack 服务质量等级=1
2026-06-04 07:06:57 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2062185771665858562],耗时:[13]毫秒 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/finish/key 服务质量等级=1
2026-06-04 07:06:59 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2062185771665858562],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 08:59:34 [MQTT Call: water-server-8081] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 已订阅 主题=/+/publish/error 服务质量等级=1
2026-06-04 07:06:59 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2062185771665858562],耗时:[5]毫秒 2026-06-13 08:59:34 [mqtt-consumer-4] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 08:59:34 设备编号=01 消息体={
2026-06-04 07:07:01 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[DELETE /app/v1/deleteDevice/2062185771665858562],参数类型[json],参数:[{"clientId":"32324a04c0175c8d61ac8a282553aea1"}] "deviceName": "Waterer_01",
2026-06-04 07:07:01 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[DELETE /app/v1/deleteDevice/2062185771665858562],耗时:[10]毫秒 "powerLevel": 50,
2026-06-04 07:07:03 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "charging": 0
2026-06-04 07:07:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] }
2026-06-04 07:07:03 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[5]毫秒 2026-06-13 08:59:34 [mqtt-consumer-7] INFO o.d.a.handler.ScheduleFinishHandler - [MQTT] 浇水排程上报已保存 时间=2026-06-13 08:59:34 设备编号=01 消息体={
2026-06-04 07:07:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[22]毫秒 "deviceNo": "01",
2026-06-04 07:07:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/schedulelist],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] "startWeek": 5,
2026-06-04 07:07:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/schedulelist],耗时:[12]毫秒 "startTime": "2026-06-11 8:20",
2026-06-04 07:07:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/schedulelist],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["2"]}] "durationMin": 20,
2026-06-04 07:07:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/schedulelist],耗时:[15]毫秒 "triggerType": "mqtt on",
2026-06-04 07:07:11 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "triggerON":"mqtt on",
2026-06-04 07:07:11 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[4]毫秒 "triggerOFF":"mqtt off"
2026-06-04 07:07:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/dataStatistics],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] }
2026-06-04 07:07:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/dataStatistics],耗时:[8]毫秒 2026-06-13 08:59:34 [mqtt-consumer-8] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 08:59:34 设备编号=01 消息体={
2026-06-04 07:07:14 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/waterLogList],参数类型[param],参数:[{"param[endTime]":[""],"param[beginTime]":[""],"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] "deviceNo": "01",
2026-06-04 07:07:14 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/waterLogList],耗时:[16]毫秒 "deviceName": "Waterer_01",
2026-06-04 07:07:16 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "powerLevel": 63,
2026-06-04 07:07:16 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[4]毫秒 "deviceEm": "11667796A-Y43-2606",
2026-06-04 07:07:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "deviceSn": "260601",
2026-06-04 07:07:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[6]毫秒 "deviceMac": "dc:da:0c:fa:28:9c",
2026-06-04 07:07:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] "version": "1.0"
2026-06-04 07:07:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[6]毫秒 }
2026-06-04 07:38:43 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 09:00:02 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],参数
2026-06-04 07:38:43 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"],"pageSize":["10"],"pageNum":["1"]}] 2026-06-13 09:00:02 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[19]毫秒
2026-06-04 07:38:43 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[11]毫秒 2026-06-13 09:00:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 07:38:43 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[28]毫秒 2026-06-13 09:00:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-04 07:38:46 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2059509304398151682],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 09:00:52 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 07:38:46 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2059509304398151682],耗时:[7]毫秒 2026-06-13 09:00:52 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[9]毫秒
2026-06-04 07:38:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/getUserInfo],参数类型[param],参数:[{"clientId":["32324a04c0175c8d61ac8a282553aea1"]}] 2026-06-13 09:01:28 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],参数
2026-06-04 07:38:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[4]毫秒 2026-06-13 09:01:28 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 08:11:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:02:04 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 08:11:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:02:04 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 08:11:32 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2060265891827957762],无参数 2026-06-13 09:02:06 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:11:32 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2060265891827957762],耗时:[8]毫秒 2026-06-13 09:02:06 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-04 08:11:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:05:12 [schedule-pool-10] INFO o.d.app.task.DeviceOfflineCheckTask - [设备离线] 检测到 1 台设备离线,已更新数据库状态,设备编号:[01]
2026-06-04 08:11:34 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-04 08:11:35 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2060266873861324801],无参数 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[3]毫秒
2026-06-04 08:11:35 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2060266873861324801],耗时:[3]毫秒 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:11:37 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:05:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 08:11:37 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:06:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:11:38 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/device/2060266816596492289],无参数 2026-06-13 09:06:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-04 08:11:38 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/device/2060266816596492289],耗时:[2]毫秒 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-04 08:11:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[PUT /app/v1/switchDevice],参数类型[json],参数:[{"id":"2060266816596492289","workStatus":"1","durationMin":"60"}] 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[8]毫秒
2026-06-04 08:11:51 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[PUT /app/v1/switchDevice],耗时:[13]毫秒 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:12:04 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:07:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-04 08:12:04 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:08:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:21:57 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 08:21:57 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"dc:da:0c:fa:28:9c"}]
2026-06-04 08:22:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[2]毫秒
2026-06-04 08:22:06 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 08:27:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-04 08:27:53 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[29]毫秒 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:28:49 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 10:28:49 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[49]毫秒 2026-06-13 09:08:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 10:31:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-04 10:31:13 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[27]毫秒 2026-06-13 09:08:49 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:44:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:49 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 10:44:03 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[23]毫秒 2026-06-13 09:08:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 10:50:27 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-04 10:50:27 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:08:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:52:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[3]毫秒
2026-06-04 10:52:28 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[25]毫秒 2026-06-13 09:08:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 10:52:47 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:08:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-04 10:52:47 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:09:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 10:53:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:09:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-04 10:53:33 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒 2026-06-13 09:09:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 11:05:23 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:09:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-04 11:05:23 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒 2026-06-13 09:11:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-04 11:08:20 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:11:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-04 11:08:20 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:12:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 11:17:17 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:12:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-04 11:17:17 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[21]毫秒 2026-06-13 09:12:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-04 11:20:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:12:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[3]毫秒
2026-06-04 11:20:07 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:13:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-04 11:21:29 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:13:56 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-04 11:21:29 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:15:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-04 11:22:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:15:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-04 11:22:24 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒 2026-06-13 09:15:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-04 11:23:26 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /app/v1/deviceList],无参数 2026-06-13 09:15:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[5]毫秒
2026-06-04 11:23:26 [XNIO-1 task-2] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [PLUS]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒 2026-06-13 09:15:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/scheduleDeviceList/2062827585485807618],无参数
2026-06-13 09:15:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/scheduleDeviceList/2062827585485807618],耗时:[7]毫秒
2026-06-13 09:15:36 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addScheduleDevice],参数类型[JSON],参数:[{"scheduleId":"2062827585485807618","deviceNos":["1779176626954"]}]
2026-06-13 09:15:36 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addScheduleDevice],耗时:[20]毫秒
2026-06-13 09:15:37 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:37 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[11]毫秒
2026-06-13 09:15:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[4]毫秒
2026-06-13 09:15:46 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:46 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[6]毫秒
2026-06-13 09:15:50 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:15:50 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-13 09:15:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:15:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[6]毫秒
2026-06-13 09:17:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:17:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 09:17:47 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:17:47 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[8]毫秒
2026-06-13 09:17:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedule/2062827585485807618],无参数
2026-06-13 09:17:48 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedule/2062827585485807618],耗时:[9]毫秒
2026-06-13 09:17:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:17:51 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-13 09:24:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:24:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 09:24:17 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:24:17 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:26:05 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:26:05 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:26:07 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:26:07 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:27:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:27:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:27:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:27:40 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[22]毫秒
2026-06-13 09:46:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:46:39 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[26]毫秒
2026-06-13 09:46:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:46:42 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[16]毫秒
2026-06-13 09:46:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["fuck"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:46:54 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[20]毫秒
2026-06-13 09:46:58 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:46:58 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 09:47:10 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["fuck"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:47:10 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[11]毫秒
2026-06-13 09:47:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:47:14 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:49:16 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:49:16 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 09:49:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:49:18 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 09:49:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["666"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:49:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[12]毫秒
2026-06-13 09:49:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:49:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 09:51:11 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/waterLogList],参数类型[表单参数],参数:[{"pageNum":["1"]}]
2026-06-13 09:51:11 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/waterLogList],耗时:[34]毫秒
2026-06-13 09:51:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 09:51:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 09:51:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:51:24 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 09:51:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:51:32 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 09:51:33 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 09:51:33 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[7]毫秒
2026-06-13 09:53:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:53:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 09:53:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:53:23 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"dc:da:0c:fa:28:9c"}]
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[4]毫秒
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:53:53 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 09:54:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:12 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:54:21 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"dc:da:0c:fa:28:9e"}]
2026-06-13 09:54:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[1]毫秒
2026-06-13 09:54:21 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:54:46 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:46 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[4]毫秒
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:54:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 09:55:12 [mqtt-consumer-16] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 09:55:12 设备编号=01 消息体={
"deviceNo": "01",
"deviceName": "Waterer_01",
"powerLevel": 48,
"deviceEm": "11667796A-Y43-2606",
"deviceSn": "260601",
"deviceMac": "dc:da:0c:fa:28:9c",
"version": "1.0"
}
2026-06-13 09:55:12 [mqtt-consumer-13] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 09:55:12 设备编号=01 消息体={
"deviceName": "Waterer_01",
"powerLevel": 48,
"charging": 0
}
2026-06-13 09:55:12 [mqtt-consumer-14] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=2ab3a6e58d1b4a89919d218fda63b3f7 状态=null
2026-06-13 09:55:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addDevice],参数类型[JSON],参数:[{"deviceName":"浇水设备007","wifiName":"研发部","wifiPassword":"ckkj123456","macAddress":"DC:DA:0C:FA:28:9E","deviceNo":"01"}]
2026-06-13 09:55:38 [XNIO-1 task-4] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 消息已发布 主题=/01/subscriber/cmd 消息体={"commandType":"bindDevice","deviceNo":"01","cmd":-1,"commandId":"73669072eb924e9f9772d80fb9be57f8","bindStatus":true}
2026-06-13 09:55:38 [XNIO-1 task-4] INFO o.d.a.s.i.DeviceCommandServiceImpl - [命令] 命令已下发 设备编号=01 命令类型=bindDevice 命令编号=73669072eb924e9f9772d80fb9be57f8
2026-06-13 09:55:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addDevice],耗时:[39]毫秒
2026-06-13 09:55:38 [mqtt-consumer-11] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=73669072eb924e9f9772d80fb9be57f8 状态=null
2026-06-13 09:55:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 09:55:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-13 09:55:59 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:56:00 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/bindDeviceStatus],参数类型[JSON],参数:[{"macAddress":"DC:DA:0C:FA:28:9E"}]
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/bindDeviceStatus],耗时:[1]毫秒
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:56:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 09:57:15 [mqtt-consumer-8] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 09:57:15 设备编号=01 消息体={
"deviceNo": "01",
"deviceName": "Waterer_01",
"powerLevel": 48,
"deviceEm": "11667796A-Y43-2606",
"deviceSn": "260601",
"deviceMac": "dc:da:0c:fa:28:9c",
"version": "1.0"
}
2026-06-13 09:57:15 [mqtt-consumer-8] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 09:57:15 设备编号=01 消息体={
"deviceName": "Waterer_01",
"powerLevel": 48,
"charging": 0
}
2026-06-13 09:57:15 [mqtt-consumer-12] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=2ab3a6e58d1b4a89919d218fda63b3f7 状态=null
2026-06-13 09:57:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addDevice],参数类型[JSON],参数:[{"deviceName":"智能浇水","wifiName":"测试机","wifiPassword":"66668888","macAddress":"DC:DA:0C:FA:28:9E","deviceNo":"01"}]
2026-06-13 09:57:39 [XNIO-1 task-4] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 消息已发布 主题=/01/subscriber/cmd 消息体={"commandType":"bindDevice","deviceNo":"01","cmd":-1,"commandId":"a783262582fd41fcb09d59d55a07c9e3","bindStatus":true}
2026-06-13 09:57:39 [XNIO-1 task-4] INFO o.d.a.s.i.DeviceCommandServiceImpl - [命令] 命令已下发 设备编号=01 命令类型=bindDevice 命令编号=a783262582fd41fcb09d59d55a07c9e3
2026-06-13 09:57:39 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addDevice],耗时:[35]毫秒
2026-06-13 09:57:40 [mqtt-consumer-3] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=a783262582fd41fcb09d59d55a07c9e3 状态=null
2026-06-13 09:57:40 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 09:57:40 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-13 09:59:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:59:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 09:59:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 09:59:58 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:02:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:02:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:02:11 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 10:02:11 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[3]毫秒
2026-06-13 10:02:12 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:02:12 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-13 10:03:42 [schedule-pool-5] INFO o.d.app.task.DeviceOfflineCheckTask - [设备离线] 检测到 1 台设备离线,已更新数据库状态,设备编号:[01]
2026-06-13 10:04:26 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:04:26 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:11:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 10:11:55 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[2]毫秒
2026-06-13 10:12:03 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:12:03 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 10:12:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:12:23 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["fuck"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:23 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[13]毫秒
2026-06-13 10:12:28 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:28 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 10:12:36 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:36 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[7]毫秒
2026-06-13 10:12:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":["66"],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:38 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[10]毫秒
2026-06-13 10:12:42 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:12:42 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:16:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:16:20 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[7]毫秒
2026-06-13 10:16:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:16:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:16:46 [mqtt-consumer-1] INFO o.d.a.handler.DeviceRegisterHandler - [MQTT] 设备注册 时间=2026-06-13 10:16:46 设备编号=01 消息体={
"deviceNo": "01",
"deviceName": "Waterer_01",
"powerLevel": 48,
"deviceEm": "11667796A-Y43-2606",
"deviceSn": "260601",
"deviceMac": "dc:da:0c:fa:28:9c",
"version": "1.0"
}
2026-06-13 10:16:46 [mqtt-consumer-16] INFO o.d.app.handler.DeviceDataHandler - [MQTT] 设备电量更新 时间=2026-06-13 10:16:46 设备编号=01 消息体={
"deviceName": "Waterer_01",
"powerLevel": 48,
"charging": 0
}
2026-06-13 10:16:47 [mqtt-consumer-16] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=2ab3a6e58d1b4a89919d218fda63b3f7 状态=null
2026-06-13 10:16:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[POST /app/v1/addDevice],参数类型[JSON],参数:[{"deviceName":"还是浇水设备","wifiName":"研发部","wifiPassword":"ckkj123456","macAddress":"DC:DA:0C:FA:28:9E","deviceNo":"01"}]
2026-06-13 10:16:57 [XNIO-1 task-4] INFO org.dromara.mqtt.MqttClientManager - [MQTT] 消息已发布 主题=/01/subscriber/cmd 消息体={"commandType":"bindDevice","deviceNo":"01","cmd":-1,"commandId":"00119ae0fc844c1c88d61052e1ded66b","bindStatus":true}
2026-06-13 10:16:57 [XNIO-1 task-4] INFO o.d.a.s.i.DeviceCommandServiceImpl - [命令] 命令已下发 设备编号=01 命令类型=bindDevice 命令编号=00119ae0fc844c1c88d61052e1ded66b
2026-06-13 10:16:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[POST /app/v1/addDevice],耗时:[36]毫秒
2026-06-13 10:16:57 [mqtt-consumer-6] INFO o.dromara.mqtt.MqttCommandAckService - [MQTT] 收到命令确认 设备编号=01 命令编号=00119ae0fc844c1c88d61052e1ded66b 状态=null
2026-06-13 10:16:58 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/device/01],无参数
2026-06-13 10:16:58 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/device/01],耗时:[5]毫秒
2026-06-13 10:17:04 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:17:04 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:18:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:18:48 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:23:12 [schedule-pool-4] INFO o.d.app.task.DeviceOfflineCheckTask - [设备离线] 检测到 1 台设备离线,已更新数据库状态,设备编号:[01]
2026-06-13 10:34:56 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:34:56 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[22]毫秒
2026-06-13 10:34:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:34:57 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:35:05 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:37:22 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:37:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:37:22 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[18]毫秒
2026-06-13 10:37:22 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:38:02 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/waterLogList],参数类型[表单参数],参数:[{"pageNum":["1"]}]
2026-06-13 10:38:02 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/waterLogList],耗时:[14]毫秒
2026-06-13 10:38:06 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:38:06 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/waterLogList],参数类型[表单参数],参数:[{"pageNum":["1"]}]
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/waterLogList],耗时:[12]毫秒
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/schedulelist],参数类型[表单参数],参数:[{"name":[""],"pageSize":["10"],"pageNum":["1"]}]
2026-06-13 10:38:08 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/schedulelist],耗时:[9]毫秒
2026-06-13 10:38:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:09 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[15]毫秒
2026-06-13 10:38:13 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/getUserInfo],无参数
2026-06-13 10:38:13 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[9]毫秒
2026-06-13 10:38:14 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:14 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:38:16 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/getUserInfo],无参数
2026-06-13 10:38:16 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[6]毫秒
2026-06-13 10:38:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:19 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/getUserInfo],无参数
2026-06-13 10:38:19 [XNIO-1 task-4] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/getUserInfo],耗时:[5]毫秒
2026-06-13 10:38:19 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[16]毫秒
2026-06-13 10:38:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:21 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[17]毫秒
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:25 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[13]毫秒
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]开始请求 => URL[GET /app/v1/deviceList],无参数
2026-06-13 10:38:27 [XNIO-1 task-3] INFO o.d.c.w.i.PlusWebInvokeTimeInterceptor - [请求]结束请求 => URL[GET /app/v1/deviceList],耗时:[14]毫秒

View File

@@ -18,7 +18,7 @@ public class DromaraApplication {
SpringApplication application = new SpringApplication(DromaraApplication.class); SpringApplication application = new SpringApplication(DromaraApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048)); application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args); application.run(args);
log.info("(♥◠‿◠)ノ゙ water-Vue-Plus启动成功 ლ(´ڡ`ლ)゙"); log.info("(♥◠‿◠)ノ゙ 系统启动成功 ლ(´ڡ`ლ)゙");
} }
} }

View File

@@ -98,7 +98,7 @@ public class AuthController {
SysClientVo client = clientService.queryByClientId(clientId); SysClientVo client = clientService.queryByClientId(clientId);
// 查询不到 client 或 client 内不包含 grantType // 查询不到 client 或 client 内不包含 grantType
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); log.info("客户端ID{} 认证类型:{} 异常", clientId, grantType);
return R.fail(MessageUtils.message("auth.grant.type.error")); return R.fail(MessageUtils.message("auth.grant.type.error"));
} else if (!SystemConstants.NORMAL.equals(client.getStatus())) { } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
return R.fail(MessageUtils.message("auth.grant.type.blocked")); return R.fail(MessageUtils.message("auth.grant.type.blocked"));

View File

@@ -73,7 +73,7 @@ public class UserActionListener implements SaTokenListener {
SpringUtils.context().publishEvent(logininforEvent); SpringUtils.context().publishEvent(logininforEvent);
// 更新登录信息 // 更新登录信息
loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); log.info("用户登录用户ID:{},令牌:{}", loginId, tokenValue);
} }
/** /**
@@ -85,7 +85,7 @@ public class UserActionListener implements SaTokenListener {
TenantHelper.dynamic(tenantId, () -> { TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
}); });
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); log.info("用户注销用户ID:{},令牌:{}", loginId, tokenValue);
} }
/** /**
@@ -97,7 +97,7 @@ public class UserActionListener implements SaTokenListener {
TenantHelper.dynamic(tenantId, () -> { TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
}); });
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); log.info("用户被踢下线用户ID:{},令牌:{}", loginId, tokenValue);
} }
/** /**
@@ -109,7 +109,7 @@ public class UserActionListener implements SaTokenListener {
TenantHelper.dynamic(tenantId, () -> { TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
}); });
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); log.info("用户被顶下线用户ID:{},令牌:{}", loginId, tokenValue);
} }
/** /**

View File

@@ -55,6 +55,9 @@ mqtt:
pending-set-key: "mqtt:command:pending:ids" pending-set-key: "mqtt:command:pending:ids"
device-status-cache-prefix: "mqtt:device:status:" device-status-cache-prefix: "mqtt:device:status:"
device-status-cache-ttl-seconds: 300 device-status-cache-ttl-seconds: 300
offline-check:
enabled: true
interval-ms: 90000
topics: topics:
subscribe: subscribe:

View File

@@ -1 +1,78 @@
#错误消息 #错误消息
operation.success=操作成功
operation.fail=操作失败
query.success=查询成功
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,账户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.email.not.username=邮箱号不一致
user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误
user.mobile.phone.number.not.username=手机号不一致
user.login.success=登录成功
user.register.success=注册成功
user.register.save.error=保存用户 {0} 失败,注册账号已存在
user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,账户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,账户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
tenant.blocked=对不起,您的租户已禁用,请联系管理员
tenant.expired=对不起,您的租户已过期,请联系管理员
##APP
app.device.no.not.blank=设备编号不能为空
app.device.not.exists.or.denied=设备不存在或无权访问
app.schedule.id.not.blank=排程编号不能为空
app.schedule.not.exists.or.denied=排程不存在或无权访问
app.schedule.id.not.blank.request=scheduleId不能为空
app.schedule.devices.not.blank=deviceNos不能为空
app.watering.log.id.not.blank=浇水记录编号不能为空
app.watering.log.not.exists.or.denied=浇水记录不存在或无权访问
app.user.data.denied=没有权限访问用户数据!
app.user.phone.exists=修改用户'{0}'失败,手机号码已存在

View File

@@ -1,4 +1,7 @@
#错误消息 # error messages
operation.success=Operation successful
operation.fail=Operation failed
query.success=Query successful
not.null=* Required fill in not.null=* Required fill in
user.jcaptcha.error=Captcha error user.jcaptcha.error=Captcha error
user.jcaptcha.expire=Captcha invalid user.jcaptcha.expire=Captcha invalid
@@ -6,9 +9,9 @@ user.not.exists=Sorry, your account: {0} does not exist
user.password.not.match=User does not exist/Password error user.password.not.match=User does not exist/Password error
user.password.retry.limit.count=Password input error {0} times user.password.retry.limit.count=Password input error {0} times
user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes
user.password.delete=Sorry, your account{0} has been deleted user.password.delete=Sorry, your account: {0} has been deleted
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
role.blocked=Role disabledplease contact administrators role.blocked=Role disabled, please contact administrators
user.logout.success=Exit successful user.logout.success=Exit successful
length.not.valid=The length must be between {min} and {max} characters length.not.valid=The length must be between {min} and {max} characters
user.username.not.blank=Username cannot be blank user.username.not.blank=Username cannot be blank
@@ -20,29 +23,31 @@ user.password.not.valid=* 5-50 characters
user.password.format.valid=Password must contain uppercase, lowercase, digit, and special character user.password.format.valid=Password must contain uppercase, lowercase, digit, and special character
user.email.not.valid=Mailbox format error user.email.not.valid=Mailbox format error
user.email.not.blank=Mailbox cannot be blank user.email.not.blank=Mailbox cannot be blank
user.email.not.username=Email does not match the current user
user.phonenumber.not.blank=Phone number cannot be blank user.phonenumber.not.blank=Phone number cannot be blank
user.mobile.phone.number.not.valid=Phone number format error user.mobile.phone.number.not.valid=Phone number format error
user.mobile.phone.number.not.username=Phone number does not match the current user
user.login.success=Login successful user.login.success=Login successful
user.register.success=Register successful user.register.success=Register successful
user.register.save.error=Failed to save user {0}, The registered account already exists user.register.save.error=Failed to save user {0}, The registered account already exists
user.register.error=Register failed, please contact system administrator user.register.error=Register failed, please contact system administrator
user.notfound=Please login again user.notfound=Please login again
user.forcelogout=The administrator is forced to exitplease login again user.forcelogout=The administrator is forced to exit, please login again
user.unknown.error=Unknown error, please login again user.unknown.error=Unknown error, please login again
auth.grant.type.error=Auth grant type error auth.grant.type.error=Auth grant type error
auth.grant.type.blocked=Auth grant type disabled auth.grant.type.blocked=Auth grant type disabled
auth.grant.type.not.blank=Auth grant type cannot be blank auth.grant.type.not.blank=Auth grant type cannot be blank
auth.clientid.not.blank=Auth clientid cannot be blank auth.clientid.not.blank=Auth clientid cannot be blank
##文件上传消息 ## upload messages
upload.exceed.maxSize=The uploaded file size exceeds the limit file size<br/>the maximum allowed file size is{0}MB upload.exceed.maxSize=The uploaded file size exceeds the limit file size!<br/>the maximum allowed file size is: {0}MB!
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
##权限 ## permissions
no.permission=You do not have permission to the dataplease contact your administrator to add permissions [{0}] no.permission=You do not have permission to the data, please contact your administrator to add permissions [{0}]
no.create.permission=You do not have permission to create dataplease contact your administrator to add permissions [{0}] no.create.permission=You do not have permission to create data, please contact your administrator to add permissions [{0}]
no.update.permission=You do not have permission to modify dataplease contact your administrator to add permissions [{0}] no.update.permission=You do not have permission to modify data, please contact your administrator to add permissions [{0}]
no.delete.permission=You do not have permission to delete dataplease contact your administrator to add permissions [{0}] no.delete.permission=You do not have permission to delete data, please contact your administrator to add permissions [{0}]
no.export.permission=You do not have permission to export dataplease contact your administrator to add permissions [{0}] no.export.permission=You do not have permission to export data, please contact your administrator to add permissions [{0}]
no.view.permission=You do not have permission to view dataplease contact your administrator to add permissions [{0}] no.view.permission=You do not have permission to view data, please contact your administrator to add permissions [{0}]
repeat.submit.message=Repeat submit is not allowed, please try again later repeat.submit.message=Repeat submit is not allowed, please try again later
rate.limiter.message=Visit too frequently, please try again later rate.limiter.message=Visit too frequently, please try again later
sms.code.not.blank=Sms code cannot be blank sms.code.not.blank=Sms code cannot be blank
@@ -55,8 +60,19 @@ xcx.code.not.blank=Mini program [code] cannot be blank
social.source.not.blank=Social login platform [source] cannot be blank social.source.not.blank=Social login platform [source] cannot be blank
social.code.not.blank=Social login platform [code] cannot be blank social.code.not.blank=Social login platform [code] cannot be blank
social.state.not.blank=Social login platform [state] cannot be blank social.state.not.blank=Social login platform [state] cannot be blank
##租户 ## tenant
tenant.number.not.blank=Tenant number cannot be blank tenant.number.not.blank=Tenant number cannot be blank
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator
tenant.expired=Sorry, your tenant has expired. Please contact the administrator. tenant.expired=Sorry, your tenant has expired. Please contact the administrator.
## APP
app.device.no.not.blank=Device number cannot be blank
app.device.not.exists.or.denied=Device does not exist or access is denied
app.schedule.id.not.blank=Schedule ID cannot be blank
app.schedule.not.exists.or.denied=Schedule does not exist or access is denied
app.schedule.id.not.blank.request=scheduleId cannot be blank
app.schedule.devices.not.blank=deviceNos cannot be blank
app.watering.log.id.not.blank=Watering log ID cannot be blank
app.watering.log.not.exists.or.denied=Watering log does not exist or access is denied
app.user.data.denied=No permission to access user data
app.user.phone.exists=Failed to update user ''{0}'': phone number already exists

View File

@@ -1,4 +1,7 @@
#错误消息 #错误消息
operation.success=操作成功
operation.fail=操作失败
query.success=查询成功
not.null=* 必须填写 not.null=* 必须填写
user.jcaptcha.error=验证码错误 user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效 user.jcaptcha.expire=验证码已失效
@@ -20,8 +23,10 @@ user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符 user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误 user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空 user.email.not.blank=邮箱不能为空
user.email.not.username=邮箱号不一致
user.phonenumber.not.blank=用户手机号不能为空 user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误 user.mobile.phone.number.not.valid=手机号格式错误
user.mobile.phone.number.not.username=手机号不一致
user.login.success=登录成功 user.login.success=登录成功
user.register.success=注册成功 user.register.success=注册成功
user.register.save.error=保存用户 {0} 失败,注册账号已存在 user.register.save.error=保存用户 {0} 失败,注册账号已存在
@@ -60,3 +65,14 @@ tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员 tenant.not.exists=对不起, 您的租户不存在,请联系管理员
tenant.blocked=对不起,您的租户已禁用,请联系管理员 tenant.blocked=对不起,您的租户已禁用,请联系管理员
tenant.expired=对不起,您的租户已过期,请联系管理员 tenant.expired=对不起,您的租户已过期,请联系管理员
##APP
app.device.no.not.blank=设备编号不能为空
app.device.not.exists.or.denied=设备不存在或无权访问
app.schedule.id.not.blank=排程编号不能为空
app.schedule.not.exists.or.denied=排程不存在或无权访问
app.schedule.id.not.blank.request=scheduleId不能为空
app.schedule.devices.not.blank=deviceNos不能为空
app.watering.log.id.not.blank=浇水记录编号不能为空
app.watering.log.not.exists.or.denied=浇水记录不存在或无权访问
app.user.data.denied=没有权限访问用户数据!
app.user.phone.exists=修改用户'{0}'失败,手机号码已存在

View File

@@ -70,7 +70,7 @@ public class ThreadPoolConfig {
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
pool.shutdownNow(); pool.shutdownNow();
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
log.info("Pool did not terminate"); log.info("线程池未在指定时间内终止");
} }
} }
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
@@ -87,9 +87,8 @@ public class ThreadPoolConfig {
* 打印线程异常信息 * 打印线程异常信息
*/ */
public static void printException(Runnable r, Throwable t) { public static void printException(Runnable r, Throwable t) {
if (t == null && r instanceof Future<?>) { if (t == null && r instanceof Future<?> future) {
try { try {
Future<?> future = (Future<?>) r;
if (future.isDone()) { if (future.isDone()) {
future.get(); future.get();
} }

View File

@@ -1,8 +1,9 @@
package org.dromara.common.core.domain; package org.dromara.common.core.domain;
import org.dromara.common.core.constant.HttpStatus;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.utils.MessageUtils;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
@@ -36,11 +37,11 @@ public class R<T> implements Serializable {
private T data; private T data;
public static <T> R<T> ok() { public static <T> R<T> ok() {
return restResult(null, SUCCESS, "操作成功"); return restResult(null, SUCCESS, message("operation.success", "操作成功"));
} }
public static <T> R<T> ok(T data) { public static <T> R<T> ok(T data) {
return restResult(data, SUCCESS, "操作成功"); return restResult(data, SUCCESS, message("operation.success", "操作成功"));
} }
public static <T> R<T> ok(String msg) { public static <T> R<T> ok(String msg) {
@@ -52,7 +53,7 @@ public class R<T> implements Serializable {
} }
public static <T> R<T> fail() { public static <T> R<T> fail() {
return restResult(null, FAIL, "操作失败"); return restResult(null, FAIL, message("operation.fail", "操作失败"));
} }
public static <T> R<T> fail(String msg) { public static <T> R<T> fail(String msg) {
@@ -100,6 +101,15 @@ public class R<T> implements Serializable {
return r; return r;
} }
private static String message(String code, String defaultMessage) {
try {
String message = MessageUtils.message(code);
return code.equals(message) ? defaultMessage : message;
} catch (Exception e) {
return defaultMessage;
}
}
public static <T> Boolean isError(R<T> ret) { public static <T> Boolean isError(R<T> ret) {
return !isSuccess(ret); return !isSuccess(ret);
} }

View File

@@ -63,7 +63,7 @@ public class RegionUtils {
Config v6Config = null; Config v6Config = null;
InputStream v6XdbInputStream = ResourceUtil.getStreamSafe(DEFAULT_IPV6_XDB_PATH); InputStream v6XdbInputStream = ResourceUtil.getStreamSafe(DEFAULT_IPV6_XDB_PATH);
if (v6XdbInputStream == null) { if (v6XdbInputStream == null) {
log.warn("未加载 IPv6 地址库:未在类路径下找到文件 {}。当前仅启用 IPv4 查询。如需启用 IPv6请将 ip2region_v6.xdb 放置到 resources 目录", DEFAULT_IPV6_XDB_PATH); log.warn("未加载 IPv6 地址库:未在类路径下找到文件 {}。当前仅启用 IPv4 查询。如需启用 IPv6请将 ip2region_v6.xdb 放置到资源目录", DEFAULT_IPV6_XDB_PATH);
} else { } else {
v6Config = Config.custom() v6Config = Config.custom()
.setCachePolicy(Config.BufferCache) .setCachePolicy(Config.BufferCache)
@@ -128,7 +128,7 @@ public class RegionUtils {
try { try {
ip2Region.close(10000); ip2Region.close(10000);
} catch (Exception e) { } catch (Exception e) {
log.error("Ip2Region服务关闭异常", e); log.error("IP地址库服务关闭异常", e);
} }
} }
@@ -148,7 +148,7 @@ public class RegionUtils {
try { try {
ip2Region.close(timeout.toMillis()); ip2Region.close(timeout.toMillis());
} catch (Exception e) { } catch (Exception e) {
log.error("Ip2Region服务关闭异常", e); log.error("IP地址库服务关闭异常", e);
} }
} }

View File

@@ -46,7 +46,7 @@ public class SaTokenAnnotationMetadataJavadocResolver extends AbstractMetadataJa
SA_IGNORE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_IGNORE_CLASS_NAME, false); SA_IGNORE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_IGNORE_CLASS_NAME, false);
SA_CHECK_LOGIN_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_LOGIN_NAME, false); SA_CHECK_LOGIN_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_LOGIN_NAME, false);
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("SaTokenAnnotationJavadocResolver init success, load annotation class: {}", List.of(SA_CHECK_ROLE_CLASS, SA_CHECK_PERMISSION_CLASS, SA_IGNORE_CLASS, SA_CHECK_LOGIN_CLASS)); log.debug("SaToken 注解 Javadoc 解析器初始化成功,已加载注解类:{}", List.of(SA_CHECK_ROLE_CLASS, SA_CHECK_PERMISSION_CLASS, SA_IGNORE_CLASS, SA_CHECK_LOGIN_CLASS));
} }
} }

View File

@@ -48,7 +48,7 @@ public class JacksonConfig {
public Jackson2ObjectMapperBuilderCustomizer customizer() { public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> { return builder -> {
builder.timeZone(TimeZone.getDefault()); builder.timeZone(TimeZone.getDefault());
log.info("初始化 jackson 配置"); log.info("初始化JSON序列化配置");
}; };
} }

View File

@@ -35,6 +35,10 @@
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>water-common-json</artifactId> <artifactId>water-common-json</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>water-common-core</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>water-app</artifactId> <artifactId>water-app</artifactId>

View File

@@ -34,7 +34,12 @@ public class DeviceMqttCommandPublisher implements IDeviceCommandPublisher {
command.getPayload().put("commandType", command.getCommandType()); command.getPayload().put("commandType", command.getCommandType());
ackService.savePending(command); ackService.savePending(command);
mqttClientManager.publish(command.getTopic(), JsonUtils.toJsonString(command.getPayload())); try {
mqttClientManager.publish(command.getTopic(), JsonUtils.toJsonString(command.getPayload()));
} catch (RuntimeException e) {
ackService.removePending(command.getCommandId());
throw e;
}
return command.getCommandId(); return command.getCommandId();
} }

View File

@@ -3,13 +3,10 @@ package org.dromara.mqtt;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.app.mqtt.MqttMessageDispatcher; import org.dromara.app.mqtt.MqttMessageDispatcher;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.mqtt.config.properties.MqttProperties; import org.dromara.mqtt.config.properties.MqttProperties;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -26,11 +23,7 @@ import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.List; import java.util.List;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@Slf4j @Slf4j
@@ -50,6 +43,12 @@ public class MqttClientManager implements DisposableBean {
@Bean @Bean
@ConditionalOnProperty(prefix = "mqtt", name = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = "mqtt", name = "enabled", havingValue = "true")
public MqttAsyncClient mqttConnect() throws MqttException { public MqttAsyncClient mqttConnect() throws MqttException {
if (StringUtils.isBlank(props.getBrokerUrl())) {
throw new ServiceException("启用 MQTT 时必须配置 broker-url");
}
if (StringUtils.isBlank(props.getClientId())) {
throw new ServiceException("启用 MQTT 时必须配置 client-id");
}
messageQueue = new ArrayBlockingQueue<>(Math.max(1, props.getAsync().getQueueCapacity())); messageQueue = new ArrayBlockingQueue<>(Math.max(1, props.getAsync().getQueueCapacity()));
consumerExecutor = createConsumerExecutor(); consumerExecutor = createConsumerExecutor();
running = true; running = true;
@@ -71,13 +70,13 @@ public class MqttClientManager implements DisposableBean {
client.setCallback(new MqttCallbackExtended() { client.setCallback(new MqttCallbackExtended() {
@Override @Override
public void connectionLost(Throwable cause) { public void connectionLost(Throwable cause) {
log.warn("[MQTT] connection lost: {}", cause == null ? "" : cause.getMessage()); log.warn("[MQTT] 连接已断开:{}", cause == null ? "" : cause.getMessage());
} }
@Override @Override
public void connectComplete(boolean reconnect, String serverURI) { public void connectComplete(boolean reconnect, String serverURI) {
if (reconnect) { if (reconnect) {
log.info("[MQTT] reconnected: {}", serverURI); log.info("[MQTT] 已重新连接:{}", serverURI);
subscribeTopics(); subscribeTopics();
} }
} }
@@ -94,7 +93,7 @@ public class MqttClientManager implements DisposableBean {
}); });
client.connect(options).waitForCompletion(); client.connect(options).waitForCompletion();
log.info("[MQTT] connected: {}", props.getBrokerUrl()); log.info("[MQTT] 已连接:{}", props.getBrokerUrl());
subscribeTopics(); subscribeTopics();
return client; return client;
} }
@@ -183,11 +182,11 @@ public class MqttClientManager implements DisposableBean {
InboundMessage inboundMessage = new InboundMessage(topic, payload); InboundMessage inboundMessage = new InboundMessage(topic, payload);
try { try {
if (!messageQueue.offer(inboundMessage, props.getAsync().getOfferTimeoutMs(), TimeUnit.MILLISECONDS)) { if (!messageQueue.offer(inboundMessage, props.getAsync().getOfferTimeoutMs(), TimeUnit.MILLISECONDS)) {
log.warn("[MQTT] inbound queue full, dropped topic={}", topic); log.warn("[MQTT] 上行消息队列已满,丢弃 主题={}", topic);
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
log.warn("[MQTT] enqueue interrupted topic={}", topic); log.warn("[MQTT] 上行消息入队被中断 主题={}", topic);
} }
} }
@@ -221,7 +220,7 @@ public class MqttClientManager implements DisposableBean {
try { try {
dispatcher.dispatch(message.topic(), message.payload()); dispatcher.dispatch(message.topic(), message.payload());
} catch (Exception e) { } catch (Exception e) {
log.error("[MQTT] dispatch failed topic={}", message.topic(), e); log.error("[MQTT] 消息分发失败 主题={}", message.topic(), e);
} }
} }
@@ -232,30 +231,31 @@ public class MqttClientManager implements DisposableBean {
} }
List<String> topics = props.getTopics() == null ? List.of() : props.getTopics().getSubscribe(); List<String> topics = props.getTopics() == null ? List.of() : props.getTopics().getSubscribe();
if (topics == null || topics.isEmpty()) { if (topics == null || topics.isEmpty()) {
log.warn("[MQTT] no subscribe topics configured"); log.warn("[MQTT] 未配置订阅主题");
return; return;
} }
int[] qos = topics.stream().mapToInt(topic -> props.getQos()).toArray(); int[] qos = topics.stream().mapToInt(topic -> props.getQos()).toArray();
client.subscribe(topics.toArray(new String[0]), qos).waitForCompletion(); client.subscribe(topics.toArray(new String[0]), qos).waitForCompletion();
topics.forEach(topic -> log.info("[MQTT] subscribed: {} qos={}", topic, props.getQos())); topics.forEach(topic -> log.info("[MQTT] 已订阅 主题={} 服务质量等级={}", topic, props.getQos()));
} catch (MqttException e) { } catch (MqttException e) {
log.error("[MQTT] subscribe failed", e); log.error("[MQTT] 订阅失败", e);
} }
} }
public void publish(String topic, String payload) { public void publish(String topic, String payload) {
try { try {
if (client == null || !client.isConnected()) { if (client == null || !client.isConnected()) {
log.warn("[MQTT] publish skipped, client not connected topic={}", topic); throw new ServiceException("MQTT客户端未连接");
return;
} }
MqttMessage message = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8)); MqttMessage message = new MqttMessage(payload.getBytes(StandardCharsets.UTF_8));
message.setQos(props.getQos()); message.setQos(props.getQos());
message.setRetained(false); message.setRetained(false);
client.publish(topic, message); IMqttDeliveryToken token = client.publish(topic, message);
log.info("[MQTT] published topic={} payload={}", topic, payload); token.waitForCompletion(Math.max(1000L, props.getConnectionTimeout() * 1000L));
log.info("[MQTT] 消息已发布 主题={} 消息体={}", topic, payload);
} catch (MqttException e) { } catch (MqttException e) {
log.error("[MQTT] publish failed topic={}", topic, e); log.error("[MQTT] 消息发布失败 主题={}", topic, e);
throw new ServiceException("MQTT 消息发布失败:{}", e.getMessage());
} }
} }

View File

@@ -1,9 +1,12 @@
package org.dromara.mqtt; package org.dromara.mqtt;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.domain.mqtt.DeviceCommand; import org.dromara.app.domain.mqtt.DeviceCommand;
import org.dromara.app.domain.mqtt.DeviceCommandAck; import org.dromara.app.domain.mqtt.DeviceCommandAck;
import org.dromara.app.mapper.AppDeviceMapper;
import org.dromara.app.service.IDeviceCommandAckHandler; import org.dromara.app.service.IDeviceCommandAckHandler;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
@@ -17,11 +20,7 @@ import org.springframework.stereotype.Service;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Slf4j @Slf4j
@@ -36,16 +35,24 @@ public class MqttCommandAckService implements IDeviceCommandAckHandler {
@Autowired @Autowired
private MqttClientManager mqttClientManager; private MqttClientManager mqttClientManager;
private final MqttProperties mqttProperties; private final MqttProperties mqttProperties;
private final AppDeviceMapper appDeviceMapper;
public void savePending(DeviceCommand command) { public void savePending(DeviceCommand command) {
RedisUtils.setCacheObject(pendingKey(command.getCommandId()), command, Duration.ofSeconds(mqttProperties.getCommandAck().getPendingTtlSeconds())); RedisUtils.setCacheObject(pendingKey(command.getCommandId()), command, Duration.ofSeconds(mqttProperties.getCommandAck().getPendingTtlSeconds()));
pendingIds().add(command.getCommandId()); pendingIds().add(command.getCommandId());
} }
public void removePending(String commandId) {
if (StringUtils.isBlank(commandId)) {
return;
}
deletePending(commandId);
}
@Override @Override
public void handleAck(String deviceNo, String payload) { public void handleAck(String deviceNo, String payload) {
if (StringUtils.isBlank(payload)) { if (StringUtils.isBlank(payload)) {
log.warn("[MQTT] 收到空 ACK deviceNo={}", deviceNo); log.warn("[MQTT] 收到空 ACK 设备编号={}", deviceNo);
return; return;
} }
@@ -59,26 +66,25 @@ public class MqttCommandAckService implements IDeviceCommandAckHandler {
try { try {
ack = JsonUtils.parseObject(trimmedPayload, DeviceCommandAck.class); ack = JsonUtils.parseObject(trimmedPayload, DeviceCommandAck.class);
} catch (RuntimeException e) { } catch (RuntimeException e) {
log.warn("[MQTT] ACK JSON 格式错误 deviceNo={} payload={}", deviceNo, payload, e); log.warn("[MQTT] ACK JSON 格式错误 设备编号={} 消息体={}", deviceNo, payload, e);
return; return;
} }
if (ack == null || ack.getCommandId() == null) { if (ack == null || ack.getCommandId() == null) {
log.warn("[MQTT] ACK 缺少 commandId deviceNo={} payload={}", deviceNo, payload); log.warn("[MQTT] ACK 缺少命令编号 设备编号={} 消息体={}", deviceNo, payload);
return; return;
} }
ack.setDeviceNo(deviceNo); ack.setDeviceNo(deviceNo);
RedisUtils.deleteObject(pendingKey(ack.getCommandId())); deletePending(ack.getCommandId());
pendingIds().remove(ack.getCommandId());
RedisUtils.setCacheObject(ackKey(ack.getCommandId()), ack, Duration.ofSeconds(mqttProperties.getCommandAck().getAckTtlSeconds())); RedisUtils.setCacheObject(ackKey(ack.getCommandId()), ack, Duration.ofSeconds(mqttProperties.getCommandAck().getAckTtlSeconds()));
refreshDeviceOnline(deviceNo); refreshDeviceOnline(deviceNo);
log.info("[MQTT] command ack received deviceNo={} commandId={} status={}", deviceNo, ack.getCommandId(), ack.getStatus()); log.info("[MQTT] 收到命令确认 设备编号={} 命令编号={} 状态={}", deviceNo, ack.getCommandId(), ack.getStatus());
} }
private void handlePlainAck(String deviceNo, String payload) { private void handlePlainAck(String deviceNo, String payload) {
List<DeviceCommand> pendingCommands = findPendingCommandsByDeviceNo(deviceNo); List<DeviceCommand> pendingCommands = findPendingCommandsByDeviceNo(deviceNo);
refreshDeviceOnline(deviceNo); refreshDeviceOnline(deviceNo);
if (pendingCommands.size() != 1) { if (pendingCommands.size() != 1) {
log.warn("[MQTT] 非 JSON ACK 无法唯一匹配待确认命令 deviceNo={} pendingCount={} payload={}", deviceNo, pendingCommands.size(), payload); log.warn("[MQTT] 非 JSON ACK 无法唯一匹配待确认命令 设备编号={} 待确认数量={} 消息体={}", deviceNo, pendingCommands.size(), payload);
return; return;
} }
@@ -90,10 +96,9 @@ public class MqttCommandAckService implements IDeviceCommandAckHandler {
ack.setStatus("1"); ack.setStatus("1");
ack.setMessage(payload); ack.setMessage(payload);
RedisUtils.deleteObject(pendingKey(commandId)); deletePending(commandId);
pendingIds().remove(commandId);
RedisUtils.setCacheObject(ackKey(commandId), ack, Duration.ofSeconds(mqttProperties.getCommandAck().getAckTtlSeconds())); RedisUtils.setCacheObject(ackKey(commandId), ack, Duration.ofSeconds(mqttProperties.getCommandAck().getAckTtlSeconds()));
log.info("[MQTT] plain command ack received deviceNo={} commandId={} payload={}", deviceNo, commandId, payload); log.info("[MQTT] 收到非 JSON 命令确认 设备编号={} 命令编号={} 消息体={}", deviceNo, commandId, payload);
} }
private List<DeviceCommand> findPendingCommandsByDeviceNo(String deviceNo) { private List<DeviceCommand> findPendingCommandsByDeviceNo(String deviceNo) {
@@ -126,7 +131,9 @@ public class MqttCommandAckService implements IDeviceCommandAckHandler {
retryCommand(commandId, now); retryCommand(commandId, now);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
log.warn("[MQTT] 命令重试锁等待被中断 commandId={}", commandId); log.warn("[MQTT] 命令重试锁等待被中断 命令编号={}", commandId);
} catch (RuntimeException e) {
log.error("[MQTT] 命令重试失败 命令编号={}", commandId, e);
} finally { } finally {
if (locked && lock.isHeldByCurrentThread()) { if (locked && lock.isHeldByCurrentThread()) {
lock.unlock(); lock.unlock();
@@ -144,15 +151,20 @@ public class MqttCommandAckService implements IDeviceCommandAckHandler {
return; return;
} }
if (command.getRetryCount() >= mqttProperties.getCommandAck().getMaxRetryCount()) { if (command.getRetryCount() >= mqttProperties.getCommandAck().getMaxRetryCount()) {
RedisUtils.deleteObject(pendingKey(commandId)); deletePending(commandId);
pendingIds().remove(commandId); log.warn("[MQTT] 命令重试次数已达上限 设备编号={} 命令编号={}", command.getDeviceNo(), commandId);
log.warn("[MQTT] 命令重试次数已达上限 deviceNo={} commandId={}", command.getDeviceNo(), commandId);
return; return;
} }
if (!isDeviceOnline(command.getDeviceNo())) { if (!isDeviceOnline(command.getDeviceNo())) {
command.setRetryCount(command.getRetryCount() + 1);
if (command.getRetryCount() >= mqttProperties.getCommandAck().getMaxRetryCount()) {
deletePending(commandId);
log.warn("[MQTT] 设备离线,命令达到最大等待次数,已删除待确认命令 设备编号={} 命令编号={}", command.getDeviceNo(), commandId);
return;
}
command.setNextRetryAt(now + mqttProperties.getCommandAck().getRetryIntervalMs()); command.setNextRetryAt(now + mqttProperties.getCommandAck().getRetryIntervalMs());
savePending(command); savePending(command);
log.debug("[MQTT] command retry delayed, device offline deviceNo={} commandId={}", command.getDeviceNo(), commandId); log.debug("[MQTT] 设备离线,命令延后重试 设备编号={} 命令编号={} 等待次数={}", command.getDeviceNo(), commandId, command.getRetryCount());
return; return;
} }
@@ -161,7 +173,7 @@ public class MqttCommandAckService implements IDeviceCommandAckHandler {
command.setNextRetryAt(now + mqttProperties.getCommandAck().getRetryIntervalMs()); command.setNextRetryAt(now + mqttProperties.getCommandAck().getRetryIntervalMs());
mqttClientManager.publish(command.getTopic(), JsonUtils.toJsonString(command.getPayload())); mqttClientManager.publish(command.getTopic(), JsonUtils.toJsonString(command.getPayload()));
savePending(command); savePending(command);
log.info("[MQTT] command resent deviceNo={} commandId={} retry={}", command.getDeviceNo(), commandId, command.getRetryCount()); log.info("[MQTT] 命令已重新下发 设备编号={} 命令编号={} 重试次数={}", command.getDeviceNo(), commandId, command.getRetryCount());
} }
private boolean isDeviceOnline(String deviceNo) { private boolean isDeviceOnline(String deviceNo) {
@@ -183,12 +195,22 @@ public class MqttCommandAckService implements IDeviceCommandAckHandler {
statusCache, statusCache,
Duration.ofSeconds(mqttProperties.getCommandAck().getDeviceStatusCacheTtlSeconds()) Duration.ofSeconds(mqttProperties.getCommandAck().getDeviceStatusCacheTtlSeconds())
); );
appDeviceMapper.update(null,
new LambdaUpdateWrapper<AppDevice>()
.set(AppDevice::getStatus, "1")
.eq(AppDevice::getDeviceNo, deviceNo)
);
} }
private RSet<String> pendingIds() { private RSet<String> pendingIds() {
return RedisUtils.getClient().getSet(mqttProperties.getCommandAck().getPendingSetKey()); return RedisUtils.getClient().getSet(mqttProperties.getCommandAck().getPendingSetKey());
} }
private void deletePending(String commandId) {
RedisUtils.deleteObject(pendingKey(commandId));
pendingIds().remove(commandId);
}
private String pendingKey(String commandId) { private String pendingKey(String commandId) {
return mqttProperties.getCommandAck().getPendingKeyPrefix() + commandId; return mqttProperties.getCommandAck().getPendingKeyPrefix() + commandId;
} }

View File

@@ -1,10 +1,12 @@
package org.dromara.mqtt; package org.dromara.mqtt;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mqtt.config.properties.MqttProperties; import org.dromara.mqtt.config.properties.MqttProperties;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class MqttCommandRetryTask { public class MqttCommandRetryTask {
@@ -15,6 +17,7 @@ public class MqttCommandRetryTask {
@Scheduled(fixedDelayString = "${mqtt.command-ack.scan-interval-ms:5000}") @Scheduled(fixedDelayString = "${mqtt.command-ack.scan-interval-ms:5000}")
public void retryExpiredCommands() { public void retryExpiredCommands() {
if (mqttProperties.getCommandAck().isEnabled()) { if (mqttProperties.getCommandAck().isEnabled()) {
// log.info("[设备命令重新下发] 定时器调用成功 ");
ackService.retryExpiredCommands(); ackService.retryExpiredCommands();
} }
} }

View File

@@ -5,6 +5,7 @@ import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.MessageUtils;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
@@ -52,7 +53,7 @@ public class TableDataInfo<T> implements Serializable {
this.rows = list; this.rows = list;
this.total = total; this.total = total;
this.code = HttpStatus.HTTP_OK; this.code = HttpStatus.HTTP_OK;
this.msg = "查询成功"; this.msg = message("query.success", "查询成功");
} }
/** /**
@@ -61,7 +62,7 @@ public class TableDataInfo<T> implements Serializable {
public static <T> TableDataInfo<T> build(IPage<T> page) { public static <T> TableDataInfo<T> build(IPage<T> page) {
TableDataInfo<T> rspData = new TableDataInfo<>(); TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK); rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功"); rspData.setMsg(message("query.success", "查询成功"));
rspData.setRows(page.getRecords()); rspData.setRows(page.getRecords());
rspData.setTotal(page.getTotal()); rspData.setTotal(page.getTotal());
return rspData; return rspData;
@@ -73,7 +74,7 @@ public class TableDataInfo<T> implements Serializable {
public static <T> TableDataInfo<T> build(List<T> list) { public static <T> TableDataInfo<T> build(List<T> list) {
TableDataInfo<T> rspData = new TableDataInfo<>(); TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK); rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功"); rspData.setMsg(message("query.success", "查询成功"));
rspData.setRows(list); rspData.setRows(list);
rspData.setTotal(list.size()); rspData.setTotal(list.size());
return rspData; return rspData;
@@ -85,7 +86,7 @@ public class TableDataInfo<T> implements Serializable {
public static <T> TableDataInfo<T> build() { public static <T> TableDataInfo<T> build() {
TableDataInfo<T> rspData = new TableDataInfo<>(); TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK); rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功"); rspData.setMsg(message("query.success", "查询成功"));
return rspData; return rspData;
} }
@@ -104,4 +105,13 @@ public class TableDataInfo<T> implements Serializable {
return new TableDataInfo<>(pageList, list.size()); return new TableDataInfo<>(pageList, list.size());
} }
private static String message(String code, String defaultMessage) {
try {
String message = MessageUtils.message(code);
return code.equals(message) ? defaultMessage : message;
} catch (Exception e) {
return defaultMessage;
}
}
} }

View File

@@ -45,7 +45,7 @@ public class MybatisExceptionHandler {
log.error("请求地址'{}', 未找到数据源", requestURI); log.error("请求地址'{}', 未找到数据源", requestURI);
return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认"); return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认");
} }
log.error("请求地址'{}', Mybatis系统异常", requestURI, e); log.error("请求地址'{}', MyBatis系统异常", requestURI, e);
return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()); return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage());
} }

View File

@@ -60,7 +60,7 @@ public class OssFactory {
client = CLIENT_CACHE.get(key); client = CLIENT_CACHE.get(key);
if (client == null || !client.checkPropertiesSame(properties)) { if (client == null || !client.checkPropertiesSame(properties)) {
CLIENT_CACHE.put(key, new OssClient(configKey, properties)); CLIENT_CACHE.put(key, new OssClient(configKey, properties));
log.info("创建OSS实例 key => {}", configKey); log.info("创建OSS实例 配置键 => {}", configKey);
return CLIENT_CACHE.get(key); return CLIENT_CACHE.get(key);
} }
} finally { } finally {

View File

@@ -69,7 +69,7 @@ public class RateLimiterAspect {
} }
throw new ServiceException(message); throw new ServiceException(message);
} }
log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, combineKey); log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存 => '{}'", count, number, combineKey);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof ServiceException) { if (e instanceof ServiceException) {
throw e; throw e;

View File

@@ -98,7 +98,7 @@ public class RedisConfig {
.setReadMode(clusterServersConfig.getReadMode()) .setReadMode(clusterServersConfig.getReadMode())
.setSubscriptionMode(clusterServersConfig.getSubscriptionMode()); .setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
} }
log.info("初始化 redis 配置"); log.info("初始化 Redis 配置");
}; };
} }

View File

@@ -23,7 +23,7 @@ public class SmsExceptionHandler {
@ExceptionHandler(SmsBlendException.class) @ExceptionHandler(SmsBlendException.class)
public R<Void> handleSmsBlendException(SmsBlendException e, HttpServletRequest request) { public R<Void> handleSmsBlendException(SmsBlendException e, HttpServletRequest request) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生sms短信异常.", requestURI, e); log.error("请求地址'{}',发生短信异常.", requestURI, e);
return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "短信发送失败,请稍后再试..."); return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "短信发送失败,请稍后再试...");
} }

View File

@@ -10,7 +10,6 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -210,7 +209,7 @@ public class SseEmitterManager {
broadcastMessage.setMessage(sseMessageDto.getMessage()); broadcastMessage.setMessage(sseMessageDto.getMessage());
broadcastMessage.setUserIds(sseMessageDto.getUserIds()); broadcastMessage.setUserIds(sseMessageDto.getUserIds());
RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> { RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> {
log.info("SSE发送主题订阅消息topic:{} session keys:{} message:{}", log.info("SSE发送主题订阅消息 主题={} 用户ID={} 消息={}",
SSE_TOPIC, sseMessageDto.getUserIds(), sseMessageDto.getMessage()); SSE_TOPIC, sseMessageDto.getUserIds(), sseMessageDto.getMessage());
}); });
} }
@@ -224,7 +223,7 @@ public class SseEmitterManager {
SseMessageDto broadcastMessage = new SseMessageDto(); SseMessageDto broadcastMessage = new SseMessageDto();
broadcastMessage.setMessage(message); broadcastMessage.setMessage(message);
RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> { RedisUtils.publish(SSE_TOPIC, broadcastMessage, consumer -> {
log.info("SSE发送主题订阅消息topic:{} message:{}", SSE_TOPIC, message); log.info("SSE发送主题订阅消息 主题={} 消息={}", SSE_TOPIC, message);
}); });
} }
} }

View File

@@ -28,7 +28,7 @@ public class SseTopicListener implements ApplicationRunner, Ordered {
@Override @Override
public void run(ApplicationArguments args) throws Exception { public void run(ApplicationArguments args) throws Exception {
sseEmitterManager.subscribeMessage((message) -> { sseEmitterManager.subscribeMessage((message) -> {
log.info("SSE主题订阅收到消息session keys={} message={}", message.getUserIds(), message.getMessage()); log.info("SSE主题订阅收到消息 用户ID={} 消息={}", message.getUserIds(), message.getMessage());
// 如果key不为空就按照key发消息 如果为空就群发 // 如果key不为空就按照key发消息 如果为空就群发
if (CollUtil.isNotEmpty(message.getUserIds())) { if (CollUtil.isNotEmpty(message.getUserIds())) {
message.getUserIds().forEach(key -> { message.getUserIds().forEach(key -> {

View File

@@ -28,7 +28,7 @@ public class PlusTenantLineHandler implements TenantLineHandler {
public Expression getTenantId() { public Expression getTenantId() {
String tenantId = TenantHelper.getTenantId(); String tenantId = TenantHelper.getTenantId();
if (StringUtils.isBlank(tenantId)) { if (StringUtils.isBlank(tenantId)) {
log.error("无法获取有效的租户id -> Null"); log.error("无法获取有效的租户ID -> ");
return new NullValue(); return new NullValue();
} }
// 返回固定租户 // 返回固定租户

View File

@@ -39,10 +39,10 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler {
} }
String tenantId = TenantHelper.getTenantId(); String tenantId = TenantHelper.getTenantId();
if (StringUtils.isBlank(tenantId)) { if (StringUtils.isBlank(tenantId)) {
log.debug("无法获取有效的租户id -> Null"); log.debug("无法获取有效的租户ID -> ");
return super.map(name); return super.map(name);
} }
if (StringUtils.startsWith(name, tenantId + "")) { if (StringUtils.startsWith(name, tenantId)) {
// 如果存在则直接返回 // 如果存在则直接返回
return super.map(name); return super.map(name);
} }
@@ -70,10 +70,10 @@ public class TenantKeyPrefixHandler extends KeyPrefixHandler {
} }
String tenantId = TenantHelper.getTenantId(); String tenantId = TenantHelper.getTenantId();
if (StringUtils.isBlank(tenantId)) { if (StringUtils.isBlank(tenantId)) {
log.debug("无法获取有效的租户id -> Null"); log.debug("无法获取有效的租户ID -> ");
return unmap; return unmap;
} }
if (StringUtils.startsWith(unmap, tenantId + "")) { if (StringUtils.startsWith(unmap, tenantId)) {
// 如果存在则删除 // 如果存在则删除
return unmap.substring((tenantId + ":").length()); return unmap.substring((tenantId + ":").length());
} }

View File

@@ -29,7 +29,7 @@ public class TenantSpringCacheManager extends PlusSpringCacheManager {
} }
String tenantId = TenantHelper.getTenantId(); String tenantId = TenantHelper.getTenantId();
if (StringUtils.isBlank(tenantId)) { if (StringUtils.isBlank(tenantId)) {
log.error("无法获取有效的租户id -> Null"); log.error("无法获取有效的租户ID -> ");
} }
if (StringUtils.startsWith(name, tenantId)) { if (StringUtils.startsWith(name, tenantId)) {
// 如果存在则直接返回 // 如果存在则直接返回

View File

@@ -7,11 +7,11 @@ import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.translation.annotation.Translation; import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.core.TranslationInterface; import org.dromara.common.translation.core.TranslationInterface;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
@@ -61,7 +61,7 @@ public class TranslationHandler extends JsonSerializer<Object> implements Contex
Object result = trans.translation(value, translation.other()); Object result = trans.translation(value, translation.other());
gen.writeObject(result); gen.writeObject(result);
} catch (Exception e) { } catch (Exception e) {
log.error("翻译处理异常,type: {}, value: {}", translation.type(), value, e); log.error("翻译处理异常,类型:{},值:{}", translation.type(), value, e);
// 出现异常时输出原始值而不是中断序列化 // 出现异常时输出原始值而不是中断序列化
gen.writeObject(value); gen.writeObject(value);
} }

View File

@@ -1,9 +1,10 @@
package org.dromara.common.web.core; package org.dromara.common.web.core;
import org.springframework.web.servlet.LocaleResolver;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import java.util.Locale; import java.util.Locale;
/** /**
@@ -16,12 +17,24 @@ public class I18nLocaleResolver implements LocaleResolver {
@Override @Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) { public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String language = httpServletRequest.getHeader("content-language"); String language = httpServletRequest.getHeader("content-language");
Locale locale = Locale.getDefault(); if (StringUtils.isBlank(language)) {
if (language != null && language.length() > 0) { language = httpServletRequest.getParameter("lang");
String[] split = language.split("_");
locale = new Locale(split[0], split[1]);
} }
return locale; if (StringUtils.isBlank(language)) {
language = httpServletRequest.getHeader("Accept-Language");
}
if (StringUtils.isBlank(language)) {
return Locale.SIMPLIFIED_CHINESE;
}
String languageTag = language.split(",")[0].trim().replace('_', '-');
if ("zh".equalsIgnoreCase(languageTag)) {
return Locale.SIMPLIFIED_CHINESE;
}
if ("en".equalsIgnoreCase(languageTag)) {
return Locale.US;
}
Locale locale = Locale.forLanguageTag(languageTag);
return StringUtils.isBlank(locale.getLanguage()) ? Locale.SIMPLIFIED_CHINESE : locale;
} }
@Override @Override

View File

@@ -51,16 +51,16 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
jsonParam = rootNode.toString(); jsonParam = rootNode.toString();
} }
} }
log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); log.info("[请求]开始请求 => URL[{}],参数类型[JSON],参数:[{}]", url, jsonParam);
} else { } else {
Map<String, String[]> parameterMap = request.getParameterMap(); Map<String, String[]> parameterMap = request.getParameterMap();
if (MapUtil.isNotEmpty(parameterMap)) { if (MapUtil.isNotEmpty(parameterMap)) {
Map<String, String[]> map = new LinkedHashMap<>(parameterMap); Map<String, String[]> map = new LinkedHashMap<>(parameterMap);
MapUtil.removeAny(map, SystemConstants.EXCLUDE_PROPERTIES); MapUtil.removeAny(map, SystemConstants.EXCLUDE_PROPERTIES);
String parameters = JsonUtils.toJsonString(map); String parameters = JsonUtils.toJsonString(map);
log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); log.info("[请求]开始请求 => URL[{}],参数类型[表单参数],参数:[{}]", url, parameters);
} else { } else {
log.info("[PLUS]开始请求 => URL[{}],无参数", url); log.info("[请求]开始请求 => URL[{}],无参数", url);
} }
} }
@@ -105,7 +105,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
StopWatch stopWatch = KEY_CACHE.get(); StopWatch stopWatch = KEY_CACHE.get();
if (ObjectUtil.isNotNull(stopWatch)) { if (ObjectUtil.isNotNull(stopWatch)) {
stopWatch.stop(); stopWatch.stop();
log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getDuration().toMillis()); log.info("[请求]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getDuration().toMillis());
KEY_CACHE.remove(); KEY_CACHE.remove();
} }
} }

View File

@@ -31,11 +31,11 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY); LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
if (ObjectUtil.isNull(loginUser)) { if (ObjectUtil.isNull(loginUser)) {
session.close(CloseStatus.BAD_DATA); session.close(CloseStatus.BAD_DATA);
log.info("[connect] invalid token received. sessionId: {}", session.getId()); log.info("[连接] 收到无效令牌会话ID={}", session.getId());
return; return;
} }
WebSocketSessionHolder.addSession(loginUser.getUserId(), new ConcurrentWebSocketSessionDecorator(session, 10 * 1000, 64000)); WebSocketSessionHolder.addSession(loginUser.getUserId(), new ConcurrentWebSocketSessionDecorator(session, 10 * 1000, 64000));
log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType()); log.info("[连接] 会话ID={}用户ID={},用户类型={}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
} }
/** /**
@@ -90,7 +90,7 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
*/ */
@Override @Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
log.error("[transport error] sessionId: {} , exception:{}", session.getId(), exception.getMessage()); log.error("[传输错误] 会话ID={},异常={}", session.getId(), exception.getMessage());
} }
/** /**
@@ -103,11 +103,11 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY); LoginUser loginUser = (LoginUser) session.getAttributes().get(LOGIN_USER_KEY);
if (ObjectUtil.isNull(loginUser)) { if (ObjectUtil.isNull(loginUser)) {
log.info("[disconnect] invalid token received. sessionId: {}", session.getId()); log.info("[断开连接] 收到无效令牌会话ID={}", session.getId());
return; return;
} }
WebSocketSessionHolder.removeSession(loginUser.getUserId()); WebSocketSessionHolder.removeSession(loginUser.getUserId());
log.info("[disconnect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType()); log.info("[断开连接] 会话ID={}用户ID={},用户类型={}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
} }
/** /**

View File

@@ -54,7 +54,7 @@ public class PlusWebSocketInterceptor implements HandshakeInterceptor {
attributes.put(LOGIN_USER_KEY, loginUser); attributes.put(LOGIN_USER_KEY, loginUser);
return true; return true;
} catch (NotLoginException e) { } catch (NotLoginException e) {
log.error("WebSocket 认证失败'{}',无法访问系统资源", e.getMessage()); log.error("网络套接字认证失败'{}',无法访问系统资源", e.getMessage());
return false; return false;
} }
} }

View File

@@ -26,7 +26,7 @@ public class WebSocketTopicListener implements ApplicationRunner, Ordered {
public void run(ApplicationArguments args) throws Exception { public void run(ApplicationArguments args) throws Exception {
// 订阅WebSocket消息 // 订阅WebSocket消息
WebSocketUtils.subscribeMessage((message) -> { WebSocketUtils.subscribeMessage((message) -> {
log.info("WebSocket主题订阅收到消息session keys={} message={}", message.getSessionKeys(), message.getMessage()); log.info("网络套接字主题订阅收到消息 会话标识={} 消息={}", message.getSessionKeys(), message.getMessage());
// 如果key不为空就按照key发消息 如果为空就群发 // 如果key不为空就按照key发消息 如果为空就群发
if (CollUtil.isNotEmpty(message.getSessionKeys())) { if (CollUtil.isNotEmpty(message.getSessionKeys())) {
message.getSessionKeys().forEach(key -> { message.getSessionKeys().forEach(key -> {
@@ -40,7 +40,7 @@ public class WebSocketTopicListener implements ApplicationRunner, Ordered {
}); });
} }
}); });
log.info("初始化WebSocket主题订阅监听器成功"); log.info("初始化网络套接字主题订阅监听器成功");
} }
@Override @Override

View File

@@ -69,7 +69,7 @@ public class WebSocketUtils {
broadcastMessage.setMessage(webSocketMessage.getMessage()); broadcastMessage.setMessage(webSocketMessage.getMessage());
broadcastMessage.setSessionKeys(unsentSessionKeys); broadcastMessage.setSessionKeys(unsentSessionKeys);
RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> { RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> {
log.info(" WebSocket发送主题订阅消息topic:{} session keys:{} message:{}", log.info("网络套接字发送主题订阅消息 主题={} 会话标识={} 消息={}",
WEB_SOCKET_TOPIC, unsentSessionKeys, webSocketMessage.getMessage()); WEB_SOCKET_TOPIC, unsentSessionKeys, webSocketMessage.getMessage());
}); });
} }
@@ -84,7 +84,7 @@ public class WebSocketUtils {
WebSocketMessageDto broadcastMessage = new WebSocketMessageDto(); WebSocketMessageDto broadcastMessage = new WebSocketMessageDto();
broadcastMessage.setMessage(message); broadcastMessage.setMessage(message);
RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> { RedisUtils.publish(WEB_SOCKET_TOPIC, broadcastMessage, consumer -> {
log.info("WebSocket发送主题订阅消息topic:{} message:{}", WEB_SOCKET_TOPIC, message); log.info("网络套接字发送主题订阅消息 主题={} 消息={}", WEB_SOCKET_TOPIC, message);
}); });
} }
@@ -115,12 +115,12 @@ public class WebSocketUtils {
*/ */
private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) { private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
if (session == null || !session.isOpen()) { if (session == null || !session.isOpen()) {
log.warn("[send] session会话已经关闭"); log.warn("[发送] 会话已经关闭");
} else { } else {
try { try {
session.sendMessage(message); session.sendMessage(message);
} catch (IOException e) { } catch (IOException e) {
log.error("[send] session({}) 发送消息({}) 异常", session, message, e); log.error("[发送] 会话({}) 发送消息({}) 异常", session, message, e);
} }
} }
} }

View File

@@ -47,7 +47,7 @@ public class CustomNotifier extends AbstractEventNotifier {
case STATUS_UNKNOWN -> "服务未知异常"; //监控系统无法确定实例的当前状态 case STATUS_UNKNOWN -> "服务未知异常"; //监控系统无法确定实例的当前状态
default -> "未知状态"; //没有匹配的状态 default -> "未知状态"; //没有匹配的状态
}; };
log.info("Instance Status Change: 状态名称【{}】, 注册名称【{}】, 实例ID【{}】, 状态【{}】, 服务URL【{}】", log.info("实例状态变更:状态名称【{}】, 注册名称【{}】, 实例ID【{}】, 状态【{}】, 服务URL【{}】",
statusName, registName, instanceId, status, serviceUrl); statusName, registName, instanceId, status, serviceUrl);
} }
}); });

View File

@@ -10,15 +10,19 @@ import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.bo.*; import org.dromara.app.domain.bo.AppDeviceBo;
import org.dromara.app.domain.bo.AppScheduleBo;
import org.dromara.app.domain.bo.AppSchedulingDeviceBo;
import org.dromara.app.domain.bo.AppWateringLogBo;
import org.dromara.app.domain.mqtt.DeviceCommand; import org.dromara.app.domain.mqtt.DeviceCommand;
import org.dromara.app.domain.vo.*; import org.dromara.app.domain.vo.*;
import org.dromara.app.service.*; import org.dromara.app.service.*;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.app.service.impl.AppScheduleServiceImpl; import org.dromara.app.service.impl.AppScheduleServiceImpl;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup; import org.dromara.common.core.validate.EditGroup;
@@ -77,16 +81,15 @@ public class AppController extends BaseController {
* *
*/ */
@ApiEncrypt @ApiEncrypt
@PostMapping("/addDevice") @PostMapping("/bindDeviceStatus")
public R<Void> addDevice(@RequestBody String body) { public R<Map> bindDeviceStatus(@RequestBody String body) {
AppDeviceBo appDevic = JsonUtils.parseObject(body, AppDeviceBo.class); try {
appDevic.setUserId(LoginHelper.getUserId()); AppDeviceBo appDevice = JsonUtils.parseObject(body, AppDeviceBo.class);
Boolean b = appDeviceService.insertByBo(appDevic); appDevice.setUserId(LoginHelper.getUserId());
if (b){ Map<String, Object> map = appDeviceService.bindDeviceStatus(appDevice);
return R.ok(); return R.ok(map);
}else { } catch (Exception e) {
return fail(e);
return R.fail();
} }
} }
@@ -94,11 +97,15 @@ public class AppController extends BaseController {
* 绑定已通过MQTT注册上线的设备 * 绑定已通过MQTT注册上线的设备
*/ */
@ApiEncrypt @ApiEncrypt
@PostMapping("/bindDevice") @PostMapping("/addDevice")
public R<AppDeviceVo> bindDevice(@RequestBody String body) { public R<AppDeviceVo> bindDevice(@RequestBody String body) {
AppDeviceBo appDevice = JsonUtils.parseObject(body, AppDeviceBo.class); try {
appDevice.setUserId(LoginHelper.getUserId()); AppDeviceBo appDevice = JsonUtils.parseObject(body, AppDeviceBo.class);
return R.ok(appDeviceService.bindRegisteredDevice(appDevice)); appDevice.setUserId(LoginHelper.getUserId());
return R.ok(appDeviceService.bindRegisteredDevice(appDevice));
} catch (Exception e) {
return fail(e);
}
} }
/** /**
@@ -110,7 +117,11 @@ public class AppController extends BaseController {
@GetMapping("/device/{deviceNo}") @GetMapping("/device/{deviceNo}")
public R<AppDeviceVo> getDeviceInfo(@NotNull(message = "主键不能为空") public R<AppDeviceVo> getDeviceInfo(@NotNull(message = "主键不能为空")
@PathVariable String deviceNo) { @PathVariable String deviceNo) {
return R.ok(appDeviceService.queryById(deviceNo)); try {
return R.ok(getOwnedDevice(deviceNo));
} catch (Exception e) {
return fail(e);
}
} }
@@ -123,9 +134,11 @@ public class AppController extends BaseController {
@PutMapping("/updateDeviceInfo") @PutMapping("/updateDeviceInfo")
public R<Void> updateDeviceInfo(@Validated @RequestBody AppDeviceBo bo) { public R<Void> updateDeviceInfo(@Validated @RequestBody AppDeviceBo bo) {
try { try {
assertDeviceOwned(bo.getDeviceNo());
bo.setUserId(LoginHelper.getUserId());
return toAjax(appDeviceService.updateByBo(bo)); return toAjax(appDeviceService.updateByBo(bo));
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); return fail(e);
} }
} }
@@ -137,7 +150,14 @@ public class AppController extends BaseController {
@DeleteMapping("/deleteDevice/{deviceNos}") @DeleteMapping("/deleteDevice/{deviceNos}")
public R<Void> removeDevice(@NotEmpty(message = "主键不能为空") public R<Void> removeDevice(@NotEmpty(message = "主键不能为空")
@PathVariable String[] deviceNos) { @PathVariable String[] deviceNos) {
return toAjax(appDeviceService.deleteWithValidByIds(List.of(deviceNos), true)); try {
for (String deviceNo : deviceNos) {
assertDeviceOwned(deviceNo);
}
return toAjax(appDeviceService.deleteWithValidByIds(List.of(deviceNos), true));
} catch (Exception e) {
return fail(e);
}
} }
@@ -149,11 +169,11 @@ public class AppController extends BaseController {
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/switchDevice") @PutMapping("/switchDevice")
public R<Void> switchDevice(@RequestBody String body) { public R<Void> switchDevice(@RequestBody String body) {
Map<String,String> map = JsonUtils.parseObject(body, Map.class);
try { try {
Map<String,String> map = JsonUtils.parseObject(body, Map.class);
String deviceNo = map.get("deviceNo"); String deviceNo = map.get("deviceNo");
String status = map.get("workStatus"); String status = map.get("workStatus");
Date date = new Date(); String startTime = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String durationMinValue = map.get("durationMin"); String durationMinValue = map.get("durationMin");
Integer durationMin = StringUtils.isBlank(durationMinValue) ? null : Integer.valueOf(durationMinValue); Integer durationMin = StringUtils.isBlank(durationMinValue) ? null : Integer.valueOf(durationMinValue);
//更新设备工作状态 //更新设备工作状态
@@ -161,7 +181,7 @@ public class AppController extends BaseController {
// 记录信息到历史表 // 记录信息到历史表
return toAjax(flag); return toAjax(flag);
} catch (Exception e) { } catch (Exception e) {
return R.fail(e.getMessage()); return fail(e);
} }
} }
@@ -173,28 +193,32 @@ public class AppController extends BaseController {
*/ */
@GetMapping("/scheduleDeviceList/{scheduleId}") @GetMapping("/scheduleDeviceList/{scheduleId}")
public R<Map<String,Object>> scheduleDeviceList( @PathVariable Long scheduleId) { public R<Map<String,Object>> scheduleDeviceList( @PathVariable Long scheduleId) {
try {
Map retMap = new HashMap(); Map retMap = new HashMap();
//所有设备列表 //所有设备列表
AppDeviceBo appDeviceBo = new AppDeviceBo(); AppDeviceBo appDeviceBo = new AppDeviceBo();
appDeviceBo.setUserId(LoginHelper.getUserId()); appDeviceBo.setUserId(LoginHelper.getUserId());
List<AppDeviceVo> appDeviceVos = appDeviceService.queryList(appDeviceBo); List<AppDeviceVo> appDeviceVos = appDeviceService.queryList(appDeviceBo);
List<AppDeviceVo> appDeviceVoList = new ArrayList(); List<AppDeviceVo> appDeviceVoList = new ArrayList();
AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo(); AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
schedulingDeviceBo.setScheduleId(scheduleId); schedulingDeviceBo.setScheduleId(scheduleId);
for (AppDeviceVo appDeviceVo:appDeviceVos){ for (AppDeviceVo appDeviceVo:appDeviceVos){
List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.findByDeviceNo(appDeviceVo.getDeviceNo()); List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.findByDeviceNo(appDeviceVo.getDeviceNo());
if(appSchedulingDeviceVos.size() == 0){ if(appSchedulingDeviceVos.size() == 0){
appDeviceVoList.add(appDeviceVo); appDeviceVoList.add(appDeviceVo);
}
} }
retMap.put("deviceList", appDeviceVoList);
return R.ok(retMap);
} catch (Exception e) {
return fail(e);
} }
retMap.put("deviceList", appDeviceVoList);
return R.ok(retMap);
} }
/** /**
*绑定排程跟设备 *绑定排程跟设备
@@ -202,26 +226,32 @@ public class AppController extends BaseController {
@RepeatSubmit() @RepeatSubmit()
@PostMapping("/addScheduleDevice") @PostMapping("/addScheduleDevice")
public R<Void> addScheduleDevice( @RequestBody String body) { public R<Void> addScheduleDevice( @RequestBody String body) {
Map<String, Object> map = JsonUtils.parseObject(body, Map.class); try {
if (map == null || map.get("scheduleId") == null) { Map<String, Object> map = JsonUtils.parseObject(body, Map.class);
throw new ServiceException("scheduleId不能为空"); if (map == null || map.get("scheduleId") == null) {
} throw new ServiceException("app.schedule.id.not.blank.request");
Long scheduleId = Long.valueOf(map.get("scheduleId").toString());
List<String> deviceNos = parseDeviceNos(map);
if (deviceNos.isEmpty()) {
throw new ServiceException("deviceNos不能为空");
}
for (String deviceNo:deviceNos){
if (StringUtils.isBlank(deviceNo)) {
continue;
} }
AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo(); Long scheduleId = Long.valueOf(map.get("scheduleId").toString());
schedulingDeviceBo.setDeviceNo(deviceNo); assertScheduleOwned(scheduleId);
schedulingDeviceBo.setScheduleId(scheduleId); List<String> deviceNos = parseDeviceNos(map);
appSchedulingDeviceService.insertByBo(schedulingDeviceBo); if (deviceNos.isEmpty()) {
throw new ServiceException("app.schedule.devices.not.blank");
}
for (String deviceNo:deviceNos){
if (StringUtils.isBlank(deviceNo)) {
continue;
}
assertDeviceOwned(deviceNo);
AppSchedulingDeviceBo schedulingDeviceBo = new AppSchedulingDeviceBo();
schedulingDeviceBo.setDeviceNo(deviceNo);
schedulingDeviceBo.setScheduleId(scheduleId);
appSchedulingDeviceService.insertByBo(schedulingDeviceBo);
}
return R.ok();
} catch (Exception e) {
return fail(e);
} }
return R.ok();
} }
private List<String> parseDeviceNos(Map<String, Object> map) { private List<String> parseDeviceNos(Map<String, Object> map) {
@@ -255,9 +285,22 @@ public class AppController extends BaseController {
@Log(title = "排程与设备关联", businessType = BusinessType.DELETE) @Log(title = "排程与设备关联", businessType = BusinessType.DELETE)
@DeleteMapping("/deleteScheduleDevice") @DeleteMapping("/deleteScheduleDevice")
public R<Void> deleteScheduleDevice(@NotEmpty(message = "主键不能为空") public R<Void> deleteScheduleDevice(@RequestParam(required = false) String scheduleId,
@RequestParam String scheduleId, @RequestParam String deviceNo) { @RequestParam(required = false) String deviceNo) {
return toAjax(appSchedulingDeviceService.deleteWithValidByScheduleIdAndDeviceNo(Long.valueOf(scheduleId), deviceNo)); try {
if (StringUtils.isBlank(scheduleId)) {
throw new ServiceException("app.schedule.id.not.blank.request");
}
if (StringUtils.isBlank(deviceNo)) {
throw new ServiceException("app.device.no.not.blank");
}
Long scheduleIdValue = Long.valueOf(scheduleId);
assertScheduleOwned(scheduleIdValue);
assertDeviceOwned(deviceNo);
return toAjax(appSchedulingDeviceService.deleteWithValidByScheduleIdAndDeviceNo(scheduleIdValue, deviceNo));
} catch (Exception e) {
return fail(e);
}
} }
@@ -289,8 +332,8 @@ public class AppController extends BaseController {
} }
return R.fail(); return R.fail();
// return toAjax(appScheduleService.insertByBo(bo)); // return toAjax(appScheduleService.insertByBo(bo));
} catch (RuntimeException e) { } catch (Exception e) {
throw new RuntimeException(e); return fail(e);
} }
} }
@@ -303,48 +346,52 @@ public class AppController extends BaseController {
@GetMapping("/schedule/{id}") @GetMapping("/schedule/{id}")
public R<AppScheduleVo> getScheduleInfo(@NotNull(message = "主键不能为空") public R<AppScheduleVo> getScheduleInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) { @PathVariable Long id) {
AppScheduleVo appScheduleVo = appScheduleService.queryById(id); try {
if (ObjectUtil.isNotNull(appScheduleVo)){ AppScheduleVo appScheduleVo = getOwnedSchedule(id);
List<AppScheduleDetailVo> list = appScheduleDetailService.queryByScheduleIdByStatus(appScheduleVo.getId()); if (ObjectUtil.isNotNull(appScheduleVo)){
List<AppScheduleDetailVo> list = appScheduleDetailService.queryByScheduleIdByStatus(appScheduleVo.getId());
AppSchedulingDeviceBo appSchedulingDeviceBo = new AppSchedulingDeviceBo(); AppSchedulingDeviceBo appSchedulingDeviceBo = new AppSchedulingDeviceBo();
appSchedulingDeviceBo.setScheduleId(appScheduleVo.getId()); appSchedulingDeviceBo.setScheduleId(appScheduleVo.getId());
List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.queryList(appSchedulingDeviceBo); List<AppSchedulingDeviceVo> appSchedulingDeviceVos = appSchedulingDeviceService.queryList(appSchedulingDeviceBo);
List deviceList = new ArrayList(); List deviceList = new ArrayList();
for (AppSchedulingDeviceVo appSchedulingDeviceVo:appSchedulingDeviceVos){ for (AppSchedulingDeviceVo appSchedulingDeviceVo:appSchedulingDeviceVos){
AppDeviceVo appDeviceVo = appDeviceService.queryById(appSchedulingDeviceVo.getDeviceNo()); AppDeviceVo appDeviceVo = appDeviceService.queryById(appSchedulingDeviceVo.getDeviceNo());
if (ObjectUtil.isNotNull(appDeviceVo)){ if (ObjectUtil.isNotNull(appDeviceVo)){
deviceList.add(appDeviceVo); deviceList.add(appDeviceVo);
}
}
List detailList = new ArrayList();
for (AppScheduleDetailVo detail:list){
List timeSlots = new ArrayList();
JSONArray mapArray = JSONUtil.parseArray(detail.getTimeData());
for (Object timeSlot:mapArray){
timeSlots.add(timeSlot);
}
Map map = new HashMap();
map.put("id",detail.getId() );
map.put("weekday",detail.getWeekday() );
map.put("timeData",timeSlots );
map.put("triggerType",detail.getTriggerType() );
map.put("status",detail.getStatus() );
detailList.add(map);
} }
}
List detailList = new ArrayList();
for (AppScheduleDetailVo detail:list){
List timeSlots = new ArrayList();
JSONArray mapArray = JSONUtil.parseArray(detail.getTimeData());
for (Object timeSlot:mapArray){
timeSlots.add(timeSlot);
}
Map map = new HashMap(); appScheduleVo.setDetails(detailList);
map.put("id",detail.getId() ); appScheduleVo.setDeviceNos(deviceList);
map.put("weekday",detail.getWeekday() ); return R.ok(appScheduleVo);
map.put("timeData",timeSlots );
map.put("triggerType",detail.getTriggerType() );
map.put("status",detail.getStatus() );
detailList.add(map);
} }
return R.fail(appScheduleVo);
appScheduleVo.setDetails(detailList); } catch (Exception e) {
appScheduleVo.setDeviceNos(deviceList); return fail(e);
return R.ok(appScheduleVo);
} }
return R.fail(appScheduleVo);
} }
@@ -356,8 +403,13 @@ public class AppController extends BaseController {
@PutMapping("/editScheduleStatus") @PutMapping("/editScheduleStatus")
public R<Void> editScheduleStatus(@NotNull(message = "主键不能为空") public R<Void> editScheduleStatus(@NotNull(message = "主键不能为空")
@RequestBody AppScheduleBo bo) { @RequestBody AppScheduleBo bo) {
try {
return toAjax(appScheduleService.updateStatusByBo(bo)); assertScheduleOwned(bo.getId());
bo.setUserId(LoginHelper.getUserId());
return toAjax(appScheduleService.updateStatusByBo(bo));
} catch (Exception e) {
return fail(e);
}
} }
/** /**
@@ -367,11 +419,17 @@ public class AppController extends BaseController {
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/updataschedule") @PutMapping("/updataschedule")
public R<Object> editSchedule(@Validated(EditGroup.class) @RequestBody AppScheduleBo bo) { public R<Object> editSchedule(@Validated(EditGroup.class) @RequestBody AppScheduleBo bo) {
Map<String, Object> map = appScheduleService.updateByBo(bo); try {
if (Boolean.valueOf(map.get("flag").toString())){ assertScheduleOwned(bo.getId());
return R.ok(map.get("appSchedule")); bo.setUserId(LoginHelper.getUserId());
}else { Map<String, Object> map = appScheduleService.updateByBo(bo);
return R.fail(); if (Boolean.valueOf(map.get("flag").toString())){
return R.ok(map.get("appSchedule"));
}else {
return R.fail();
}
} catch (Exception e) {
return fail(e);
} }
} }
@@ -386,7 +444,14 @@ public class AppController extends BaseController {
@DeleteMapping("/deleteschedule/{ids}") @DeleteMapping("/deleteschedule/{ids}")
public R<Void> removeSchedule(@NotEmpty(message = "主键不能为空") public R<Void> removeSchedule(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) { @PathVariable Long[] ids) {
return toAjax(appScheduleService.deleteWithValidByIds(List.of(ids), true)); try {
for (Long id : ids) {
assertScheduleOwned(id);
}
return toAjax(appScheduleService.deleteWithValidByIds(List.of(ids), true));
} catch (Exception e) {
return fail(e);
}
} }
/** /**
@@ -427,7 +492,14 @@ public class AppController extends BaseController {
@DeleteMapping("/waterLog/{ids}") @DeleteMapping("/waterLog/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) { @PathVariable Long[] ids) {
return toAjax(appWateringLogService.deleteWithValidByIds(List.of(ids), true)); try {
for (Long id : ids) {
assertWateringLogOwned(id);
}
return toAjax(appWateringLogService.deleteWithValidByIds(List.of(ids), true));
} catch (Exception e) {
return fail(e);
}
} }
@@ -463,7 +535,7 @@ public class AppController extends BaseController {
map.put("averageTime",averageTime); map.put("averageTime",averageTime);
return R.ok(map); return R.ok(map);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); return fail(e);
} }
} }
@@ -475,16 +547,20 @@ public class AppController extends BaseController {
*/ */
@GetMapping("/getUserInfo") @GetMapping("/getUserInfo")
public R<UserInfoVo> getUserInfo() { public R<UserInfoVo> getUserInfo() {
UserInfoVo userInfoVo = new UserInfoVo(); try {
LoginUser loginUser = LoginHelper.getLoginUser(); UserInfoVo userInfoVo = new UserInfoVo();
SysUserVo user = DataPermissionHelper.ignore(() -> userService.selectUserById(loginUser.getUserId())); LoginUser loginUser = LoginHelper.getLoginUser();
if (ObjectUtil.isNull(user)) { SysUserVo user = DataPermissionHelper.ignore(() -> userService.selectUserById(loginUser.getUserId()));
return R.fail("没有权限访问用户数据!"); if (ObjectUtil.isNull(user)) {
return R.fail(MessageUtils.message("app.user.data.denied"));
}
userInfoVo.setUser(user);
userInfoVo.setPermissions(loginUser.getMenuPermission());
userInfoVo.setRoles(loginUser.getRolePermission());
return R.ok(userInfoVo);
} catch (Exception e) {
return fail(e);
} }
userInfoVo.setUser(user);
userInfoVo.setPermissions(loginUser.getMenuPermission());
userInfoVo.setRoles(loginUser.getRolePermission());
return R.ok(userInfoVo);
} }
/** /**
@@ -495,13 +571,17 @@ public class AppController extends BaseController {
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/updataUser") @PutMapping("/updataUser")
public R<Void> editUser( @RequestBody SysUserBo user) { public R<Void> editUser( @RequestBody SysUserBo user) {
userService.checkUserAllowed(user.getUserId()); try {
userService.checkUserDataScope(user.getUserId()); userService.checkUserAllowed(user.getUserId());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { userService.checkUserDataScope(user.getUserId());
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
} return R.fail(MessageUtils.message("app.user.phone.exists", user.getUserName()));
}
return toAjax(userService.updateAppUser(user)); return toAjax(userService.updateAppUser(user));
} catch (Exception e) {
return fail(e);
}
} }
/** /**
@@ -510,15 +590,19 @@ public class AppController extends BaseController {
@RepeatSubmit() @RepeatSubmit()
@PutMapping("/updataPassword") @PutMapping("/updataPassword")
public R<Void> updataPassword(@RequestBody SysUserVo vo) { public R<Void> updataPassword(@RequestBody SysUserVo vo) {
Long userId = LoginHelper.getUserId(); try {
SysUserVo sysUserVo = userService.selectUserById(userId); Long userId = LoginHelper.getUserId();
if (!BCrypt.checkpw(vo.getOldPassword(), sysUserVo.getPassword())){ SysUserVo sysUserVo = userService.selectUserById(userId);
throw new UserException("user.password.not.match"); if (!BCrypt.checkpw(vo.getOldPassword(), sysUserVo.getPassword())){
throw new UserException("user.password.not.match");
}
SysUserBo userBo = new SysUserBo();
userBo.setUserId(userId);
userBo.setPassword(vo.getPassword());
return toAjax(userService.updateUserPas(userBo));
} catch (Exception e) {
return fail(e);
} }
SysUserBo userBo = new SysUserBo();
userBo.setUserId(userId);
userBo.setPassword(vo.getPassword());
return toAjax(userService.updateUserPas(userBo));
} }
/** /**
@@ -527,35 +611,99 @@ public class AppController extends BaseController {
@ApiEncrypt @ApiEncrypt
@PostMapping("/retrievePassword") @PostMapping("/retrievePassword")
public R<Void> forgot(@RequestBody String body) { public R<Void> forgot(@RequestBody String body) {
SysUserBo bo = JsonUtils.parseObject(body, SysUserBo.class); try {
bo.setUserId(LoginHelper.getUserId()); SysUserBo bo = JsonUtils.parseObject(body, SysUserBo.class);
bo.setUserId(LoginHelper.getUserId());
if(Validator.isEmail(bo.getUserName())){ if(Validator.isEmail(bo.getUserName())){
boolean checkPhoneFlag = userService.checkEmailUnique(bo); boolean checkPhoneFlag = userService.checkEmailUnique(bo);
if (!checkPhoneFlag){ if (!checkPhoneFlag){
throw new UserException("user.email.not.username"); throw new UserException("user.email.not.username");
} }
}else if (Validator.isMobile(bo.getUserName())){ }else if (Validator.isMobile(bo.getUserName())){
boolean checkPhoneFlag = userService.checkPhoneUnique(bo); boolean checkPhoneFlag = userService.checkPhoneUnique(bo);
if (!checkPhoneFlag){ if (!checkPhoneFlag){
throw new UserException("user.mobile.phone.number.not.username"); throw new UserException("user.mobile.phone.number.not.username");
}
} }
return toAjax(userService.updateUserPas(bo));
} catch (Exception e) {
return fail(e);
} }
return toAjax(userService.updateUserPas(bo));
} }
@GetMapping("/test") @GetMapping("/test")
public R<Void> test (){ public R<Void> test (){
try {
DeviceCommand deviceCommand = new DeviceCommand(); DeviceCommand deviceCommand = new DeviceCommand();
String bindDevice = deviceCommandService.sendBindDeviceCommand("01"); String bindDevice = deviceCommandService.sendBindDeviceCommand("01");
// String s = deviceCommandService.sendSwitchCommand("01", "1", "2026-06-10 14:35:31", 20); // String s = deviceCommandService.sendSwitchCommand("01", "1", "2026-06-10 14:35:31", 20);
return R.ok(bindDevice); return R.ok(bindDevice);
} catch (Exception e) {
return fail(e);
}
}
private <T> R<T> fail(Exception e) {
return R.fail(toI18nErrorMessage(e));
}
private String toI18nErrorMessage(Exception e) {
String message = e.getMessage();
if (StringUtils.isBlank(message)) {
return MessageUtils.message("operation.fail");
}
String i18nMessage = MessageUtils.message(message);
return message.equals(i18nMessage) ? message : i18nMessage;
}
private AppDeviceVo getOwnedDevice(String deviceNo) {
if (StringUtils.isBlank(deviceNo)) {
throw new ServiceException("app.device.no.not.blank");
}
AppDeviceVo device = appDeviceService.queryById(deviceNo);
Long userId = LoginHelper.getUserId();
if (device == null || !Objects.equals(device.getUserId(), userId)) {
throw new ServiceException("app.device.not.exists.or.denied");
}
return device;
}
private void assertDeviceOwned(String deviceNo) {
getOwnedDevice(deviceNo);
}
private AppScheduleVo getOwnedSchedule(Long scheduleId) {
if (scheduleId == null) {
throw new ServiceException("app.schedule.id.not.blank");
}
AppScheduleVo schedule = appScheduleService.queryById(scheduleId);
Long userId = LoginHelper.getUserId();
if (schedule == null || !Objects.equals(schedule.getUserId(), userId)) {
throw new ServiceException("app.schedule.not.exists.or.denied");
}
return schedule;
}
private void assertScheduleOwned(Long scheduleId) {
getOwnedSchedule(scheduleId);
}
private void assertWateringLogOwned(Long id) {
if (id == null) {
throw new ServiceException("app.watering.log.id.not.blank");
}
AppWateringLogVo log = appWateringLogService.queryById(id);
Long userId = LoginHelper.getUserId();
if (log == null || !Objects.equals(log.getUserId(), userId)) {
throw new ServiceException("app.watering.log.not.exists.or.denied");
}
} }
} }

View File

@@ -1,14 +1,22 @@
package org.dromara.app.handler; package org.dromara.app.handler;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.domain.bo.AppDeviceBo; import org.dromara.app.domain.bo.AppDeviceBo;
import org.dromara.app.mapper.AppDeviceMapper;
import org.dromara.app.mqtt.MqttTopicHandler; import org.dromara.app.mqtt.MqttTopicHandler;
import org.dromara.app.service.IAppDeviceService; import org.dromara.app.service.IAppDeviceService;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -22,6 +30,13 @@ public class DeviceDataHandler implements MqttTopicHandler {
private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/power$"); private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/power$");
private final IAppDeviceService appDeviceService; private final IAppDeviceService appDeviceService;
private final AppDeviceMapper appDeviceMapper;
@Value("${mqtt.command-ack.device-status-cache-prefix}")
private String deviceStatusCachePrefix;
@Value("${mqtt.command-ack.device-status-cache-ttl-seconds}")
private int deviceStatusCacheTtlSeconds;
@Override @Override
public Pattern topicPattern() { public Pattern topicPattern() {
@@ -32,14 +47,44 @@ public class DeviceDataHandler implements MqttTopicHandler {
public void handle(String deviceNo, String payload) { public void handle(String deviceNo, String payload) {
try { try {
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class); Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
if (dto == null || dto.get("powerLevel") == null) {
log.warn("[MQTT] 设备电量上报缺少电量字段 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
refreshDeviceOnline(deviceNo);
return;
}
// 更新设备电量 + 同步在线状态到数据库
AppDeviceBo appDeviceBo = new AppDeviceBo(); AppDeviceBo appDeviceBo = new AppDeviceBo();
appDeviceBo.setDeviceNo(deviceNo); appDeviceBo.setDeviceNo(deviceNo);
appDeviceBo.setPowerLevel(dto.get("powerLevel").toString()); appDeviceBo.setPowerLevel(dto.get("powerLevel").toString());
appDeviceBo.setPowerLevelUpdatatime(new Date()); appDeviceBo.setPowerLevelUpdatatime(new Date());
appDeviceBo.setStatus("1"); // 收到数据 = 在线
appDeviceService.updateByBo(appDeviceBo); appDeviceService.updateByBo(appDeviceBo);
log.info("[MQTT] 设备电量更新 deviceNo={} payload={}", deviceNo, payload); // 同步刷新 Redis 在线缓存(定时任务离线检测依赖此 Key
refreshDeviceOnline(deviceNo);
log.info("[MQTT] 设备电量更新 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
} catch (Exception e) { } catch (Exception e) {
log.error("[MQTT] 设备电量更新失败 deviceNo={} payload={}", deviceNo, payload, e); log.error("[MQTT] 设备电量更新失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
} }
} }
/**
* 刷新设备在线状态到 Redis与 MqttCommandAckService 逻辑一致)
* 写入 Redis 后 TTL 到期自动过期 = 离线
*/
private void refreshDeviceOnline(String deviceNo) {
Map<String, Object> statusCache = new HashMap<>();
statusCache.put("deviceNo", deviceNo);
statusCache.put("status", "1");
statusCache.put("lastReportTime", Instant.now().toString());
RedisUtils.setCacheObject(
deviceStatusCachePrefix + deviceNo,
statusCache,
Duration.ofSeconds(deviceStatusCacheTtlSeconds)
);
appDeviceMapper.update(null,
new LambdaUpdateWrapper<AppDevice>()
.set(AppDevice::getStatus, "1")
.eq(AppDevice::getDeviceNo, deviceNo)
);
}
} }

View File

@@ -1,13 +1,21 @@
package org.dromara.app.handler; package org.dromara.app.handler;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.domain.bo.AppDeviceBo; import org.dromara.app.domain.bo.AppDeviceBo;
import org.dromara.app.mapper.AppDeviceMapper;
import org.dromara.app.mqtt.MqttTopicHandler; import org.dromara.app.mqtt.MqttTopicHandler;
import org.dromara.app.service.IAppDeviceService; import org.dromara.app.service.IAppDeviceService;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -22,6 +30,13 @@ public class DeviceRegisterHandler implements MqttTopicHandler {
private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/register$"); private static final Pattern PATTERN = Pattern.compile("^/([^/]+)/publish/register$");
private final IAppDeviceService appDeviceService; private final IAppDeviceService appDeviceService;
private final AppDeviceMapper appDeviceMapper;
@Value("${mqtt.command-ack.device-status-cache-prefix}")
private String deviceStatusCachePrefix;
@Value("${mqtt.command-ack.device-status-cache-ttl-seconds}")
private int deviceStatusCacheTtlSeconds;
@Override @Override
public Pattern topicPattern() { public Pattern topicPattern() {
@@ -34,15 +49,18 @@ public class DeviceRegisterHandler implements MqttTopicHandler {
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class); Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
AppDeviceBo device = buildRegisterDevice(deviceNo, dto); AppDeviceBo device = buildRegisterDevice(deviceNo, dto);
appDeviceService.registerByMqtt(device); appDeviceService.registerByMqtt(device);
log.info("[MQTT] 设备注册 deviceNo={} payload={}", deviceNo, payload); // 注册即在线
refreshDeviceOnline(deviceNo);
log.info("[MQTT] 设备注册 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
} catch (Exception e) { } catch (Exception e) {
log.error("[MQTT] 设备注册失败 deviceNo={} payload={}", deviceNo, payload, e); log.error("[MQTT] 设备注册失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
} }
} }
private AppDeviceBo buildRegisterDevice(String topicDeviceNo, Map<String, Object> dto) { private AppDeviceBo buildRegisterDevice(String topicDeviceNo, Map<String, Object> dto) {
AppDeviceBo device = new AppDeviceBo(); AppDeviceBo device = new AppDeviceBo();
device.setDeviceNo(topicDeviceNo); device.setDeviceNo(topicDeviceNo);
device.setStatus("1"); // 注册即在线
if (dto == null) { if (dto == null) {
return device; return device;
} }
@@ -67,4 +85,24 @@ public class DeviceRegisterHandler implements MqttTopicHandler {
private String valueAsString(Object value) { private String valueAsString(Object value) {
return value == null ? null : String.valueOf(value); return value == null ? null : String.valueOf(value);
} }
/**
* 刷新设备在线状态到 Redis
*/
private void refreshDeviceOnline(String deviceNo) {
Map<String, Object> statusCache = new HashMap<>();
statusCache.put("deviceNo", deviceNo);
statusCache.put("status", "1");
statusCache.put("lastReportTime", Instant.now().toString());
RedisUtils.setCacheObject(
deviceStatusCachePrefix + deviceNo,
statusCache,
Duration.ofSeconds(deviceStatusCacheTtlSeconds)
);
appDeviceMapper.update(null,
new LambdaUpdateWrapper<AppDevice>()
.set(AppDevice::getStatus, "1")
.eq(AppDevice::getDeviceNo, deviceNo)
);
}
} }

View File

@@ -28,9 +28,9 @@ public class ErromesHandler implements MqttTopicHandler {
public void handle(String deviceNo, String payload) { public void handle(String deviceNo, String payload) {
try { try {
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class); Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
log.info("[MQTT] 设备异常告警 deviceNo={} payload={}", deviceNo, dto); log.info("[MQTT] 设备异常告警 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, dto);
} catch (Exception e) { } catch (Exception e) {
log.error("[MQTT] 设备异常处理失败 deviceNo={}", deviceNo, e); log.error("[MQTT] 设备异常处理失败 时间={} 设备编号={}", HandlerLogTime.now(), deviceNo, e);
} }
} }
} }

View File

@@ -0,0 +1,13 @@
package org.dromara.app.handler;
import org.dromara.common.core.utils.DateUtils;
final class HandlerLogTime {
private HandlerLogTime() {
}
static String now() {
return DateUtils.getTime();
}
}

View File

@@ -41,16 +41,16 @@ public class KeyFinishHandler implements MqttTopicHandler {
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class); Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
AppWateringLogBo logBo = buildWateringLog(deviceNo, dto); AppWateringLogBo logBo = buildWateringLog(deviceNo, dto);
appWateringLogService.insertByBo(logBo); appWateringLogService.insertByBo(logBo);
log.info("[MQTT] 按键浇水完成上报已保存 deviceNo={} payload={}", deviceNo, payload); log.info("[MQTT] 按键浇水完成上报已保存 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
} catch (Exception e) { } catch (Exception e) {
log.error("[MQTT] 按键浇水完成上报处理失败 deviceNo={} payload={}", deviceNo, payload, e); log.error("[MQTT] 按键浇水完成上报处理失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
} }
} }
private AppWateringLogBo buildWateringLog(String topicDeviceNo, Map<String, Object> dto) { private AppWateringLogBo buildWateringLog(String topicDeviceNo, Map<String, Object> dto) {
String payloadDeviceNo = valueAsString(dto.get("deviceNo")); String payloadDeviceNo = valueAsString(dto.get("deviceNo"));
if (StringUtils.isNotBlank(payloadDeviceNo) && !topicDeviceNo.equals(payloadDeviceNo)) { if (StringUtils.isNotBlank(payloadDeviceNo) && !topicDeviceNo.equals(payloadDeviceNo)) {
log.warn("[MQTT] 按键浇水完成上报设备编号不一致 topicDeviceNo={} payloadDeviceNo={}", topicDeviceNo, payloadDeviceNo); log.warn("[MQTT] 按键浇水完成上报设备编号不一致 时间={} 主题设备编号={} 消息体设备编号={}", HandlerLogTime.now(), topicDeviceNo, payloadDeviceNo);
} }
Date receivedAt = new Date(); Date receivedAt = new Date();
@@ -127,7 +127,7 @@ public class KeyFinishHandler implements MqttTopicHandler {
} }
long diffMillis = endTime.getTime() - startTime.getTime(); long diffMillis = endTime.getTime() - startTime.getTime();
if (diffMillis < 0) { if (diffMillis < 0) {
log.warn("[MQTT] 按键浇水完成上报结束时间早于开始时间 startTime={} endTime={}", startTime, endTime); log.warn("[MQTT] 按键浇水完成上报结束时间早于开始时间 时间={} 开始时间={} 结束时间={}", HandlerLogTime.now(), startTime, endTime);
return 0; return 0;
} }
return (int) (diffMillis / 60000L); return (int) (diffMillis / 60000L);

View File

@@ -1,8 +1,8 @@
package org.dromara.app.handler; package org.dromara.app.handler;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.dromara.app.domain.AppSchedulingDevice; import org.dromara.app.domain.AppSchedulingDevice;
import org.dromara.app.domain.bo.AppWateringLogBo; import org.dromara.app.domain.bo.AppWateringLogBo;
import org.dromara.app.domain.vo.AppDeviceVo; import org.dromara.app.domain.vo.AppDeviceVo;
@@ -46,16 +46,16 @@ public class ScheduleFinishHandler implements MqttTopicHandler {
Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class); Map<String, Object> dto = JsonUtils.parseObject(payload, Map.class);
AppWateringLogBo logBo = buildWateringLog(deviceNo, dto); AppWateringLogBo logBo = buildWateringLog(deviceNo, dto);
appWateringLogService.insertByBo(logBo); appWateringLogService.insertByBo(logBo);
log.info("[MQTT] 浇水排程上报 saved deviceNo={} payload={}", deviceNo, payload); log.info("[MQTT] 浇水排程上报已保存 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload);
} catch (Exception e) { } catch (Exception e) {
log.error("[MQTT] 浇水排程上报 failed deviceNo={} payload={}", deviceNo, payload, e); log.error("[MQTT] 浇水排程上报处理失败 时间={} 设备编号={} 消息体={}", HandlerLogTime.now(), deviceNo, payload, e);
} }
} }
private AppWateringLogBo buildWateringLog(String topicDeviceNo, Map<String, Object> dto) { private AppWateringLogBo buildWateringLog(String topicDeviceNo, Map<String, Object> dto) {
String payloadDeviceNo = valueAsString(dto.get("deviceNo")); String payloadDeviceNo = valueAsString(dto.get("deviceNo"));
if (StringUtils.isNotBlank(payloadDeviceNo) && !topicDeviceNo.equals(payloadDeviceNo)) { if (StringUtils.isNotBlank(payloadDeviceNo) && !topicDeviceNo.equals(payloadDeviceNo)) {
log.warn("[MQTT] 排程完成上报设备编号不一致 topicDeviceNo={} payloadDeviceNo={}", topicDeviceNo, payloadDeviceNo); log.warn("[MQTT] 排程完成上报设备编号不一致 时间={} 主题设备编号={} 消息体设备编号={}", HandlerLogTime.now(), topicDeviceNo, payloadDeviceNo);
} }
@@ -96,11 +96,11 @@ public class ScheduleFinishHandler implements MqttTopicHandler {
.eq(AppSchedulingDevice::getDeviceNo, deviceNo) .eq(AppSchedulingDevice::getDeviceNo, deviceNo)
); );
if (schedulingDevices == null || schedulingDevices.isEmpty()) { if (schedulingDevices == null || schedulingDevices.isEmpty()) {
log.warn("[MQTT] 排程完成上报未找到设备绑定的排程 deviceNo={}", deviceNo); log.warn("[MQTT] 排程完成上报未找到设备绑定的排程 时间={} 设备编号={}", HandlerLogTime.now(), deviceNo);
return 0L; return 0L;
} }
if (schedulingDevices.size() > 1) { if (schedulingDevices.size() > 1) {
log.warn("[MQTT] 排程完成上报匹配到多个排程 deviceNo={} count={}", deviceNo, schedulingDevices.size()); log.warn("[MQTT] 排程完成上报匹配到多个排程 时间={} 设备编号={} 数量={}", HandlerLogTime.now(), deviceNo, schedulingDevices.size());
} }
return schedulingDevices.get(0).getScheduleId(); return schedulingDevices.get(0).getScheduleId();
} }
@@ -148,7 +148,7 @@ public class ScheduleFinishHandler implements MqttTopicHandler {
} }
long diffMillis = endTime.getTime() - startTime.getTime(); long diffMillis = endTime.getTime() - startTime.getTime();
if (diffMillis < 0) { if (diffMillis < 0) {
log.warn("[MQTT] 排程完成上报结束时间早于开始时间 startTime={} endTime={}", startTime, endTime); log.warn("[MQTT] 排程完成上报结束时间早于开始时间 时间={} 开始时间={} 结束时间={}", HandlerLogTime.now(), startTime, endTime);
return 0; return 0;
} }
return (int) (diffMillis / 60000L); return (int) (diffMillis / 60000L);

View File

@@ -1,12 +1,11 @@
package org.dromara.app.mapper; package org.dromara.app.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.dromara.app.domain.AppDevice; import org.dromara.app.domain.AppDevice;
import org.dromara.app.domain.vo.AppDeviceVo; import org.dromara.app.domain.vo.AppDeviceVo;
import org.dromara.common.mybatis.annotation.DataColumn;
import org.dromara.common.mybatis.annotation.DataPermission;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/** /**
* 设备信息 * 设备信息
@@ -18,26 +17,38 @@ Mapper接口
public interface AppDeviceMapper extends BaseMapperPlus<AppDevice, AppDeviceVo> { public interface AppDeviceMapper extends BaseMapperPlus<AppDevice, AppDeviceVo> {
@Update(""" @Update("""
<script> <script>
update app_device update app_device
<set> <set>
user_id = #{userId}, user_id = #{userId},
bind_token_hash = null, bind_token_hash = null,
<if test="deviceName != null and deviceName != ''"> <if test="deviceName != null and deviceName != ''">
device_name = #{deviceName}, device_name = #{deviceName},
</if> </if>
<if test="status != null and status != ''"> <if test="status != null and status != ''">
status = #{status}, status = #{status},
</if> </if>
</set> <if test="workStatus != null and workStatus != ''">
where device_no = #{deviceNo} work_status = #{workStatus},
and (user_id is null or user_id = #{userId}) </if>
</script> <if test="wifiName != null and wifiName != ''">
""") wifi_name = #{wifiName},
</if>
<if test="wifiPassword != null and wifiPassword != ''">
wifi_password = #{wifiPassword},
</if>
</set>
where device_no = #{deviceNo}
and (user_id is null or user_id = #{userId})
</script>
""")
int bindIfAvailable(@Param("deviceNo") String deviceNo, int bindIfAvailable(@Param("deviceNo") String deviceNo,
@Param("userId") Long userId, @Param("userId") Long userId,
@Param("deviceName") String deviceName, @Param("deviceName") String deviceName,
@Param("status") String status); @Param("status") String status,
@Param("workStatus") String workStatus,
@Param("wifiName") String wifiName,
@Param("wifiPassword") String wifiPassword);
@Update(""" @Update("""
update app_device update app_device
@@ -49,4 +60,11 @@ public interface AppDeviceMapper extends BaseMapperPlus<AppDevice, AppDeviceVo>
@Param("userId") Long userId, @Param("userId") Long userId,
@Param("workStatus") String workStatus); @Param("workStatus") String workStatus);
@Select("""
select *
from app_device
where mac_address = #{macAddress}
""")
AppDevice selectByMac(@Param("macAddress") String macAddress);
} }

View File

@@ -15,13 +15,13 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
public interface AppWateringLogMapper extends BaseMapperPlus<AppWateringLog, AppWateringLogVo> { public interface AppWateringLogMapper extends BaseMapperPlus<AppWateringLog, AppWateringLogVo> {
@Select("SELECT count(id) FROM app_watering_log\n" + @Select("SELECT count(id) FROM app_watering_log\n" +
"WHERE user_id=${userId} AND YEAR(create_time) = YEAR(CURDATE()) AND MONTH(create_time) = MONTH(CURDATE());") "WHERE user_id=#{userId} AND YEAR(create_time) = YEAR(CURDATE()) AND MONTH(create_time) = MONTH(CURDATE());")
Long selectByMonCout(@Param("userId") Long userId); Long selectByMonCout(@Param("userId") Long userId);
@Select("SELECT COUNT(id) \n" + @Select("SELECT COUNT(id) \n" +
"FROM app_watering_log \n" + "FROM app_watering_log \n" +
"WHERE user_id=${userId} AND WEEK(create_time) = WEEK(CURDATE());") "WHERE user_id=#{userId} AND WEEK(create_time) = WEEK(CURDATE());")
Long selectByWeekCount(@Param("userId") Long userId); Long selectByWeekCount(@Param("userId") Long userId);
@@ -29,11 +29,11 @@ public interface AppWateringLogMapper extends BaseMapperPlus<AppWateringLog, App
@Select("SELECT COUNT(id) \n" + @Select("SELECT COUNT(id) \n" +
"FROM app_watering_log \n" + "FROM app_watering_log \n" +
"WHERE user_id=${userId} AND trigger_type =${triggerType}") "WHERE user_id=#{userId} AND trigger_type =#{triggerType}")
Long selectByTriggerTypeCount(Long userId, String triggerType); Long selectByTriggerTypeCount(@Param("userId") Long userId, @Param("triggerType") String triggerType);
@Select("SELECT SUM(duration_min)\n" + @Select("SELECT SUM(duration_min)\n" +
"FROM app_watering_log \n" + "FROM app_watering_log \n" +
"WHERE user_id=${userId}") "WHERE user_id=#{userId}")
Long selectByTotalWateringTime(Long userId); Long selectByTotalWateringTime(@Param("userId") Long userId);
} }

View File

@@ -20,9 +20,9 @@ public class MqttMessageDispatcher {
public MqttMessageDispatcher(List<MqttTopicHandler> handlers) { public MqttMessageDispatcher(List<MqttTopicHandler> handlers) {
this.handlers = handlers; this.handlers = handlers;
log.info("[MQTT] dispatcher initialized with {} handlers", handlers.size()); log.info("[MQTT] 消息分发器初始化完成,处理器数量={}", handlers.size());
for (MqttTopicHandler handler : handlers) { for (MqttTopicHandler handler : handlers) {
log.info("[MQTT] └ registered: {} -> {}", handler.getClass().getSimpleName(), handler.topicPattern()); log.info("[MQTT] 已注册处理器:{} -> {}", handler.getClass().getSimpleName(), handler.topicPattern());
} }
} }
@@ -33,7 +33,7 @@ public class MqttMessageDispatcher {
* @param payload 消息体 * @param payload 消息体
*/ */
public void dispatch(String topic, String payload) { public void dispatch(String topic, String payload) {
log.debug("[MQTT] received topic={} payload={}", topic, payload); log.debug("[MQTT] 收到消息 主题={} 消息体={}", topic, payload);
for (MqttTopicHandler handler : handlers) { for (MqttTopicHandler handler : handlers) {
Matcher matcher = handler.topicPattern().matcher(topic); Matcher matcher = handler.topicPattern().matcher(topic);
@@ -44,6 +44,6 @@ public class MqttMessageDispatcher {
} }
} }
log.warn("[MQTT] no handler for topic: {} payload={}", topic, payload); log.warn("[MQTT] 未找到匹配的处理器 主题={} 消息体={}", topic, payload);
} }
} }

View File

@@ -1,14 +1,13 @@
package org.dromara.app.service; package org.dromara.app.service;
import org.dromara.app.domain.vo.AppDeviceVo;
import org.dromara.app.domain.bo.AppDeviceBo; import org.dromara.app.domain.bo.AppDeviceBo;
import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.app.domain.vo.AppDeviceVo;
import org.dromara.common.mybatis.annotation.DataPermission;
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 org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 设备信息 * 设备信息
@@ -97,4 +96,6 @@ public interface IAppDeviceService {
* @return 是否删除成功 * @return 是否删除成功
*/ */
Boolean deleteWithValidByIds(Collection<String> deviceNos, Boolean isValid); Boolean deleteWithValidByIds(Collection<String> deviceNos, Boolean isValid);
Map<String, Object> bindDeviceStatus(AppDeviceBo appDevice);
} }

View File

@@ -25,15 +25,12 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
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.transaction.annotation.Transactional;
import java.util.Collection; import java.util.*;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -116,7 +113,7 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
device.setPowerLevelUpdatatime(new Date()); device.setPowerLevelUpdatatime(new Date());
// applyRegisterBindToken(device, bo, exists); // applyRegisterBindToken(device, bo, exists);
if (StringUtils.isBlank(device.getStatus())) { if (StringUtils.isBlank(device.getStatus())) {
device.setStatus("2"); device.setStatus("1"); // 注册默认在线
device.setWorkStatus("2"); device.setWorkStatus("2");
} }
@@ -131,6 +128,45 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
return baseMapper.updateById(device) > 0; return baseMapper.updateById(device) > 0;
} }
@Override
public Map<String, Object> bindDeviceStatus(AppDeviceBo bo) {
Map<String, Object> params = new HashMap<>();
params.put("bindDeviceStatus", 200);
params.put("bindDeviceStatusName", "");
if (bo == null) {
params.put("bindDeviceStatus", 300);
params.put("bindDeviceStatusName", "设备信息不能为空");
return params;
}
if (StringUtils.isBlank(bo.getMacAddress())) {
params.put("bindDeviceStatus", 301);
params.put("bindDeviceStatusName", "MAC地址不能为空");
return params;
}
params.put("bindDevice", null);
Long userId = LoginHelper.getUserId();
AppDevice exists = baseMapper.selectByMac(bo.getMacAddress());
if (exists == null) {
params.put("bindDeviceStatus", 302);
params.put("bindDeviceStatusName", "设备未上线注册,请先完成配网");
return params;
}
params.put("bindDevice", exists);
if (exists.getUserId() != null && !exists.getUserId().equals(userId)) {
params.put("bindDeviceStatus", 303);
params.put("bindDeviceStatusName", "设备已被其他用户绑定");
return params;
}
if (exists.getUserId() == null ) {
params.put("bindDeviceStatus", 304);
params.put("bindDeviceStatusName", "设备未绑定,请先绑定用户");
return params;
}
return params;
}
@Override @Override
public AppDeviceVo bindRegisteredDevice(AppDeviceBo bo) { public AppDeviceVo bindRegisteredDevice(AppDeviceBo bo) {
if (StringUtils.isBlank(bo.getDeviceNo())) { if (StringUtils.isBlank(bo.getDeviceNo())) {
@@ -147,8 +183,10 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
// validBindToken(bo.getBindToken(), exists); // validBindToken(bo.getBindToken(), exists);
String status = StringUtils.isBlank(bo.getStatus()) ? exists.getStatus() : bo.getStatus(); String status = StringUtils.isBlank(bo.getStatus()) ? exists.getStatus() : bo.getStatus();
String wifiName = StringUtils.isBlank(bo.getWifiName()) ? exists.getWifiName() : bo.getWifiName();
String wifiPassword = StringUtils.isBlank(bo.getWifiPassword()) ? exists.getWifiPassword() : bo.getWifiPassword();
String deviceName = StringUtils.isBlank(bo.getDeviceName()) ? exists.getDeviceName() : bo.getDeviceName(); String deviceName = StringUtils.isBlank(bo.getDeviceName()) ? exists.getDeviceName() : bo.getDeviceName();
int rows = baseMapper.bindIfAvailable(exists.getDeviceNo(), userId, deviceName, status); int rows = baseMapper.bindIfAvailable(exists.getDeviceNo(), userId, deviceName, status,"0",wifiName,wifiPassword);
if (rows <= 0) { if (rows <= 0) {
AppDevice current = baseMapper.selectById(bo.getDeviceNo()); AppDevice current = baseMapper.selectById(bo.getDeviceNo());
if (current == null) { if (current == null) {
@@ -188,7 +226,7 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
if (ObjectUtil.isNotNull(device) && StringUtils.isNotBlank(device.getDeviceNo())) { if (ObjectUtil.isNotNull(device) && StringUtils.isNotBlank(device.getDeviceNo())) {
// 通过业务层统一下发命令(含浇水日志记录) // 通过业务层统一下发命令(含浇水日志记录)
String commandId = deviceCommandService.sendSwitchCommand(deviceNo, workStatus, startTime, durationMin); String commandId = deviceCommandService.sendSwitchCommand(deviceNo, workStatus, startTime, durationMin);
log.info("[DEVICE] switch command sent deviceNo={} commandId={}", deviceNo, commandId); log.info("[设备] 开关命令已下发 设备编号={} 命令编号={}", deviceNo, commandId);
} }
return true; return true;

View File

@@ -25,11 +25,7 @@ import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -88,7 +84,13 @@ public class AppScheduleServiceImpl implements IAppScheduleService {
@Override @Override
public Boolean updateStatusByBo(AppScheduleBo bo) { public Boolean updateStatusByBo(AppScheduleBo bo) {
AppSchedule update = MapstructUtils.convert(bo, AppSchedule.class); AppSchedule update = MapstructUtils.convert(bo, AppSchedule.class);
return baseMapper.updateById(update) > 0; Long userId = bo.getUserId() == null ? LoginHelper.getUserId() : bo.getUserId();
return baseMapper.update(
update,
Wrappers.<AppSchedule>lambdaQuery()
.eq(AppSchedule::getId, bo.getId())
.eq(AppSchedule::getUserId, userId)
) > 0;
} }
@Override @Override
@@ -98,7 +100,13 @@ public class AppScheduleServiceImpl implements IAppScheduleService {
validEntityBeforeSave(update); validEntityBeforeSave(update);
Map<String, Object> repMap = new HashMap<>(); Map<String, Object> repMap = new HashMap<>();
Boolean flag = baseMapper.updateById(update) > 0; Long userId = bo.getUserId() == null ? LoginHelper.getUserId() : bo.getUserId();
Boolean flag = baseMapper.update(
update,
Wrappers.<AppSchedule>lambdaQuery()
.eq(AppSchedule::getId, bo.getId())
.eq(AppSchedule::getUserId, userId)
) > 0;
repMap.put("flag", flag); repMap.put("flag", flag);
repMap.put("list", new ArrayList<>()); repMap.put("list", new ArrayList<>());
repMap.put("appSchedule", ""); repMap.put("appSchedule", "");

View File

@@ -3,13 +3,13 @@ package org.dromara.app.service.impl;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.bo.AppWateringLogBo;
import org.dromara.app.domain.mqtt.DeviceCommand; import org.dromara.app.domain.mqtt.DeviceCommand;
import org.dromara.app.domain.vo.AppDeviceVo; import org.dromara.app.domain.vo.AppDeviceVo;
import org.dromara.app.service.IAppDeviceService; import org.dromara.app.service.IAppDeviceService;
import org.dromara.app.service.IAppWateringLogService; import org.dromara.app.service.IAppWateringLogService;
import org.dromara.app.service.IDeviceCommandPublisher; import org.dromara.app.service.IDeviceCommandPublisher;
import org.dromara.app.service.IDeviceCommandService; import org.dromara.app.service.IDeviceCommandService;
import org.dromara.app.domain.bo.AppWateringLogBo;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
@@ -50,7 +50,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
public String sendCommand(DeviceCommand command) { public String sendCommand(DeviceCommand command) {
validate(command); validate(command);
String commandId = commandPublisher.send(command); String commandId = commandPublisher.send(command);
log.info("[CMD] command sent deviceNo={} commandType={} commandId={}", log.info("[命令] 命令已下发 设备编号={} 命令类型={} 命令编号={}",
command.getDeviceNo(), command.getCommandType(), commandId); command.getDeviceNo(), command.getCommandType(), commandId);
return commandId; return commandId;
} }
@@ -76,7 +76,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
} }
String commandId = commandPublisher.send(command); String commandId = commandPublisher.send(command);
log.info("[CMD] switch command sent deviceNo={} workStatus={} durationMin={} commandId={}", log.info("[命令] 开关命令已下发 设备编号={} 工作状态={} 持续分钟数={} 命令编号={}",
deviceNo, workStatus, durationMin, commandId); deviceNo, workStatus, durationMin, commandId);
Long userId = LoginHelper.getUserId(); Long userId = LoginHelper.getUserId();
@@ -86,7 +86,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
} else { } else {
boolean updated = wateringLogService.finishManualLog(deviceNo, userId, new Date()); boolean updated = wateringLogService.finishManualLog(deviceNo, userId, new Date());
if (!updated) { if (!updated) {
log.warn("[CMD] no active manual watering log found deviceNo={} userId={} stopCommandId={}", log.warn("[命令] 未找到进行中的手动浇水记录 设备编号={} 用户ID={} 停止命令编号={}",
deviceNo, userId, commandId); deviceNo, userId, commandId);
} }
} }
@@ -119,7 +119,7 @@ public class DeviceCommandServiceImpl implements IDeviceCommandService {
command.setCommandType("bindDevice"); command.setCommandType("bindDevice");
Map<String, Object> objectObjectHashMap = new HashMap<>(); Map<String, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("bindStatus", true); objectObjectHashMap.put("bindStatus", true);
objectObjectHashMap.put("deviceNo", "01"); objectObjectHashMap.put("deviceNo", deviceNo);
objectObjectHashMap.put("cmd", -1); objectObjectHashMap.put("cmd", -1);
command.getPayload().putAll(objectObjectHashMap); command.getPayload().putAll(objectObjectHashMap);
return sendCommand(command); return sendCommand(command);

View File

@@ -0,0 +1,86 @@
package org.dromara.app.task;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.mapper.AppDeviceMapper;
import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* 设备离线检测定时任务
* <p>
* 逻辑Redis 中 mqtt:device:status:{deviceNo} Key 的 TTL 由 device-status-cache-ttl-seconds 控制。
* 设备断电/断网后不再上报数据TTL 到期 Key 自动消失。
* 本任务定期扫描数据库中非离线状态的设备,如果对应 Redis Key 已不存在,
* 则将数据库 status 更新为 0离线
* </p>
*
* <p>扫描间隔通过 {@code mqtt.offline-check.interval-ms} 配置,建议小于设备在线 Key 的 TTL。</p>
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DeviceOfflineCheckTask {
private final AppDeviceMapper appDeviceMapper;
@Value("${mqtt.command-ack.device-status-cache-prefix}")
private String deviceStatusCachePrefix;
@Value("${mqtt.offline-check.enabled:true}")
private boolean enabled;
/**
* 每 90 秒检查一次Redis TTL 默认 300s90s 间隔确保 1.5 个周期内同步)
* 可通过 mqtt.offline-check.interval-ms 覆盖(单位 ms
*/
@Scheduled(fixedDelayString = "${mqtt.offline-check.interval-ms:90000}")
@Transactional(rollbackFor = Exception.class)
public void checkOfflineDevices() {
if (!enabled) {
return;
}
// 查询数据库中所有非离线状态的设备,避免状态不是 1 但仍需离线修正的设备被漏掉
List<AppDevice> onlineDevices = appDeviceMapper.selectList(
new LambdaQueryWrapper<AppDevice>()
.ne(AppDevice::getStatus, "0")
.select(AppDevice::getDeviceNo)
);
if (onlineDevices.isEmpty()) {
return;
}
// 过滤出 Redis Key 已过期(不存在)的设备
List<String> offlineDeviceNos = onlineDevices.stream()
.map(AppDevice::getDeviceNo)
.filter(deviceNo -> !RedisUtils.hasKey(deviceStatusCachePrefix + deviceNo))
.collect(Collectors.toList());
if (offlineDeviceNos.isEmpty()) {
log.debug("[设备离线] 本次检测未发现离线设备,扫描设备数:{}", onlineDevices.size());
return;
}
// 批量更新为离线
int updated = appDeviceMapper.update(null,
new LambdaUpdateWrapper<AppDevice>()
.set(AppDevice::getStatus, "0")
.ne(AppDevice::getStatus, "0")
.in(AppDevice::getDeviceNo, offlineDeviceNos)
);
log.info("[设备离线] 检测到 {} 台设备离线,已更新数据库状态,设备编号:{}",
updated, offlineDeviceNos);
}
}

View File

@@ -56,7 +56,7 @@ public class SaTokenTestController {
@SaCheckRole("none_exist") // 该注解会被忽略 @SaCheckRole("none_exist") // 该注解会被忽略
@GetMapping("/basic/ignoreAll") @GetMapping("/basic/ignoreAll")
public R<Void> ignoreAll() { public R<Void> ignoreAll() {
log.info("【场景4】SaIgnore忽略所有权限校验"); log.info("【场景4】忽略注解跳过所有权限校验");
return R.ok("SaIgnore生效所有权限校验被忽略"); return R.ok("SaIgnore生效所有权限校验被忽略");
} }
@@ -68,7 +68,7 @@ public class SaTokenTestController {
@SaCheckRole(value = {"admin", "operator"}, mode = SaMode.AND) @SaCheckRole(value = {"admin", "operator"}, mode = SaMode.AND)
@GetMapping("/advance/multiRoleAnd") @GetMapping("/advance/multiRoleAnd")
public R<Void> multiRoleAnd() { public R<Void> multiRoleAnd() {
log.info("【场景5】多角色AND模式(admin+operator)校验通过"); log.info("【场景5】多角色模式(admin+operator)校验通过");
return R.ok("同时拥有admin和operator角色校验通过"); return R.ok("同时拥有admin和operator角色校验通过");
} }
@@ -78,7 +78,7 @@ public class SaTokenTestController {
@SaCheckRole(value = {"admin", "test"}, mode = SaMode.OR) @SaCheckRole(value = {"admin", "test"}, mode = SaMode.OR)
@GetMapping("/advance/multiRoleOr") @GetMapping("/advance/multiRoleOr")
public R<Void> multiRoleOr() { public R<Void> multiRoleOr() {
log.info("【场景6】多角色OR模式(admin|test)校验通过"); log.info("【场景6】多角色模式(admin|test)校验通过");
return R.ok("拥有admin或test角色校验通过"); return R.ok("拥有admin或test角色校验通过");
} }
@@ -88,7 +88,7 @@ public class SaTokenTestController {
@SaCheckPermission(value = {"system:user:edit", "system:log:view"}, mode = SaMode.AND) @SaCheckPermission(value = {"system:user:edit", "system:log:view"}, mode = SaMode.AND)
@GetMapping("/advance/multiPermAnd") @GetMapping("/advance/multiPermAnd")
public R<Void> multiPermAnd() { public R<Void> multiPermAnd() {
log.info("【场景7】多权限AND模式(system:user:edit+system:log:view)校验通过"); log.info("【场景7】多权限模式(system:user:edit+system:log:view)校验通过");
return R.ok("同时拥有system:user:edit和system:log:view权限校验通过"); return R.ok("同时拥有system:user:edit和system:log:view权限校验通过");
} }
@@ -98,7 +98,7 @@ public class SaTokenTestController {
@SaCheckPermission(value = {"system:user:add", "system:user:delete"}, mode = SaMode.OR) @SaCheckPermission(value = {"system:user:add", "system:user:delete"}, mode = SaMode.OR)
@GetMapping("/advance/multiPermOr") @GetMapping("/advance/multiPermOr")
public R<Void> multiPermOr() { public R<Void> multiPermOr() {
log.info("【场景8】多权限OR模式(system:user:add|system:user:delete)校验通过"); log.info("【场景8】多权限模式(system:user:add|system:user:delete)校验通过");
return R.ok("拥有system:user:add或system:user:delete权限校验通过"); return R.ok("拥有system:user:add或system:user:delete权限校验通过");
} }
@@ -134,7 +134,7 @@ public class SaTokenTestController {
@SaCheckPermission("system:user:all") @SaCheckPermission("system:user:all")
@GetMapping("/advanced/mixRolePermAnd") @GetMapping("/advanced/mixRolePermAnd")
public R<Void> mixRolePermAnd() { public R<Void> mixRolePermAnd() {
log.info("【场景11】角色+权限混合AND(admin+system:user:all)校验通过"); log.info("【场景11】角色+权限混合且模式(admin+system:user:all)校验通过");
return R.ok("拥有admin角色且拥有system:user:all权限校验通过"); return R.ok("拥有admin角色且拥有system:user:all权限校验通过");
} }
@@ -146,7 +146,7 @@ public class SaTokenTestController {
@SaCheckPermission(value = {"system:manage"}, mode = SaMode.OR) @SaCheckPermission(value = {"system:manage"}, mode = SaMode.OR)
@GetMapping("/advanced/mixRolePermOr") @GetMapping("/advanced/mixRolePermOr")
public R<Void> mixRolePermOr() { public R<Void> mixRolePermOr() {
log.info("【场景12】角色+权限混合OR(super_admin|system:manage)校验通过"); log.info("【场景12】角色+权限混合或模式(super_admin|system:manage)校验通过");
return R.ok("拥有super_admin角色或system:manage权限校验通过"); return R.ok("拥有super_admin角色或system:manage权限校验通过");
} }
@@ -157,7 +157,7 @@ public class SaTokenTestController {
@SaCheckPermission(value = "system:user:export", orRole = {"admin", "operator"}) @SaCheckPermission(value = "system:user:export", orRole = {"admin", "operator"})
@GetMapping("/advanced/permWithOrRole") @GetMapping("/advanced/permWithOrRole")
public R<Void> permWithOrRole() { public R<Void> permWithOrRole() {
log.info("【场景13】权限+orRole兜底校验通过"); log.info("【场景13】权限+角色兜底校验通过");
return R.ok("拥有system:user:export权限或拥有admin/operator角色校验通过"); return R.ok("拥有system:user:export权限或拥有admin/operator角色校验通过");
} }
@@ -170,7 +170,7 @@ public class SaTokenTestController {
@SaIgnore @SaIgnore
@GetMapping("/special/ignoreOverride") @GetMapping("/special/ignoreOverride")
public R<Void> ignoreOverride() { public R<Void> ignoreOverride() {
log.info("【场景14】SaIgnore覆盖类级别权限注解"); log.info("【场景14】忽略注解覆盖类级别权限注解");
return R.ok("方法级SaIgnore覆盖类级别权限校验"); return R.ok("方法级SaIgnore覆盖类级别权限校验");
} }

View File

@@ -107,7 +107,7 @@ public class FlwCommonServiceImpl implements IFlwCommonService {
// // 指定获取一个短信服务商 configKey // // 指定获取一个短信服务商 configKey
// SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); // SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
// SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); // SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
log.info("【短信发送 - TODO】用户数量={} 内容={}", userList.size(), message); log.info("【短信发送 - 待实现】用户数量={} 内容={}", userList.size(), message);
} }
default -> log.warn("【消息发送】未处理的消息类型:{}", messageTypeEnum); default -> log.warn("【消息发送】未处理的消息类型:{}", messageTypeEnum);
} }

View File

@@ -194,8 +194,8 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
try { try {
defService.removeDef(ids); defService.removeDef(ids);
} catch (Exception e) { } catch (Exception e) {
log.error("Error removing flow definitions: {}", e.getMessage(), e); log.error("删除流程定义失败:{}", e.getMessage(), e);
throw new RuntimeException("Failed to remove flow definitions", e); throw new RuntimeException("删除流程定义失败", e);
} }
return true; return true;
} }

View File

@@ -266,7 +266,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService {
flowInstances.forEach(instance -> { flowInstances.forEach(instance -> {
Definition definition = definitionMap.get(instance.getDefinitionId()); Definition definition = definitionMap.get(instance.getDefinitionId());
if (ObjectUtil.isNull(definition)) { if (ObjectUtil.isNull(definition)) {
log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); log.warn("实例ID{} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId());
return; return;
} }
flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId()); flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());

View File

@@ -267,7 +267,7 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService
nodeExtVo.setVariables(variables); nodeExtVo.setVariables(variables);
} else { } else {
// 未知扩展类型,记录日志 // 未知扩展类型,记录日志
log.warn("未知扩展类型:code={}, value={}", code, value); log.warn("未知扩展类型:编码={},值={}", code, value);
} }
} }
return nodeExtVo; return nodeExtVo;

View File

@@ -283,7 +283,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand
return Pair.of(type, parts[1]); return Pair.of(type, parts[1]);
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("解析 storageId 失败,格式非法:{},错误信息:{}", storageId, e.getMessage()); log.warn("解析存储ID失败,格式非法:{},错误信息:{}", storageId, e.getMessage());
return null; return null;
} }
} }

View File

@@ -754,8 +754,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
public boolean taskOperation(TaskOperationBo bo, String taskOperation) { public boolean taskOperation(TaskOperationBo bo, String taskOperation) {
TaskOperationEnum op = TaskOperationEnum.getByCode(taskOperation); TaskOperationEnum op = TaskOperationEnum.getByCode(taskOperation);
if (op == null) { if (op == null) {
log.error("Invalid operation type:{} ", taskOperation); log.error("无效的操作类型:{}", taskOperation);
throw new ServiceException("Invalid operation type " + taskOperation); throw new ServiceException("无效的操作类型 " + taskOperation);
} }
FlowParams flowParams = FlowParams.build().message(bo.getMessage()); FlowParams flowParams = FlowParams.build().message(bo.getMessage());