设备状态离线和设备离线接到数据中心
parent
ad656d8cc3
commit
7cb223e15e
|
|
@ -1,6 +1,8 @@
|
||||||
package com.agri.framework.manager;
|
package com.agri.framework.manager;
|
||||||
|
|
||||||
import com.agri.framework.config.MqttConfig;
|
import com.agri.framework.config.MqttConfig;
|
||||||
|
import com.agri.system.domain.SysAgriInfo;
|
||||||
|
import com.agri.system.service.AgriService;
|
||||||
import com.agri.system.service.ISysAgriInfoService;
|
import com.agri.system.service.ISysAgriInfoService;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
@ -42,6 +44,8 @@ public class AgriStatusManager {
|
||||||
|
|
||||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AgriService agriService;
|
||||||
|
|
||||||
// ========== 批量查在线状态(Pipeline 优化版,JDK 8 适配) ==========
|
// ========== 批量查在线状态(Pipeline 优化版,JDK 8 适配) ==========
|
||||||
// 在线离线的都得推
|
// 在线离线的都得推
|
||||||
|
|
@ -127,13 +131,7 @@ public class AgriStatusManager {
|
||||||
}
|
}
|
||||||
// 无论设备是否在线 只要离线就推送设备状态
|
// 无论设备是否在线 只要离线就推送设备状态
|
||||||
if (!imeiMap.get("imeiOnline")) {
|
if (!imeiMap.get("imeiOnline")) {
|
||||||
// todo 设备离线推送 发消息提醒
|
agriService.sendAlarmMessage("设备离线", imei, dateNow);
|
||||||
Map<String, Object> alarmMsg = new HashMap<>();
|
|
||||||
alarmMsg.put("online", "设备离线");
|
|
||||||
alarmMsg.put("time", dateNow);
|
|
||||||
alarmMsg.put("imei", imei);
|
|
||||||
String alarmMessage = objectMapper.writeValueAsString(alarmMsg);
|
|
||||||
mqttMessageSender.publish("frontend/" + imei + "/alarm", alarmMessage);
|
|
||||||
}
|
}
|
||||||
successCount++;
|
successCount++;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.agri.quartz.task;
|
package com.agri.quartz.task;
|
||||||
|
|
||||||
import com.agri.framework.manager.AgriStatusManager;
|
import com.agri.framework.manager.AgriStatusManager;
|
||||||
|
import com.agri.system.domain.SysAgriInfo;
|
||||||
|
import com.agri.system.service.AgriService;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.agri.framework.config.MqttConfig;
|
import com.agri.framework.config.MqttConfig;
|
||||||
import com.agri.system.service.ISysAgriInfoService;
|
import com.agri.system.service.ISysAgriInfoService;
|
||||||
|
|
@ -47,6 +49,8 @@ public class AgriStatusTask {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AgriStatusManager agriStatusManager;
|
private AgriStatusManager agriStatusManager;
|
||||||
|
@Autowired
|
||||||
|
private AgriService agriService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时任务:每10秒执行一次
|
* 定时任务:每10秒执行一次
|
||||||
|
|
@ -72,14 +76,21 @@ public class AgriStatusTask {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// 查询大棚列表所有在线设备
|
// 查询大棚列表所有在线设备
|
||||||
List<String> imeiList = agriInfoService.queryImeiByUserId(null);
|
List<SysAgriInfo> agriInfos = agriInfoService.findAgriByUser(new SysAgriInfo());
|
||||||
|
List<String> imeiList = agriService.queryAllGreenhouseImei(agriInfos);
|
||||||
if (imeiList.isEmpty()) {
|
if (imeiList.isEmpty()) {
|
||||||
log.info("大棚表无数据,结束推送");
|
log.info("大棚表无数据,结束推送");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Map<String, Map<String, Boolean>> statusMap
|
||||||
|
= agriStatusManager.batchCheckDeviceOnline(imeiList);
|
||||||
// 3. 批量查询设备在线状态(Redis Pipeline,一次网络往返)
|
// 3. 批量查询设备在线状态(Redis Pipeline,一次网络往返)
|
||||||
agriStatusManager.asyncBatchPushMqtt(agriStatusManager.batchCheckDeviceOnline(imeiList));
|
agriStatusManager.asyncBatchPushMqtt(statusMap);
|
||||||
|
// 4. 保存离线设备
|
||||||
|
List<SysAgriInfo> offlineDevices = findOfflineDevices(agriInfos, statusMap);
|
||||||
|
// 5. 保存消息中心
|
||||||
|
agriService.saveMessage(offlineDevices,"怀疑设备离线");
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("设备在线状态推送任务异常", e);
|
log.error("设备在线状态推送任务异常", e);
|
||||||
// 可选:异常告警(如企业微信/钉钉)
|
// 可选:异常告警(如企业微信/钉钉)
|
||||||
|
|
@ -89,7 +100,22 @@ public class AgriStatusTask {
|
||||||
stringRedisTemplate.delete(LOCK_KEY);
|
stringRedisTemplate.delete(LOCK_KEY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 查找离线设备
|
||||||
|
private List<SysAgriInfo> findOfflineDevices(List<SysAgriInfo> agriInfos,
|
||||||
|
Map<String, Map<String, Boolean>> statusMap) {
|
||||||
|
if (statusMap.isEmpty()) {
|
||||||
|
log.info("不存在任何imei");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
List<SysAgriInfo> offlineDevices = new ArrayList<>();
|
||||||
|
for (SysAgriInfo agriInfo : agriInfos) {
|
||||||
|
if (!statusMap.containsKey(agriInfo.getImei())) {
|
||||||
|
offlineDevices.add(agriInfo);
|
||||||
|
log.info("设备{} 不存在设备状态", agriInfo.getImei());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offlineDevices;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 企业级Lua方案:基于大棚表IMEI列表,批量查sub:{imei}是否存在
|
* 企业级Lua方案:基于大棚表IMEI列表,批量查sub:{imei}是否存在
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package com.agri.quartz.task;
|
package com.agri.quartz.task;
|
||||||
|
|
||||||
import com.agri.framework.config.MqttConfig;
|
import com.agri.system.service.AgriService;
|
||||||
|
import com.agri.system.domain.SysAgriInfo;
|
||||||
import com.agri.system.service.ISysAgriInfoService;
|
import com.agri.system.service.ISysAgriInfoService;
|
||||||
import com.agri.system.service.ISysDtuDataService;
|
import com.agri.system.service.ISysDtuDataService;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
@ -15,11 +15,9 @@ import org.springframework.stereotype.Component;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class AgriTempTask {
|
public class AgriTempTask {
|
||||||
|
|
@ -39,31 +37,31 @@ public class AgriTempTask {
|
||||||
@Value("${spring.mqtt.dtu-ctl-lock-ttl:60}")
|
@Value("${spring.mqtt.dtu-ctl-lock-ttl:60}")
|
||||||
private int lockTtl;
|
private int lockTtl;
|
||||||
|
|
||||||
@Resource
|
@Autowired
|
||||||
private MqttConfig.MqttMessageSender mqttMessageSender;
|
private AgriService agriService;
|
||||||
|
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
|
|
||||||
public void checkTempStatus() {
|
public void checkTempStatus() {
|
||||||
if (!acquireLock()) {
|
if (!acquireLock()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
List<String> imeiList = queryAllGreenhouseImei();
|
List<SysAgriInfo> agriInfos = agriInfoService.findAgriByUser(new SysAgriInfo());
|
||||||
|
List<String> imeiList = agriService.queryAllGreenhouseImei(agriInfos);
|
||||||
if (CollectionUtils.isEmpty(imeiList)) {
|
if (CollectionUtils.isEmpty(imeiList)) {
|
||||||
log.info("大棚表无数据,结束推送");
|
log.info("大棚表无数据,结束推送");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String, Object> latestDataMap = queryLatestDtuData(imeiList);
|
Map<String, Object> latestDataMap = queryLatestDtuData(imeiList);
|
||||||
List<String> offlineDeviceList = findOfflineDevices(imeiList, latestDataMap);
|
List<SysAgriInfo> offlineDevices = findOfflineDevices(agriInfos, latestDataMap);
|
||||||
pushOfflineAlarm(offlineDeviceList);
|
pushOfflineAlarm(offlineDevices);
|
||||||
|
agriService.saveMessage(offlineDevices,"怀疑温度离线");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("设备在线状态推送任务异常", e);
|
log.error("设备在线状态推送任务异常", e);
|
||||||
} finally {
|
} finally {
|
||||||
releaseLock();
|
releaseLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 获取分布式锁
|
||||||
private boolean acquireLock() {
|
private boolean acquireLock() {
|
||||||
Boolean lockSuccess = stringRedisTemplate.opsForValue()
|
Boolean lockSuccess = stringRedisTemplate.opsForValue()
|
||||||
.setIfAbsent(LOCK_KEY, "agriTempTask", lockTtl, TimeUnit.SECONDS);
|
.setIfAbsent(LOCK_KEY, "agriTempTask", lockTtl, TimeUnit.SECONDS);
|
||||||
|
|
@ -78,14 +76,13 @@ public class AgriTempTask {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 释放分布式锁
|
||||||
private void releaseLock() {
|
private void releaseLock() {
|
||||||
stringRedisTemplate.delete(LOCK_KEY);
|
stringRedisTemplate.delete(LOCK_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> queryAllGreenhouseImei() {
|
|
||||||
return agriInfoService.queryImeiByUserId(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 查询所有大棚的最新温湿度数据
|
||||||
private Map<String, Object> queryLatestDtuData(List<String> imeiList) {
|
private Map<String, Object> queryLatestDtuData(List<String> imeiList) {
|
||||||
List<Map<String, Object>> dtuDataList = dtuDataService.getLastDtuDataByImeiList(imeiList);
|
List<Map<String, Object>> dtuDataList = dtuDataService.getLastDtuDataByImeiList(imeiList);
|
||||||
Map<String, Object> dataMap = new HashMap<>();
|
Map<String, Object> dataMap = new HashMap<>();
|
||||||
|
|
@ -101,37 +98,33 @@ public class AgriTempTask {
|
||||||
return dataMap;
|
return dataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> findOfflineDevices(List<String> imeiList, Map<String, Object> latestDataMap) {
|
// 查找离线设备
|
||||||
List<String> offlineList = new ArrayList<>();
|
private List<SysAgriInfo> findOfflineDevices(List<SysAgriInfo> agriInfos,
|
||||||
for (String imei : imeiList) {
|
Map<String, Object> latestDataMap) {
|
||||||
if (!latestDataMap.containsKey(imei)) {
|
List<SysAgriInfo> offlineList = new ArrayList<>();
|
||||||
offlineList.add(imei);
|
for (SysAgriInfo agriInfo : agriInfos) {
|
||||||
log.info("设备{} 不存在温湿度数据", imei);
|
if (!latestDataMap.containsKey(agriInfo.getImei())) {
|
||||||
|
offlineList.add(agriInfo);
|
||||||
|
log.info("设备{} 不存在温湿度数据", agriInfo.getImei());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return offlineList;
|
return offlineList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushOfflineAlarm(List<String> offlineDeviceList) {
|
// 推送离线告警
|
||||||
if (CollectionUtils.isEmpty(offlineDeviceList)) {
|
private void pushOfflineAlarm(List<SysAgriInfo> offlineList) {
|
||||||
|
if (CollectionUtils.isEmpty(offlineList)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||||
for (String imei : offlineDeviceList) {
|
for (SysAgriInfo agriInfo : offlineList) {
|
||||||
sendAlarmMessage(imei, timeStr);
|
try {
|
||||||
|
agriService.sendAlarmMessage("温度离线",agriInfo.getImei(), timeStr);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送设备离线告警失败, imei={}", agriInfo.getImei(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendAlarmMessage(String imei, String timeStr) {
|
|
||||||
try {
|
|
||||||
Map<String, Object> alarmMsg = new HashMap<>();
|
|
||||||
alarmMsg.put("online", "温度离线");
|
|
||||||
alarmMsg.put("time", timeStr);
|
|
||||||
alarmMsg.put("imei", imei);
|
|
||||||
String alarmMessage = objectMapper.writeValueAsString(alarmMsg);
|
|
||||||
mqttMessageSender.publish("frontend/" + imei + "/alarm", alarmMessage);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("发送设备离线告警失败, imei={}", imei, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.Date;
|
||||||
* @author agri
|
* @author agri
|
||||||
* @date 2026-01-08
|
* @date 2026-01-08
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
@TableName("sys_agri_info")
|
@TableName("sys_agri_info")
|
||||||
public class SysAgriInfo extends BaseEntity
|
public class SysAgriInfo extends BaseEntity
|
||||||
{
|
{
|
||||||
|
|
@ -96,115 +97,19 @@ public class SysAgriInfo extends BaseEntity
|
||||||
*/
|
*/
|
||||||
private Integer blindNum;
|
private Integer blindNum;
|
||||||
|
|
||||||
public Long getId() {
|
/** 温度上限(℃) */
|
||||||
return id;
|
@Excel(name = "温度上限(℃)")
|
||||||
}
|
private BigDecimal tempUp;
|
||||||
|
|
||||||
public void setId(Long id) {
|
/** 温度下限(℃) */
|
||||||
this.id = id;
|
@Excel(name = "温度下限(℃)")
|
||||||
}
|
private BigDecimal tempLow;
|
||||||
|
|
||||||
public String getImei() {
|
/** 湿度上限(%RH) */
|
||||||
return imei;
|
@Excel(name = "湿度上限(%RH)")
|
||||||
}
|
private BigDecimal humiUp;
|
||||||
|
|
||||||
public void setImei(String imei) {
|
/** 湿度下限(%RH) */
|
||||||
this.imei = imei;
|
@Excel(name = "湿度下限(%RH)")
|
||||||
}
|
private BigDecimal humiLow;
|
||||||
|
|
||||||
public String getAgriName() {
|
|
||||||
return agriName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAgriName(String agriName) {
|
|
||||||
this.agriName = agriName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getWorkMode() {
|
|
||||||
return workMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWorkMode(Integer workMode) {
|
|
||||||
this.workMode = workMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getUserId() {
|
|
||||||
return userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserId(Long userId) {
|
|
||||||
this.userId = userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getAlarmStatus() {
|
|
||||||
return alarmStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlarmStatus(Integer alarmStatus) {
|
|
||||||
this.alarmStatus = alarmStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getDeviceStatus() {
|
|
||||||
return deviceStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeviceStatus(Integer deviceStatus) {
|
|
||||||
this.deviceStatus = deviceStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getInstallTime() {
|
|
||||||
return installTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInstallTime(Date installTime) {
|
|
||||||
this.installTime = installTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLocation() {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLocation(String location) {
|
|
||||||
this.location = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getIsDeleted() {
|
|
||||||
return isDeleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIsDeleted(Integer isDeleted) {
|
|
||||||
this.isDeleted = isDeleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getSourceCode() {
|
|
||||||
return sourceCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSourceCode(Integer sourceCode) {
|
|
||||||
this.sourceCode = sourceCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getQuiltNum() {
|
|
||||||
return quiltNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuiltNum(Integer quiltNum) {
|
|
||||||
this.quiltNum = quiltNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getFilmNum() {
|
|
||||||
return filmNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFilmNum(Integer filmNum) {
|
|
||||||
this.filmNum = filmNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getBlindNum() {
|
|
||||||
return blindNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBlindNum(Integer blindNum) {
|
|
||||||
this.blindNum = blindNum;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.agri.system.domain;
|
package com.agri.system.domain;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
|
@ -14,6 +15,7 @@ import com.agri.common.core.domain.BaseEntity;
|
||||||
* @author lld
|
* @author lld
|
||||||
* @date 2026-03-26
|
* @date 2026-03-26
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
@TableName("sys_message")
|
@TableName("sys_message")
|
||||||
public class SysMessage extends BaseEntity
|
public class SysMessage extends BaseEntity
|
||||||
{
|
{
|
||||||
|
|
@ -25,14 +27,14 @@ public class SysMessage extends BaseEntity
|
||||||
|
|
||||||
/** 接收人:all=全体用户,其他=用户ID */
|
/** 接收人:all=全体用户,其他=用户ID */
|
||||||
@Excel(name = "接收人:all=全体用户,其他=用户ID")
|
@Excel(name = "接收人:all=全体用户,其他=用户ID")
|
||||||
private String receiver;
|
private Long receiver;
|
||||||
|
|
||||||
/** 消息标题 */
|
/** 消息标题 */
|
||||||
@Excel(name = "消息标题")
|
@Excel(name = "消息标题")
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
/** 消息类型:activity-活动 notice-通知 interact-互动消息 */
|
/** 消息类型:activity-活动 notice-通知 interact-互动消息 */
|
||||||
@Excel(name = "消息类型:activity-活动 notice-通知 interact-互动消息")
|
@Excel(name = "消息类型:status-状态,notice-通知,event-活动")
|
||||||
private String msgType;
|
private String msgType;
|
||||||
|
|
||||||
/** 阅读状态:0-未读 1-已读 */
|
/** 阅读状态:0-未读 1-已读 */
|
||||||
|
|
@ -55,113 +57,4 @@ public class SysMessage extends BaseEntity
|
||||||
@Excel(name = "跳转链接地址")
|
@Excel(name = "跳转链接地址")
|
||||||
private String linkUrl;
|
private String linkUrl;
|
||||||
|
|
||||||
public void setId(Long id)
|
|
||||||
{
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId()
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReceiver(String receiver)
|
|
||||||
{
|
|
||||||
this.receiver = receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getReceiver()
|
|
||||||
{
|
|
||||||
return receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title)
|
|
||||||
{
|
|
||||||
this.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle()
|
|
||||||
{
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMsgType(String msgType)
|
|
||||||
{
|
|
||||||
this.msgType = msgType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMsgType()
|
|
||||||
{
|
|
||||||
return msgType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReadStatus(Long readStatus)
|
|
||||||
{
|
|
||||||
this.readStatus = readStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getReadStatus()
|
|
||||||
{
|
|
||||||
return readStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContent(String content)
|
|
||||||
{
|
|
||||||
this.content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContent()
|
|
||||||
{
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRichContent(String richContent)
|
|
||||||
{
|
|
||||||
this.richContent = richContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRichContent()
|
|
||||||
{
|
|
||||||
return richContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImgUrl(String imgUrl)
|
|
||||||
{
|
|
||||||
this.imgUrl = imgUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getImgUrl()
|
|
||||||
{
|
|
||||||
return imgUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLinkUrl(String linkUrl)
|
|
||||||
{
|
|
||||||
this.linkUrl = linkUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLinkUrl()
|
|
||||||
{
|
|
||||||
return linkUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
|
||||||
.append("id", getId())
|
|
||||||
.append("receiver", getReceiver())
|
|
||||||
.append("title", getTitle())
|
|
||||||
.append("msgType", getMsgType())
|
|
||||||
.append("readStatus", getReadStatus())
|
|
||||||
.append("content", getContent())
|
|
||||||
.append("richContent", getRichContent())
|
|
||||||
.append("imgUrl", getImgUrl())
|
|
||||||
.append("linkUrl", getLinkUrl())
|
|
||||||
.append("createBy", getCreateBy())
|
|
||||||
.append("createTime", getCreateTime())
|
|
||||||
.append("updateBy", getUpdateBy())
|
|
||||||
.append("updateTime", getUpdateTime())
|
|
||||||
.append("remark", getRemark())
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,12 @@ import com.baomidou.mybatisplus.annotation.Version;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import lombok.Data;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 大棚信息(用户-设备关联)对象 sys_user_agri
|
* 大棚信息(用户-设备关联)对象 sys_user_agri
|
||||||
|
|
@ -22,6 +24,7 @@ import java.util.Date;
|
||||||
* @author lld
|
* @author lld
|
||||||
* @date 2026-01-25
|
* @date 2026-01-25
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
@TableName("sys_user_agri")
|
@TableName("sys_user_agri")
|
||||||
public class SysUserAgri extends BaseEntity
|
public class SysUserAgri extends BaseEntity
|
||||||
{
|
{
|
||||||
|
|
@ -81,146 +84,10 @@ public class SysUserAgri extends BaseEntity
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Boolean disabled;
|
private Boolean disabled;
|
||||||
|
|
||||||
public Boolean getDisabled() {
|
@TableField(exist = false)
|
||||||
return disabled;
|
private List<Long> idList;
|
||||||
}
|
|
||||||
|
|
||||||
public void setDisabled(Boolean disabled) {
|
public SysUserAgri(List<Long> idList) {
|
||||||
this.disabled = disabled;
|
this.idList = idList;
|
||||||
}
|
|
||||||
|
|
||||||
public String getImei() {
|
|
||||||
return imei;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImei(String imei) {
|
|
||||||
this.imei = imei;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SysUser getSysUser() {
|
|
||||||
return sysUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSysUser(SysUser sysUser) {
|
|
||||||
this.sysUser = sysUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long id)
|
|
||||||
{
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId()
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAgriId(String agriId)
|
|
||||||
{
|
|
||||||
this.agriId = agriId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAgriId()
|
|
||||||
{
|
|
||||||
return agriId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserId(Long userId)
|
|
||||||
{
|
|
||||||
this.userId = userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getUserId()
|
|
||||||
{
|
|
||||||
return userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRole(Integer role)
|
|
||||||
{
|
|
||||||
this.role = role;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getRole()
|
|
||||||
{
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setStatus(Integer status)
|
|
||||||
{
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getStatus()
|
|
||||||
{
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInviteBy(Long inviteBy)
|
|
||||||
{
|
|
||||||
this.inviteBy = inviteBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getInviteBy()
|
|
||||||
{
|
|
||||||
return inviteBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInviteTime(Date inviteTime)
|
|
||||||
{
|
|
||||||
this.inviteTime = inviteTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getInviteTime()
|
|
||||||
{
|
|
||||||
return inviteTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAcceptTime(Date acceptTime)
|
|
||||||
{
|
|
||||||
this.acceptTime = acceptTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getAcceptTime()
|
|
||||||
{
|
|
||||||
return acceptTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getPermMask() {
|
|
||||||
return permMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPermMask(Integer permMask) {
|
|
||||||
this.permMask = permMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getVersion() {
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVersion(Integer version) {
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
|
||||||
.append("id", getId())
|
|
||||||
.append("agriId", getAgriId())
|
|
||||||
.append("sysUser", getSysUser())
|
|
||||||
.append("userId", getUserId())
|
|
||||||
.append("role", getRole())
|
|
||||||
.append("permMask", getPermMask())
|
|
||||||
.append("status", getStatus())
|
|
||||||
.append("inviteBy", getInviteBy())
|
|
||||||
.append("inviteTime", getInviteTime())
|
|
||||||
.append("acceptTime", getAcceptTime())
|
|
||||||
.append("remark", getRemark())
|
|
||||||
.append("createTime", getCreateTime())
|
|
||||||
.append("createBy", getCreateBy())
|
|
||||||
.append("updateTime", getUpdateTime())
|
|
||||||
.append("updateBy", getUpdateBy())
|
|
||||||
.append("version", getVersion())
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.agri.system.service;
|
||||||
|
|
||||||
|
import com.agri.system.domain.SysAgriInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface AgriService {
|
||||||
|
|
||||||
|
void sendAlarmMessage(String msg, String imei, String timeStr) throws Exception;
|
||||||
|
|
||||||
|
void saveMessage(List<SysAgriInfo> offlineList,String msg);
|
||||||
|
|
||||||
|
List<String> queryAllGreenhouseImei(List<SysAgriInfo> agriInfos);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.agri.system.service.impl;
|
||||||
|
|
||||||
|
import com.agri.framework.config.MqttConfig;
|
||||||
|
import com.agri.system.service.AgriService;
|
||||||
|
import com.agri.system.domain.SysAgriInfo;
|
||||||
|
import com.agri.system.domain.SysMessage;
|
||||||
|
import com.agri.system.domain.SysUserAgri;
|
||||||
|
import com.agri.system.service.ISysMessageService;
|
||||||
|
import com.agri.system.service.ISysUserAgriService;
|
||||||
|
import com.agri.system.util.UrlEncodeUtil;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AgriServiceImpl implements AgriService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MqttConfig.MqttMessageSender mqttMessageSender;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISysUserAgriService userAgriService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISysMessageService messageService;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
// 发送离线告警消息
|
||||||
|
@Override
|
||||||
|
public void sendAlarmMessage(String msg, String imei, String timeStr) throws Exception {
|
||||||
|
|
||||||
|
Map<String, Object> alarmMsg = new HashMap<>();
|
||||||
|
alarmMsg.put("online", msg);
|
||||||
|
alarmMsg.put("time", timeStr);
|
||||||
|
alarmMsg.put("imei", imei);
|
||||||
|
String alarmMessage = objectMapper.writeValueAsString(alarmMsg);
|
||||||
|
mqttMessageSender.publish("frontend/" + imei + "/alarm", alarmMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存离线设备消息
|
||||||
|
@Override
|
||||||
|
public void saveMessage(List<SysAgriInfo> offlineList,String msg) {
|
||||||
|
// 离线是否为空
|
||||||
|
if (CollectionUtils.isEmpty(offlineList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Long> idList
|
||||||
|
= offlineList.stream().map(SysAgriInfo::getId).collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<SysUserAgri> agriUser
|
||||||
|
= userAgriService.findAgriUser(new SysUserAgri(idList));
|
||||||
|
|
||||||
|
List<SysMessage> msgList = new ArrayList<>();
|
||||||
|
for (SysAgriInfo agriInfo : offlineList) {
|
||||||
|
SysMessage message = new SysMessage();
|
||||||
|
message.setTitle(msg);
|
||||||
|
message.setMsgType("status");
|
||||||
|
message.setReadStatus(0L);
|
||||||
|
message.setContent("大棚【" + agriInfo.getAgriName() + "】" + msg + "!请检查设备状态。");
|
||||||
|
message.setImgUrl("");
|
||||||
|
message.setLinkUrl(UrlEncodeUtil.buildControlPageUrl(agriInfo, "/pages/home/control/index?agriInfo="));
|
||||||
|
for (SysUserAgri userAgri : agriUser){
|
||||||
|
message.setReceiver(userAgri.getUserId());
|
||||||
|
msgList.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(msgList)) {
|
||||||
|
messageService.saveBatch(msgList);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询所有大棚的imei号
|
||||||
|
@Override
|
||||||
|
public List<String> queryAllGreenhouseImei(List<SysAgriInfo> agriInfos) {
|
||||||
|
|
||||||
|
if (CollectionUtils.isEmpty(agriInfos)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return agriInfos.stream().map(SysAgriInfo::getImei).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.agri.system.util;
|
||||||
|
|
||||||
|
import com.agri.system.domain.SysAgriInfo;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应前端 JS:
|
||||||
|
* JSON.stringify() + encodeURIComponent()
|
||||||
|
*/
|
||||||
|
public class UrlEncodeUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【和你 JS 完全等价】生成跳转URL
|
||||||
|
*/
|
||||||
|
public static String buildControlPageUrl(SysAgriInfo agriInfo, String linkUrl) {
|
||||||
|
try {
|
||||||
|
// 1. 构建和JS一模一样的JSON对象
|
||||||
|
JSONObject agriObj = new JSONObject();
|
||||||
|
agriObj.put("imei", agriInfo.getImei());
|
||||||
|
agriObj.put("agriName", agriInfo.getAgriName());
|
||||||
|
agriObj.put("agriId", agriInfo.getId());
|
||||||
|
agriObj.put("workMode", agriInfo.getWorkMode());
|
||||||
|
|
||||||
|
// 2. JSON.stringify
|
||||||
|
String agriJson = JSON.toJSONString(agriObj);
|
||||||
|
|
||||||
|
// 3. encodeURIComponent
|
||||||
|
String agriInfoEncoded = URLEncoder.encode(agriJson, "UTF-8");
|
||||||
|
|
||||||
|
// 4. 最终URL(和你JS跳转一模一样)
|
||||||
|
return linkUrl + agriInfoEncoded;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通用版:任意对象转 encodeURIComponent 字符串
|
||||||
|
public static String encodeUriComponent(Object obj) {
|
||||||
|
try {
|
||||||
|
String json = JSON.toJSONString(obj);
|
||||||
|
return URLEncoder.encode(json, "UTF-8");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -189,6 +189,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="acceptTime != null "> and user_agri.accept_time = #{acceptTime}</if>
|
<if test="acceptTime != null "> and user_agri.accept_time = #{acceptTime}</if>
|
||||||
<if test="version != null "> and user_agri.version = #{version}</if>
|
<if test="version != null "> and user_agri.version = #{version}</if>
|
||||||
<if test="sysUser != null ">
|
<if test="sysUser != null ">
|
||||||
|
<if test="sysUser.idList != null and sysUser.idList.size() > 0">
|
||||||
|
`u`.user_id IN
|
||||||
|
<foreach collection="idList" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
|
||||||
<if test="sysUser.userId != null and sysUser.userId != 0">
|
<if test="sysUser.userId != null and sysUser.userId != 0">
|
||||||
AND `u`.user_id = #{sysUser.userId}
|
AND `u`.user_id = #{sysUser.userId}
|
||||||
</if>
|
</if>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue