This commit is contained in:
2025-11-19 10:50:30 +08:00
commit 36cad670e8
52 changed files with 3816 additions and 0 deletions

View File

@ -0,0 +1,4 @@
2025-10-01 20:48:47.371 [http-nio-7999-exec-1] INFO c.x.m.c.ClimateThresholdController - getThresholdById id: 1, threshold: Optional.empty
2025-10-01 20:50:35.861 [http-nio-7999-exec-5] INFO c.x.m.c.ClimateThresholdController - getThresholdById id: 1, threshold: Optional[com.xzzn.movecheck.repo.ClimateThreshold@9a18622]
2025-10-01 20:56:13.467 [http-nio-7999-exec-3] INFO c.x.m.c.ClimateThresholdController - getThresholdById id: 1, threshold: Optional[com.xzzn.movecheck.repo.ClimateThreshold@54616eab]
2025-10-01 20:59:12.931 [http-nio-7999-exec-8] INFO c.x.m.c.ClimateThresholdController - getThresholdById id: 1, threshold: Optional[com.xzzn.movecheck.repo.ClimateThreshold@3ed28f8d]

68
pom.xml Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xzzn</groupId>
<artifactId>movecheck</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.15</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<!-- HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,15 @@
package com.xzzn.movecheck;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MovecheckApplication {
public static void main(String[] args) {
SpringApplication.run(MovecheckApplication.class, args
);
}
}

View File

@ -0,0 +1,25 @@
package com.xzzn.movecheck.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(false) // 设置为false
.maxAge(3600);
}
};
}
}

View File

@ -0,0 +1,26 @@
// config/HttpClientConfig.java
package com.xzzn.movecheck.config;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HttpClientConfig {
@Bean
public CloseableHttpClient closeableHttpClient() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(30000) // 连接超时30秒
.setSocketTimeout(30000) // socket超时30秒
.setConnectionRequestTimeout(30000) // 请求超时30秒
.build();
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.build();
}
}

View File

@ -0,0 +1,81 @@
package com.xzzn.movecheck.config;
import com.xzzn.movecheck.service.MqttMessageHandler;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PreDestroy;
@Configuration
public class MqttAutoConfiguration {
@Value("${mqtt.broker:tcp://localhost:1883}")
private String broker;
@Value("${mqtt.clientId:springboot-mqtt-client}")
private String clientId;
@Value("${mqtt.topics:HDYDCJ_01_DEVICE,HDYDCJ_01_UP}")
private String[] topics;
@Value("${mqtt.qos:1,1}")
private int[] qos;
private MqttClient mqttClient;
@Bean
public MqttConnectOptions mqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(new String[]{broker});
options.setCleanSession(true);
options.setConnectionTimeout(10);
options.setKeepAliveInterval(20);
options.setAutomaticReconnect(true);
options.setMaxReconnectDelay(5000);
options.setUserName("dmbroker");
options.setPassword("qwer1234".toCharArray());
return options;
}
@Bean
public MqttClient mqttClient(MqttConnectOptions options, MqttMessageHandler messageHandler)
throws MqttException {
mqttClient = new MqttClient(broker, clientId, new MemoryPersistence());
// 设置回调
mqttClient.setCallback(messageHandler);
// 连接
mqttClient.connect(options);
System.out.println("MQTT客户端连接成功");
// 订阅主题
if (topics != null && topics.length > 0) {
mqttClient.subscribe(topics, qos);
for (String topic : topics) {
System.out.println("已订阅主题: " + topic);
}
}
return mqttClient;
}
@PreDestroy
public void destroy() {
if (mqttClient != null && mqttClient.isConnected()) {
try {
mqttClient.disconnect();
mqttClient.close();
System.out.println("MQTT客户端已断开连接");
} catch (MqttException e) {
System.err.println("关闭MQTT客户端时出错: " + e.getMessage());
}
}
}
}

View File

@ -0,0 +1,18 @@
// UserConfig.java
package com.xzzn.movecheck.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
public class UserConfig {
private String username = "admin";
private String password = "123456";
// Getter和Setter
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}

View File

@ -0,0 +1,149 @@
package com.xzzn.movecheck.repo;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "alert_data")
public class AlertData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "content", nullable = false, length = 1000)
private String content; // 告警内容
@Column(name = "category", nullable = false)
private String category; // 告警种类
@Column(name = "alert_time", nullable = false)
private LocalDateTime alertTime; // 告警时间
@Column(name = "level", nullable = false)
private String level; // 告警级别
@Column(name = "action")
private String action; // 处置措施
@Column(name = "action_time")
private LocalDateTime actionTime; // 处置时间
@Column(name = "create_time")
private LocalDateTime createTime; // 创建时间
@Column(name = "device_id")
private String deviceId; // 设备ID可选
// 构造函数
public AlertData() {
this.createTime = LocalDateTime.now();
}
public AlertData(String content, String category, LocalDateTime alertTime, String level) {
this();
this.content = content;
this.category = category;
this.alertTime = alertTime;
this.level = level;
}
public AlertData(String content, String category, LocalDateTime alertTime, String level,
String action, LocalDateTime actionTime, String deviceId) {
this();
this.content = content;
this.category = category;
this.alertTime = alertTime;
this.level = level;
this.action = action;
this.actionTime = actionTime;
this.deviceId = deviceId;
}
// Getter 和 Setter
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public LocalDateTime getAlertTime() {
return alertTime;
}
public void setAlertTime(LocalDateTime alertTime) {
this.alertTime = alertTime;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public LocalDateTime getActionTime() {
return actionTime;
}
public void setActionTime(LocalDateTime actionTime) {
this.actionTime = actionTime;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
@Override
public String toString() {
return "AlertData{" +
"id=" + id +
", content='" + content + '\'' +
", category='" + category + '\'' +
", alertTime=" + alertTime +
", level='" + level + '\'' +
", action='" + action + '\'' +
", actionTime=" + actionTime +
", deviceId='" + deviceId + '\'' +
", createTime=" + createTime +
'}';
}
}

View File

@ -0,0 +1,22 @@
package com.xzzn.movecheck.repo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@Repository
public interface AlertDataRepository extends JpaRepository<AlertData, Long> {
// 按创建时间倒序获取最新数据
List<AlertData> findTop10ByOrderByCreateTimeDesc();
// 分页查询
Page<AlertData> findAllByOrderByCreateTimeDesc(Pageable pageable);
// AlertDataRepository.java
Page<AlertData> findByCreateTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);
Page<AlertData> findByCreateTimeAfter(LocalDateTime startTime, Pageable pageable);
Page<AlertData> findByCreateTimeBefore(LocalDateTime endTime, Pageable pageable);}

View File

@ -0,0 +1,90 @@
// ClimateThreshold.java
package com.xzzn.movecheck.repo;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "climate_threshold") // 确保表名正确
public class ClimateThreshold {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "setting_name", unique = true, nullable = false)
private String settingName;
@Column(name = "min_temperature", precision = 5, scale = 2, nullable = false)
private BigDecimal minTemperature;
@Column(name = "max_temperature", precision = 5, scale = 2, nullable = false)
private BigDecimal maxTemperature;
@Column(name = "min_humidity", precision = 5, scale = 2, nullable = false)
private BigDecimal minHumidity;
@Column(name = "max_humidity", precision = 5, scale = 2, nullable = false)
private BigDecimal maxHumidity;
@Column(length = 500)
private String description;
@Column(name = "created_time")
private LocalDateTime createdTime;
@Column(name = "updated_time")
private LocalDateTime updatedTime;
// 构造方法
public ClimateThreshold() {
this.createdTime = LocalDateTime.now();
this.updatedTime = LocalDateTime.now();
}
public ClimateThreshold(String settingName, BigDecimal minTemperature,
BigDecimal maxTemperature, BigDecimal minHumidity,
BigDecimal maxHumidity, String description) {
this();
this.settingName = settingName;
this.minTemperature = minTemperature;
this.maxTemperature = maxTemperature;
this.minHumidity = minHumidity;
this.maxHumidity = maxHumidity;
this.description = description;
}
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getSettingName() { return settingName; }
public void setSettingName(String settingName) { this.settingName = settingName; }
public BigDecimal getMinTemperature() { return minTemperature; }
public void setMinTemperature(BigDecimal minTemperature) { this.minTemperature = minTemperature; }
public BigDecimal getMaxTemperature() { return maxTemperature; }
public void setMaxTemperature(BigDecimal maxTemperature) { this.maxTemperature = maxTemperature; }
public BigDecimal getMinHumidity() { return minHumidity; }
public void setMinHumidity(BigDecimal minHumidity) { this.minHumidity = minHumidity; }
public BigDecimal getMaxHumidity() { return maxHumidity; }
public void setMaxHumidity(BigDecimal maxHumidity) { this.maxHumidity = maxHumidity; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
public LocalDateTime getUpdatedTime() { return updatedTime; }
public void setUpdatedTime(LocalDateTime updatedTime) { this.updatedTime = updatedTime; }
@PreUpdate
public void preUpdate() {
this.updatedTime = LocalDateTime.now();
}
}

View File

@ -0,0 +1,27 @@
// ClimateThresholdRepository.java
package com.xzzn.movecheck.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@Repository
public interface ClimateThresholdRepository extends JpaRepository<ClimateThreshold, Long> {
// 根据设置名称查找
Optional<ClimateThreshold> findBySettingName(String settingName);
// 检查设置名称是否存在
boolean existsBySettingName(String settingName);
// 根据温度范围查找
List<ClimateThreshold> findByMinTemperatureLessThanEqualAndMaxTemperatureGreaterThanEqual(BigDecimal temperature, BigDecimal temperature2);
// 自定义查询示例
@Query("SELECT ct FROM ClimateThreshold ct WHERE ct.minTemperature <= :temp AND ct.maxTemperature >= :temp")
List<ClimateThreshold> findSuitableThresholdsByTemperature(BigDecimal temp);
}

View File

@ -0,0 +1,46 @@
// EmqxClientInfo.java
package com.xzzn.movecheck.repo;
import com.fasterxml.jackson.annotation.JsonProperty;
public class EmqxClientInfo {
@JsonProperty("clientid")
private String clientId;
private String username;
private Boolean connected;
private Long connectedAt;
private String ipAddress;
private Integer port;
private Integer keepalive;
private Integer protoVer;
private String protoName;
// Getter和Setter
public String getClientId() { return clientId; }
public void setClientId(String clientId) { this.clientId = clientId; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Boolean getConnected() { return connected; }
public void setConnected(Boolean connected) { this.connected = connected; }
public Long getConnectedAt() { return connectedAt; }
public void setConnectedAt(Long connectedAt) { this.connectedAt = connectedAt; }
public String getIpAddress() { return ipAddress; }
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
public Integer getPort() { return port; }
public void setPort(Integer port) { this.port = port; }
public Integer getKeepalive() { return keepalive; }
public void setKeepalive(Integer keepalive) { this.keepalive = keepalive; }
public Integer getProtoVer() { return protoVer; }
public void setProtoVer(Integer protoVer) { this.protoVer = protoVer; }
public String getProtoName() { return protoName; }
public void setProtoName(String protoName) { this.protoName = protoName; }
}

View File

@ -0,0 +1,121 @@
package com.xzzn.movecheck.repo;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "event_data")
public class EventData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "event_type", nullable = false)
private String eventType; // 事件类型
@Column(name = "event_time", nullable = false)
private LocalDateTime eventTime; // 事件时间
@Column(name = "status", nullable = false)
private String status; // 状态
@Column(name = "description")
private String description; // 事件描述
@Column(name = "create_time")
private LocalDateTime createTime; // 创建时间
@Column(name = "device_id")
private String deviceId; // 设备ID可选
// 构造函数
public EventData() {
this.createTime = LocalDateTime.now();
}
public EventData(String eventType, LocalDateTime eventTime, String status) {
this();
this.eventType = eventType;
this.eventTime = eventTime;
this.status = status;
}
public EventData(String eventType, LocalDateTime eventTime, String status, String description, String deviceId) {
this();
this.eventType = eventType;
this.eventTime = eventTime;
this.status = status;
this.description = description;
this.deviceId = deviceId;
}
// Getter 和 Setter
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public LocalDateTime getEventTime() {
return eventTime;
}
public void setEventTime(LocalDateTime eventTime) {
this.eventTime = eventTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
@Override
public String toString() {
return "EventData{" +
"id=" + id +
", eventType='" + eventType + '\'' +
", eventTime=" + eventTime +
", status='" + status + '\'' +
", description='" + description + '\'' +
", deviceId='" + deviceId + '\'' +
", createTime=" + createTime +
'}';
}
}

View File

@ -0,0 +1,25 @@
package com.xzzn.movecheck.repo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@Repository
public interface EventDataRepository extends JpaRepository<EventData, Long> {
// 按创建时间倒序获取最新数据
List<EventData> findTop10ByOrderByCreateTimeDesc();
// 分页查询
Page<EventData> findAllByOrderByCreateTimeDesc(Pageable pageable);
// EventDataRepository.java
Page<EventData> findByCreateTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);
Page<EventData> findByCreateTimeAfter(LocalDateTime startTime, Pageable pageable);
Page<EventData> findByCreateTimeBefore(LocalDateTime endTime, Pageable pageable);
}

View File

@ -0,0 +1,99 @@
// entity/HealthDevice.java
package com.xzzn.movecheck.repo;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "health_device")
public class HealthDevice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "ac", length = 1)
private String ac;
@Column(name = "wsd", length = 1)
private String wsd;
@Column(name = "pm25", length = 1)
private String pm25;
@Column(name = "created_time")
private LocalDateTime createdTime;
@Column(name = "updated_time")
private LocalDateTime updatedTime;
// 构造方法
public HealthDevice() {
}
public HealthDevice(String ac, String wsd, String pm25) {
this.ac = ac;
this.wsd = wsd;
this.pm25 = pm25;
}
// getters and setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAc() {
return ac;
}
public void setAc(String ac) {
this.ac = ac;
}
public String getWsd() {
return wsd;
}
public void setWsd(String wsd) {
this.wsd = wsd;
}
public String getPm25() {
return pm25;
}
public void setPm25(String pm25) {
this.pm25 = pm25;
}
public LocalDateTime getCreatedTime() {
return createdTime;
}
public void setCreatedTime(LocalDateTime createdTime) {
this.createdTime = createdTime;
}
public LocalDateTime getUpdatedTime() {
return updatedTime;
}
public void setUpdatedTime(LocalDateTime updatedTime) {
this.updatedTime = updatedTime;
}
@Override
public String toString() {
return "HealthDevice{" +
"id=" + id +
", ac='" + ac + '\'' +
", wsd='" + wsd + '\'' +
", pm25='" + pm25 + '\'' +
", createdTime=" + createdTime +
", updatedTime=" + updatedTime +
'}';
}
}

View File

@ -0,0 +1,19 @@
// repository/HealthDeviceRepository.java
package com.xzzn.movecheck.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@Repository
public interface HealthDeviceRepository extends JpaRepository<HealthDevice, Integer> {
// 自定义查询:查找最新的记录
@Query(value = "SELECT * FROM health_device ORDER BY created_time DESC LIMIT 1", nativeQuery = true)
List<HealthDevice> findLatestRecord();
}

View File

@ -0,0 +1,79 @@
package com.xzzn.movecheck.repo;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "sensor_data")
public class SensorData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "sd_value")
private Double sd;
@Column(name = "wd_value")
private Double wd;
@Column(name = "pm_value")
private Double pm;
@Column(name = "create_time")
private LocalDateTime createTime;
// 构造函数
public SensorData() {
this.createTime = LocalDateTime.now();
}
public SensorData(Double sd, Double wd, Double pm) {
this();
this.sd = sd;
this.wd = wd;
this.pm = pm;
}
// getter 和 setter 方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Double getSd() {
return sd;
}
public void setSd(Double sd) {
this.sd = sd;
}
public Double getWd() {
return wd;
}
public void setWd(Double wd) {
this.wd = wd;
}
public Double getPm() {
return pm;
}
public void setPm(Double pm) {
this.pm = pm;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
}

View File

@ -0,0 +1,18 @@
package com.xzzn.movecheck.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@Repository
public interface SensorDataRepository extends JpaRepository<SensorData, Long> {
// 查找最新的数据
List<SensorData> findTop10ByOrderByCreateTimeDesc();
// 根据时间范围查询
List<SensorData> findByCreateTimeBetween(LocalDateTime start, LocalDateTime end);
}

View File

@ -0,0 +1,95 @@
package com.xzzn.movecheck.repo;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "temperature_humidity_data")
public class TemperatureHumidityData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "temperature", nullable = false)
private Double temperature; // 温度
@Column(name = "humidity", nullable = false)
private Double humidity; // 湿度
@Column(name = "device_id")
private String deviceId; // 设备ID
@Column(name = "create_time")
private LocalDateTime createTime; // 创建时间
// 构造函数
public TemperatureHumidityData() {
this.createTime = LocalDateTime.now();
}
public TemperatureHumidityData(Double temperature, Double humidity) {
this();
this.temperature = temperature;
this.humidity = humidity;
}
public TemperatureHumidityData(Double temperature, Double humidity, String deviceId) {
this();
this.temperature = temperature;
this.humidity = humidity;
this.deviceId = deviceId;
}
// Getter 和 Setter
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Double getTemperature() {
return temperature;
}
public void setTemperature(Double temperature) {
this.temperature = temperature;
}
public Double getHumidity() {
return humidity;
}
public void setHumidity(Double humidity) {
this.humidity = humidity;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "TemperatureHumidityData{" +
"id=" + id +
", temperature=" + temperature +
", humidity=" + humidity +
", deviceId='" + deviceId + '\'' +
", createTime=" + createTime +
'}';
}
}

View File

@ -0,0 +1,74 @@
package com.xzzn.movecheck.repo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Repository
public interface TemperatureHumidityRepository extends JpaRepository<TemperatureHumidityData, Long> {
/**
* 获取最新的温湿度数据 - 使用原生SQL查询
*/
@Query(value = "SELECT * FROM temperature_humidity_data ORDER BY create_time DESC LIMIT 1", nativeQuery = true)
Optional<TemperatureHumidityData> findLatestData();
/**
* 根据设备ID获取最新数据 - 使用原生SQL查询
*/
@Query(value = "SELECT * FROM temperature_humidity_data WHERE device_id = :deviceId ORDER BY create_time DESC LIMIT 1", nativeQuery = true)
Optional<TemperatureHumidityData> findLatestByDeviceId(@Param("deviceId") String deviceId);
/**
* 获取最新的N条数据 - 使用Spring Data JPA方法名查询
*/
List<TemperatureHumidityData> findTop10ByOrderByCreateTimeDesc();
/**
* 根据设备ID获取最新的N条数据 - 使用Spring Data JPA方法名查询
*/
List<TemperatureHumidityData> findTop10ByDeviceIdOrderByCreateTimeDesc(String deviceId);
/**
* 分页查询所有数据
*/
Page<TemperatureHumidityData> findAllByOrderByCreateTimeDesc(Pageable pageable);
/**
* 根据设备ID分页查询
*/
Page<TemperatureHumidityData> findByDeviceIdOrderByCreateTimeDesc(String deviceId, Pageable pageable);
/**
* 根据时间范围查询
*/
List<TemperatureHumidityData> findByCreateTimeBetweenOrderByCreateTimeDesc(LocalDateTime startTime, LocalDateTime endTime);
/**
* 根据设备ID和时间范围查询
*/
List<TemperatureHumidityData> findByDeviceIdAndCreateTimeBetweenOrderByCreateTimeDesc(String deviceId, LocalDateTime startTime, LocalDateTime endTime);
/**
* 获取所有设备ID列表
*/
@Query("SELECT DISTINCT t.deviceId FROM TemperatureHumidityData t WHERE t.deviceId IS NOT NULL")
List<String> findAllDeviceIds();
/**
* 获取第一条记录(按创建时间升序)
*/
Optional<TemperatureHumidityData> findFirstByOrderByCreateTimeAsc();
/**
* 获取最后一条记录(按创建时间降序)
*/
Optional<TemperatureHumidityData> findFirstByOrderByCreateTimeDesc();
}

View File

@ -0,0 +1,71 @@
// User.java
package com.xzzn.movecheck.repo;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "system_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false, length = 50)
private String username;
@Column(nullable = false, length = 100)
private String password;
@Column(length = 100)
private String nickname;
private Boolean enabled = true;
@Column(name = "created_time")
private LocalDateTime createdTime;
@Column(name = "updated_time")
private LocalDateTime updatedTime;
// 构造方法
public User() {
this.createdTime = LocalDateTime.now();
this.updatedTime = LocalDateTime.now();
}
public User(String username, String password, String nickname) {
this();
this.username = username;
this.password = password;
this.nickname = nickname;
}
// Getter和Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
public Boolean getEnabled() { return enabled; }
public void setEnabled(Boolean enabled) { this.enabled = enabled; }
public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
public LocalDateTime getUpdatedTime() { return updatedTime; }
public void setUpdatedTime(LocalDateTime updatedTime) { this.updatedTime = updatedTime; }
@PreUpdate
public void preUpdate() {
this.updatedTime = LocalDateTime.now();
}
}

View File

@ -0,0 +1,23 @@
// UserRepository.java
package com.xzzn.movecheck.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 根据用户名查找用户
Optional<User> findByUsername(String username);
// 根据用户名和密码查找用户
@Query("SELECT u FROM User u WHERE u.username = :username AND u.password = :password AND u.enabled = true")
Optional<User> findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
// 检查用户名是否存在
boolean existsByUsername(String username);
}

View File

@ -0,0 +1,110 @@
// AuthService.java
package com.xzzn.movecheck.service;
import com.xzzn.movecheck.repo.User;
import com.xzzn.movecheck.repo.UserRepository;
import com.xzzn.movecheck.util.SimpleTokenUtil;
import com.xzzn.movecheck.wsd.LoginRequest;
import com.xzzn.movecheck.wsd.LoginResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Service
public class AuthService {
@Autowired
private UserRepository userRepository;
@Autowired
private SimpleTokenUtil tokenUtil;
// 内存token存储生产环境建议用Redis
private Map<String, String> tokenStore = new HashMap<>();
public LoginResponse login(LoginRequest loginRequest) {
// 从数据库验证用户名密码
Optional<User> userOpt = userRepository.findByUsernameAndPassword(
loginRequest.getUsername(),
loginRequest.getPassword()
);
if (userOpt.isPresent()) {
User user = userOpt.get();
// 检查用户是否被禁用
if (!user.getEnabled()) {
return new LoginResponse(false, "用户已被禁用");
}
// 生成token
String token = tokenUtil.generateToken(user.getUsername());
// 存储token
tokenStore.put(token, user.getUsername());
// 使用正确的构造函数
return new LoginResponse(
true,
"登录成功",
token,
user.getUsername(),
user.getNickname() != null ? user.getNickname() : user.getUsername() // 确保nickname不为null
);
} else {
return new LoginResponse(false, "用户名或密码错误");
}
}
public boolean validateToken(String token) {
if (token == null || !tokenStore.containsKey(token)) {
return false;
}
return tokenUtil.validateToken(token);
}
public String getUsernameFromToken(String token) {
return tokenStore.get(token);
}
// 获取当前登录用户信息
public Optional<User> getCurrentUser(String token) {
String username = getUsernameFromToken(token);
if (username != null) {
return userRepository.findByUsername(username);
}
return Optional.empty();
}
// 登出
public void logout(String token) {
tokenStore.remove(token);
}
// 获取所有用户(管理功能)
public java.util.List<User> getAllUsers() {
return userRepository.findAll();
}
// 创建用户(管理功能)
public User createUser(User user) {
if (userRepository.existsByUsername(user.getUsername())) {
throw new RuntimeException("用户名已存在");
}
return userRepository.save(user);
}
// 更新用户(管理功能)
public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在"));
user.setNickname(userDetails.getNickname());
user.setEnabled(userDetails.getEnabled());
return userRepository.save(user);
}
}

View File

@ -0,0 +1,96 @@
// ClimateThresholdService.java
package com.xzzn.movecheck.service;
import com.xzzn.movecheck.repo.ClimateThreshold;
import com.xzzn.movecheck.repo.ClimateThresholdRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
public class ClimateThresholdService {
@Autowired
private ClimateThresholdRepository thresholdRepository;
// 保存或更新阈值设置
public ClimateThreshold saveThreshold(ClimateThreshold threshold) {
validateThreshold(threshold);
return thresholdRepository.save(threshold);
}
// 根据ID查找
public Optional<ClimateThreshold> findById(Long id) {
return thresholdRepository.findById(id);
}
// 根据名称查找
public Optional<ClimateThreshold> findBySettingName(String settingName) {
return thresholdRepository.findBySettingName(settingName);
}
// 获取所有设置
public List<ClimateThreshold> findAll() {
return thresholdRepository.findAll();
}
// 删除设置
public void deleteById(Long id) {
thresholdRepository.deleteById(id);
}
// 验证阈值范围
private void validateThreshold(ClimateThreshold threshold) {
if (threshold.getMinTemperature().compareTo(threshold.getMaxTemperature()) >= 0) {
throw new IllegalArgumentException("最小温度不能大于等于最大温度");
}
if (threshold.getMinHumidity().compareTo(threshold.getMaxHumidity()) >= 0) {
throw new IllegalArgumentException("最小湿度不能大于等于最大湿度");
}
if (threshold.getMinHumidity().compareTo(BigDecimal.ZERO) < 0 ||
threshold.getMaxHumidity().compareTo(new BigDecimal("100")) > 0) {
throw new IllegalArgumentException("湿度范围应在0-100之间");
}
}
// 检查当前环境状态
public String checkEnvironmentStatus(String settingName, BigDecimal currentTemp, BigDecimal currentHumidity) {
Optional<ClimateThreshold> thresholdOpt = thresholdRepository.findBySettingName(settingName);
if (thresholdOpt == null) {
return "未找到对应的阈值设置: " + settingName;
}
ClimateThreshold threshold = thresholdOpt.get();
StringBuilder status = new StringBuilder();
// 检查温度
if (currentTemp.compareTo(threshold.getMinTemperature()) < 0) {
status.append("温度过低");
} else if (currentTemp.compareTo(threshold.getMaxTemperature()) > 0) {
status.append("温度过高");
} else {
status.append("温度正常");
}
status.append(" | ");
// 检查湿度
if (currentHumidity.compareTo(threshold.getMinHumidity()) < 0) {
status.append("湿度过低");
} else if (currentHumidity.compareTo(threshold.getMaxHumidity()) > 0) {
status.append("湿度过高");
} else {
status.append("湿度正常");
}
return status.toString();
}
}

View File

@ -0,0 +1,125 @@
// DataProcessingService.java
package com.xzzn.movecheck.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.movecheck.repo.HealthDevice;
import com.xzzn.movecheck.repo.HealthDeviceRepository;
import com.xzzn.movecheck.repo.SensorData;
import com.xzzn.movecheck.repo.SensorDataRepository;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
@Service
public class DataProcessingService implements IMqttMessageListener {
private static final Logger logger = LoggerFactory.getLogger(DataProcessingService.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private HealthDeviceRepository healthDeviceRepository;
@Autowired
private SensorDataRepository sensorDataRepository;
@Override
public void messageArrived(String topic, MqttMessage message) {
String payload = new String(message.getPayload());
logger.info("收到MQTT消息 - Topic: {}, QoS: {}", topic, message.getQos());
try {
switch (topic) {
case "HDYDCJ_01_DEVICE":
processDeviceHealthData(payload);
break;
case "HDYDCJ_01_UP":
processSensorData(payload);
break;
default:
logger.warn("未知的Topic: {}", topic);
}
} catch (Exception e) {
logger.error("处理MQTT消息失败 - Topic: {}, Error: {}", topic, e.getMessage());
// 可以根据需要在这里添加重试逻辑或错误处理
}
}
/**
* 处理设备健康数据 HDYDCJ_01_DEVICE
*/
private void processDeviceHealthData(String payload) {
logger.debug("开始处理设备健康数据: {}", payload);
JSONObject deviceHealthData = JSON.parseObject(payload);
String wsd = String.valueOf(deviceHealthData.get("WSD"));
String pm25 = String.valueOf(deviceHealthData.get("PM25"));
String ac = String.valueOf(deviceHealthData.get("AC"));
HealthDevice healthDevice = new HealthDevice();
healthDevice.setId(1);
healthDevice.setAc(ac);
healthDevice.setWsd(wsd);
healthDevice.setPm25(pm25);
healthDeviceRepository.save(healthDevice);
}
/**
* 处理传感器数据 HDYDCJ_01_UP
*/
private void processSensorData(String payload) {
logger.debug("开始处理传感器数据: {}", payload);
// 注意这里payload是数组格式
try {
JsonNode rootNode = objectMapper.readTree(payload);
// 读取顶层字段
String device = rootNode.get("Device").asText();
long timestamp = rootNode.get("timestamp").asLong();
// 读取Data对象中的字段
JsonNode dataNode = rootNode.get("Data");
double pm10 = dataNode.get("PM10").asDouble();
double pm25 = dataNode.get("PM25").asDouble();
double wd = dataNode.get("WD").asDouble();
double sd = dataNode.get("SD").asDouble();
System.out.println("Device: " + device);
System.out.println("Timestamp: " + timestamp);
System.out.println("PM10: " + pm10);
System.out.println("PM25: " + pm25);
System.out.println("WD: " + wd);
System.out.println("SD: " + sd);
List<SensorData> list = sensorDataRepository.findTop10ByOrderByCreateTimeDesc();
SensorData entity = new SensorData();
entity.setWd(wd);
entity.setPm(pm25);
entity.setSd(sd);
if (BigDecimal.valueOf(wd).compareTo(BigDecimal.ZERO) == 0) {
entity.setWd(list.get(0).getWd());
}
if (BigDecimal.valueOf(pm25).compareTo(BigDecimal.ZERO) == 0) {
entity.setPm(list.get(0).getPm());
}
if (BigDecimal.valueOf(sd).compareTo(BigDecimal.ZERO) == 0) {
entity.setSd(list.get(0).getSd());
}
sensorDataRepository.save(entity);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,193 @@
// service/EmqxClientService.java
package com.xzzn.movecheck.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.movecheck.wsd.ClientInfo;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.Base64;
import java.util.Date;
@Service
public class EmqxClientService {
private static final Logger logger = LoggerFactory.getLogger(EmqxClientService.class);
private String username = "f080a5e8e41e10bf";
private String password="ynAnM9BXWDqgwmx672D2MxHNaal6ZmpcCEgpnPx0ludL";
private String baseUrl="http://122.51.194.184:18083";
private final CloseableHttpClient httpClient;
private final ObjectMapper objectMapper;
public EmqxClientService(CloseableHttpClient httpClient, ObjectMapper objectMapper) {
this.httpClient = httpClient;
this.objectMapper = objectMapper;
}
@PostConstruct
public void init() {
logger.info("EMQX API Service initialized with base URL: {}", baseUrl);
}
@PreDestroy
public void destroy() {
try {
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
logger.error("Error closing HTTP client", e);
}
}
/**
* 获取指定客户端信息
*/
public ClientInfo getClientInfo(String clientId) {
String url = baseUrl + "/api/v5/clients/" + clientId;
logger.info("Requesting client info from: {}", url);
String auth = username + ":" + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
String authHeader = "Basic " + encodedAuth;
HttpGet request = new HttpGet(url);
request.setHeader("Authorization", authHeader);
try {
HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());
logger.debug("Response status: {}, body: {}", statusCode, responseBody);
if (statusCode == 200) {
return objectMapper.readValue(responseBody, ClientInfo.class);
} else if (statusCode == 404) {
throw new RuntimeException("客户端不存在: " + clientId);
} else {
throw new RuntimeException("API 请求失败,状态码: " + statusCode + ", 响应: " + responseBody);
}
} catch (Exception e) {
logger.error("获取客户端信息失败: {}", e.getMessage(), e);
throw new RuntimeException("获取客户端信息失败: " + e.getMessage(), e);
} finally {
request.releaseConnection();
}
}
/**
* 获取所有客户端列表
*/
public String getAllClients() {
String url = baseUrl + "/api/v5/clients";
logger.info("Requesting all clients from: {}", url);
String auth = username + ":" + password;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
String authHeader = "Basic " + encodedAuth;
HttpGet request = new HttpGet(url);
request.setHeader("Authorization", authHeader);
try {
HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());
if (statusCode == 200) {
return responseBody;
} else {
throw new RuntimeException("获取客户端列表失败,状态码: " + statusCode + ", 响应: " + responseBody);
}
} catch (Exception e) {
logger.error("获取客户端列表失败: {}", e.getMessage(), e);
throw new RuntimeException("获取客户端列表失败: " + e.getMessage(), e);
} finally {
request.releaseConnection();
}
}
/**
* 检查客户端是否在线
*/
public boolean isClientOnline(String clientId) {
try {
ClientInfo clientInfo = getClientInfo(clientId);
return clientInfo.getConnected() != null && clientInfo.getConnected();
} catch (Exception e) {
logger.warn("检查客户端在线状态失败: {}", e.getMessage());
return false;
}
}
/**
* 获取客户端连接状态详情
*/
public ClientStatusResponse getClientStatus(String clientId) {
try {
ClientInfo clientInfo = getClientInfo(clientId);
return new ClientStatusResponse(clientId,
clientInfo.getConnected() != null && clientInfo.getConnected(),
clientInfo.getIpAddress(),
clientInfo.getConnectedAt(),
clientInfo.getDisconnectedAt());
} catch (Exception e) {
throw new RuntimeException("获取客户端状态失败: " + e.getMessage(), e);
}
}
// 内部状态响应类
public static class ClientStatusResponse {
private String clientId;
private boolean online;
private String ipAddress;
private Date connectedAt;
private Date disconnectedAt;
public ClientStatusResponse(String clientId, boolean online, String ipAddress,
Date connectedAt, Date disconnectedAt) {
this.clientId = clientId;
this.online = online;
this.ipAddress = ipAddress;
this.connectedAt = connectedAt;
this.disconnectedAt = disconnectedAt;
}
// getters and setters
public String getClientId() { return clientId; }
public void setClientId(String clientId) { this.clientId = clientId; }
public boolean isOnline() { return online; }
public void setOnline(boolean online) { this.online = online; }
public String getIpAddress() { return ipAddress; }
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
public Date getConnectedAt() { return connectedAt; }
public void setConnectedAt(Date connectedAt) { this.connectedAt = connectedAt; }
public Date getDisconnectedAt() { return disconnectedAt; }
public void setDisconnectedAt(Date disconnectedAt) { this.disconnectedAt = disconnectedAt; }
}
}

View File

@ -0,0 +1,152 @@
package com.xzzn.movecheck.service;
import com.xzzn.movecheck.repo.AlertData;
import com.xzzn.movecheck.repo.AlertDataRepository;
import com.xzzn.movecheck.repo.EventData;
import com.xzzn.movecheck.repo.EventDataRepository;
import com.xzzn.movecheck.wsd.AlertRequest;
import com.xzzn.movecheck.wsd.EventRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class EventAlertService {
@Autowired
private EventDataRepository eventDataRepository;
@Autowired
private AlertDataRepository alertDataRepository;
// 事件相关方法
/**
* 保存事件数据
*/
public Map<String, Object> saveEvent(EventRequest request) {
try {
EventData eventData = new EventData(
request.getEventType(),
request.getEventTime(),
request.getStatus(),
request.getDescription(),
request.getDeviceId()
);
EventData saved = eventDataRepository.save(eventData);
Map<String, Object> result = new HashMap<>();
result.put("status", "success");
result.put("message", "事件保存成功");
result.put("id", saved.getId());
return result;
} catch (Exception e) {
Map<String, Object> result = new HashMap<>();
result.put("status", "error");
result.put("message", "事件保存失败: " + e.getMessage());
return result;
}
}
/**
* 获取最新10条事件
*/
public List<EventData> getLatestEvents() {
return eventDataRepository.findTop10ByOrderByCreateTimeDesc();
}
/**
* 分页获取事件
*/
public Page<EventData> getEventsByPage(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createTime"));
return eventDataRepository.findAllByOrderByCreateTimeDesc(pageable);
}
// 告警相关方法
/**
* 保存告警数据
*/
public Map<String, Object> saveAlert(AlertRequest request) {
try {
AlertData alertData = new AlertData(
request.getContent(),
request.getCategory(),
request.getAlertTime(),
request.getLevel(),
request.getAction(),
request.getActionTime(),
request.getDeviceId()
);
AlertData saved = alertDataRepository.save(alertData);
Map<String, Object> result = new HashMap<>();
result.put("status", "success");
result.put("message", "告警保存成功");
result.put("id", saved.getId());
return result;
} catch (Exception e) {
Map<String, Object> result = new HashMap<>();
result.put("status", "error");
result.put("message", "告警保存失败: " + e.getMessage());
return result;
}
}
// 在 EventAlertService 类中需要添加的方法签名
public Page<EventData> getEventsByCreateTime(LocalDateTime startTime, LocalDateTime endTime, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createTime"));
if (startTime != null && endTime != null) {
return eventDataRepository.findByCreateTimeBetween(startTime, endTime, pageable);
} else if (startTime != null) {
return eventDataRepository.findByCreateTimeAfter(startTime, pageable);
} else if (endTime != null) {
return eventDataRepository.findByCreateTimeBefore(endTime, pageable);
} else {
return eventDataRepository.findAllByOrderByCreateTimeDesc(pageable);
}
}
public Page<AlertData> getAlertsByCreateTime(LocalDateTime startTime, LocalDateTime endTime, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createTime"));
if (startTime != null && endTime != null) {
return alertDataRepository.findByCreateTimeBetween(startTime, endTime, pageable);
} else if (startTime != null) {
return alertDataRepository.findByCreateTimeAfter(startTime, pageable);
} else if (endTime != null) {
return alertDataRepository.findByCreateTimeBefore(endTime, pageable);
} else {
return alertDataRepository.findAllByOrderByCreateTimeDesc(pageable);
}
}
/**
* 获取最新10条告警
*/
public List<AlertData> getLatestAlerts() {
return alertDataRepository.findTop10ByOrderByCreateTimeDesc();
}
/**
* 分页获取告警
*/
public Page<AlertData> getAlertsByPage(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createTime"));
return alertDataRepository.findAllByOrderByCreateTimeDesc(pageable);
}
}

View File

@ -0,0 +1,24 @@
// service/HealthDeviceService.java
package com.xzzn.movecheck.service;
import com.xzzn.movecheck.repo.HealthDevice;
import com.xzzn.movecheck.repo.HealthDeviceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
public class HealthDeviceService {
@Autowired
private HealthDeviceRepository healthDeviceRepository;
// 获取最新记录
public List<HealthDevice> findLatestRecord() {
return healthDeviceRepository.findLatestRecord();
}
}

View File

@ -0,0 +1,143 @@
package com.xzzn.movecheck.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.movecheck.repo.HealthDevice;
import com.xzzn.movecheck.repo.HealthDeviceRepository;
import com.xzzn.movecheck.repo.SensorData;
import com.xzzn.movecheck.repo.SensorDataRepository;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@Component
public class MqttMessageHandler implements MqttCallback {
private static final Logger logger = LoggerFactory.getLogger(DataProcessingService.class);
private static final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private HealthDeviceRepository healthDeviceRepository;
@Autowired
private SensorDataRepository sensorDataRepository;
/**
* 接收到消息时的回调
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String payload = new String(message.getPayload());
System.out.println("收到消息 - Topic: " + topic + ", QoS: " + message.getQos());
try {
if ("HDYDCJ_01_UP".equals(topic)) {
processSensorData(payload);
} else if ("HDYDCJ_01_DEVICE".equals(topic)) {
processDeviceData(payload);
}
} catch (Exception e) {
System.err.println("处理MQTT消息失败: " + e.getMessage());
}
}
/**
* 连接丢失时的回调
*/
@Override
public void connectionLost(Throwable cause) {
System.err.println("MQTT连接丢失: " + cause.getMessage());
// 可以在这里添加重连逻辑
}
/**
* 消息传递完成时的回调
*/
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// 用于发布消息时的回调,订阅场景下一般不需要处理
System.out.println("消息传递完成");
}
private void processSensorData(String payload) throws Exception {
logger.debug("开始处理数据: {}", payload);
JSONArray jsonArray = JSONArray.parseArray(payload);
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject item = jsonArray.getJSONObject(i);
// 获取Device类型
String device = item.getString("Device");
Long timestamp = item.getLong("timestamp");
// 如果是PM25设备
if ("PM25".equals(device)) {
List<SensorData> list = sensorDataRepository.findTop10ByOrderByCreateTimeDesc();
JSONObject data = item.getJSONObject("Data");
// 获取PM25值如果为null则转为0
Double pm25Value = data.getDouble("PM25");
if (pm25Value == null) {
pm25Value = list.get(0).getPm();
}
// 获取SD值如果为null则转为0.0
Double sdValue = data.getDouble("SD");
if (sdValue == null) {
sdValue = list.get(0).getSd();
}
// 获取WD值如果为null则转为0.0
Double wdValue = data.getDouble("WD");
if (wdValue == null) {
wdValue = list.get(0).getWd();
}
SensorData entity = new SensorData();
entity.setWd(wdValue);
entity.setPm(pm25Value);
entity.setSd(sdValue);
sensorDataRepository.save(entity);
}
}
}
private void processDeviceData(String payload) {
logger.debug("开始处理设备健康数据: {}", payload);
JSONObject deviceHealthData = JSON.parseObject(payload);
JSONObject wsdObject = deviceHealthData.getJSONObject("WSD");
String wsdHealth = String.valueOf(wsdObject.getInteger("health"));
JSONObject acObject = deviceHealthData.getJSONObject("AC");
String acHealth = String.valueOf(acObject.getInteger("health"));
JSONObject pm25Object = deviceHealthData.getJSONObject("PM25");
String pm25Health = String.valueOf(pm25Object.getInteger("health"));
HealthDevice healthDevice = new HealthDevice();
healthDevice.setId(1);
healthDevice.setAc(acHealth);
healthDevice.setWsd(wsdHealth);
healthDevice.setPm25(pm25Health);
healthDevice.setCreatedTime(LocalDateTime.now());
healthDevice.setUpdatedTime(LocalDateTime.now());
healthDeviceRepository.save(healthDevice);
}
}

View File

@ -0,0 +1,148 @@
package com.xzzn.movecheck.service;
import com.xzzn.movecheck.repo.TemperatureHumidityData;
import com.xzzn.movecheck.repo.TemperatureHumidityRepository;
import com.xzzn.movecheck.wsd.TemperatureHumidityRequest;
import com.xzzn.movecheck.wsd.TemperatureHumidityResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class TemperatureHumidityService {
@Autowired
private TemperatureHumidityRepository repository;
/**
* 保存温湿度数据
*/
public TemperatureHumidityResponse saveData(TemperatureHumidityRequest request) {
try {
TemperatureHumidityData data = new TemperatureHumidityData(
request.getTemperature(),
request.getHumidity(),
request.getDeviceId()
);
TemperatureHumidityData saved = repository.save(data);
return TemperatureHumidityResponse.success("温湿度数据保存成功");
} catch (Exception e) {
return TemperatureHumidityResponse.error("温湿度数据保存失败: " + e.getMessage());
}
}
/**
* 获取最新温湿度数据
*/
public TemperatureHumidityResponse getLatestData() {
try {
Optional<TemperatureHumidityData> dataOpt = repository.findLatestData();
if (dataOpt.isPresent()) {
return TemperatureHumidityResponse.fromEntity(dataOpt.get());
} else {
return TemperatureHumidityResponse.error("暂无温湿度数据");
}
} catch (Exception e) {
return TemperatureHumidityResponse.error("获取温湿度数据失败: " + e.getMessage());
}
}
/**
* 根据设备ID获取最新数据
*/
public TemperatureHumidityResponse getLatestByDevice(String deviceId) {
try {
Optional<TemperatureHumidityData> dataOpt = repository.findLatestByDeviceId(deviceId);
if (dataOpt.isPresent()) {
return TemperatureHumidityResponse.fromEntity(dataOpt.get());
} else {
return TemperatureHumidityResponse.error("未找到设备 " + deviceId + " 的温湿度数据");
}
} catch (Exception e) {
return TemperatureHumidityResponse.error("获取设备温湿度数据失败: " + e.getMessage());
}
}
/**
* 获取最新10条数据
*/
public TemperatureHumidityResponse getLatest10Data() {
try {
List<TemperatureHumidityData> dataList = repository.findTop10ByOrderByCreateTimeDesc();
TemperatureHumidityResponse response = new TemperatureHumidityResponse();
response.setStatus("success");
response.setMessage("获取成功");
response.setDataList(dataList);
response.setTotal((long) dataList.size());
return response;
} catch (Exception e) {
return TemperatureHumidityResponse.error("获取温湿度数据列表失败: " + e.getMessage());
}
}
/**
* 根据设备ID获取最新10条数据
*/
public TemperatureHumidityResponse getLatest10ByDevice(String deviceId) {
try {
List<TemperatureHumidityData> dataList = repository.findTop10ByDeviceIdOrderByCreateTimeDesc(deviceId);
TemperatureHumidityResponse response = new TemperatureHumidityResponse();
response.setStatus("success");
response.setMessage("获取成功");
response.setDataList(dataList);
response.setTotal((long) dataList.size());
return response;
} catch (Exception e) {
return TemperatureHumidityResponse.error("获取设备温湿度数据列表失败: " + e.getMessage());
}
}
/**
* 分页查询数据
*/
public Page<TemperatureHumidityData> getDataByPage(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createTime"));
return repository.findAllByOrderByCreateTimeDesc(pageable);
}
/**
* 根据设备ID分页查询
*/
public Page<TemperatureHumidityData> getDataByDeviceAndPage(String deviceId, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createTime"));
return repository.findByDeviceIdOrderByCreateTimeDesc(deviceId, pageable);
}
/**
* 获取所有设备ID列表
*/
public List<String> getAllDeviceIds() {
return repository.findAllDeviceIds();
}
/**
* 获取数据总数
*/
public long getTotalCount() {
return repository.count();
}
}

View File

@ -0,0 +1,39 @@
// SimpleTokenUtil.java
package com.xzzn.movecheck.util;
import org.springframework.stereotype.Component;
import java.util.Base64;
import java.util.UUID;
@Component
public class SimpleTokenUtil {
// 生成简单的token实际项目中应该更复杂
public String generateToken(String username) {
String rawToken = username + ":" + System.currentTimeMillis() + ":" + UUID.randomUUID();
return Base64.getEncoder().encodeToString(rawToken.getBytes());
}
// 验证token这里简单验证实际项目需要更严格的验证
public boolean validateToken(String token) {
try {
String decoded = new String(Base64.getDecoder().decode(token));
String[] parts = decoded.split(":");
return parts.length == 3; // 简单的格式验证
} catch (Exception e) {
return false;
}
}
// 从token中获取用户名
public String getUsernameFromToken(String token) {
try {
String decoded = new String(Base64.getDecoder().decode(token));
String[] parts = decoded.split(":");
return parts.length >= 1 ? parts[0] : null;
} catch (Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,81 @@
package com.xzzn.movecheck.wsd;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class AlertRequest {
private String content;
private String category;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime alertTime;
private String level;
private String action;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime actionTime;
private String deviceId;
// Getter 和 Setter
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public LocalDateTime getAlertTime() {
return alertTime;
}
public void setAlertTime(LocalDateTime alertTime) {
this.alertTime = alertTime;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public LocalDateTime getActionTime() {
return actionTime;
}
public void setActionTime(LocalDateTime actionTime) {
this.actionTime = actionTime;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
}

View File

@ -0,0 +1,69 @@
// AuthController.java
package com.xzzn.movecheck.wsd;
import com.xzzn.movecheck.repo.User;
import com.xzzn.movecheck.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/auth")
@CrossOrigin(origins = "*", maxAge = 3600)
public class AuthController {
@Autowired
private AuthService authService;
@PostMapping("/login")
public LoginResponse login(@RequestBody LoginRequest loginRequest) {
return authService.login(loginRequest);
}
@PostMapping("/validate")
public LoginResponse validateToken(@RequestHeader(value = "Authorization", required = false) String authHeader) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return new LoginResponse(false, "缺少有效的token");
}
String token = authHeader.substring(7);
boolean isValid = authService.validateToken(token);
if (isValid) {
Optional<User> userOpt = authService.getCurrentUser(token);
String nickname = userOpt.map(User::getNickname).orElse("");
return new LoginResponse(true, "Token有效", token, userOpt.map(User::getUsername).orElse(""), nickname);
} else {
return new LoginResponse(false, "Token无效或已过期");
}
}
@PostMapping("/logout")
public LoginResponse logout(@RequestHeader(value = "Authorization", required = false) String authHeader) {
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
authService.logout(token);
}
return new LoginResponse(true, "登出成功");
}
// 获取当前用户信息
@GetMapping("/me")
public LoginResponse getCurrentUser(@RequestHeader(value = "Authorization", required = false) String authHeader) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return new LoginResponse(false, "未登录");
}
String token = authHeader.substring(7);
Optional<User> userOpt = authService.getCurrentUser(token);
if (userOpt.isPresent()) {
User user = userOpt.get();
return new LoginResponse(true, "获取成功", token, user.getUsername(), user.getNickname());
} else {
return new LoginResponse(false, "用户不存在");
}
}
}

View File

@ -0,0 +1,149 @@
// controller/ClientController.java
package com.xzzn.movecheck.wsd;
import com.xzzn.movecheck.service.EmqxClientService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/emqx")
@CrossOrigin(origins = "*") // 允许跨域访问
public class ClientController {
private static final Logger logger = LoggerFactory.getLogger(ClientController.class);
private final EmqxClientService emqxClientService;
public ClientController(EmqxClientService emqxClientService) {
this.emqxClientService = emqxClientService;
}
/**
* 获取客户端详细信息
* GET /api/emqx/clients/{clientId}
*/
@GetMapping("/clients/{clientId}")
public ResponseEntity<?> getClientInfo(@PathVariable String clientId) {
try {
logger.info("收到获取客户端信息请求: {}", clientId);
ClientInfo clientInfo = emqxClientService.getClientInfo(clientId);
return ResponseEntity.ok(clientInfo);
} catch (Exception e) {
logger.error("获取客户端信息失败: {}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("GET_CLIENT_INFO_FAILED", e.getMessage()));
}
}
/**
* 检查客户端在线状态
* GET /api/emqx/clients/{clientId}/status
*/
@GetMapping("/clients/{clientId}/status")
public ResponseEntity<?> getClientStatus(@PathVariable String clientId) {
try {
logger.info("收到检查客户端状态请求: {}", clientId);
boolean isOnline = emqxClientService.isClientOnline(clientId);
return ResponseEntity.ok(new StatusResponse(clientId, isOnline));
} catch (Exception e) {
logger.error("检查客户端状态失败: {}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("CHECK_CLIENT_STATUS_FAILED", e.getMessage()));
}
}
/**
* 获取客户端详细状态
* GET /api/emqx/clients/{clientId}/detail-status
*/
@GetMapping("/clients/{clientId}/detail-status")
public ResponseEntity<?> getClientDetailStatus(@PathVariable String clientId) {
try {
logger.info("收到获取客户端详细状态请求: {}", clientId);
EmqxClientService.ClientStatusResponse status = emqxClientService.getClientStatus(clientId);
return ResponseEntity.ok(status);
} catch (Exception e) {
logger.error("获取客户端详细状态失败: {}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("GET_CLIENT_DETAIL_STATUS_FAILED", e.getMessage()));
}
}
/**
* 获取所有客户端列表
* GET /api/emqx/clients
*/
@GetMapping("/clients")
public ResponseEntity<?> getAllClients() {
try {
logger.info("收到获取所有客户端列表请求");
String clients = emqxClientService.getAllClients();
return ResponseEntity.ok(clients);
} catch (Exception e) {
logger.error("获取客户端列表失败: {}", e.getMessage(), e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("GET_ALL_CLIENTS_FAILED", e.getMessage()));
}
}
/**
* 健康检查接口
* GET /api/emqx/health
*/
@GetMapping("/health")
public ResponseEntity<?> healthCheck() {
return ResponseEntity.ok(new HealthResponse("OK", "EMQX API Service is running"));
}
// 响应包装类
public static class StatusResponse {
private String clientId;
private boolean online;
public StatusResponse(String clientId, boolean online) {
this.clientId = clientId;
this.online = online;
}
public String getClientId() { return clientId; }
public void setClientId(String clientId) { this.clientId = clientId; }
public boolean isOnline() { return online; }
public void setOnline(boolean online) { this.online = online; }
}
public static class ErrorResponse {
private String error;
private String message;
public ErrorResponse(String error, String message) {
this.error = error;
this.message = message;
}
public String getError() { return error; }
public void setError(String error) { this.error = error; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
public static class HealthResponse {
private String status;
private String message;
public HealthResponse(String status, String message) {
this.status = status;
this.message = message;
}
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
}

View File

@ -0,0 +1,57 @@
// model/ClientInfo.java
package com.xzzn.movecheck.wsd;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date;
public class ClientInfo {
@JsonProperty("clientid")
private String clientId;
@JsonProperty("username")
private String username;
@JsonProperty("connected")
private Boolean connected;
@JsonProperty("ip_address")
private String ipAddress;
@JsonProperty("connected_at")
private Date connectedAt;
@JsonProperty("disconnected_at")
private Date disconnectedAt;
// getters and setters
public String getClientId() { return clientId; }
public void setClientId(String clientId) { this.clientId = clientId; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Boolean getConnected() { return connected; }
public void setConnected(Boolean connected) { this.connected = connected; }
public String getIpAddress() { return ipAddress; }
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
public Date getConnectedAt() { return connectedAt; }
public void setConnectedAt(Date connectedAt) { this.connectedAt = connectedAt; }
public Date getDisconnectedAt() { return disconnectedAt; }
public void setDisconnectedAt(Date disconnectedAt) { this.disconnectedAt = disconnectedAt; }
@Override
public String toString() {
return "ClientInfo{" +
"clientId='" + clientId + '\'' +
", username='" + username + '\'' +
", connected=" + connected +
", ipAddress='" + ipAddress + '\'' +
", connectedAt=" + connectedAt +
", disconnectedAt=" + disconnectedAt +
'}';
}
}

View File

@ -0,0 +1,88 @@
package com.xzzn.movecheck.wsd;// ClimateThresholdController.java
import com.xzzn.movecheck.repo.ClimateThreshold;
import com.xzzn.movecheck.service.ClimateThresholdService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/wsd")
public class ClimateThresholdController {
private static final Logger logger = LoggerFactory.getLogger(ClimateThresholdController.class);
@Autowired
private ClimateThresholdService thresholdService;
// 创建新的阈值设置
@PostMapping
public ResponseEntity<?> createThreshold(@RequestBody ClimateThreshold threshold) {
try {
ClimateThreshold saved = thresholdService.saveThreshold(threshold);
return ResponseEntity.ok(saved);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
// 获取所有阈值设置
@GetMapping
public List<ClimateThreshold> getAllThresholds() {
return thresholdService.findAll();
}
// 根据ID获取阈值设置
@GetMapping("/{id}")
public ResponseEntity<ClimateThreshold> getThresholdById(@PathVariable Long id) {
Optional<ClimateThreshold> threshold = thresholdService.findById(id);
logger.info("getThresholdById id: {}, threshold: {}", id, threshold);
return threshold.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// 根据名称获取阈值设置
@GetMapping("/name/{settingName}")
public ResponseEntity<ClimateThreshold> getThresholdByName(@PathVariable String settingName) {
Optional<ClimateThreshold> threshold = thresholdService.findBySettingName(settingName);
return threshold.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// 更新阈值设置
@PutMapping("/{id}")
public ResponseEntity<?> updateThreshold(@PathVariable Long id, @RequestBody ClimateThreshold threshold) {
threshold.setId(id);
try {
ClimateThreshold updated = thresholdService.saveThreshold(threshold);
return ResponseEntity.ok(updated);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
// 删除阈值设置
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteThreshold(@PathVariable Long id) {
thresholdService.deleteById(id);
return ResponseEntity.ok().build();
}
// 检查环境状态
@GetMapping("/check-status")
public ResponseEntity<String> checkStatus(
@RequestParam String settingName,
@RequestParam BigDecimal temperature,
@RequestParam BigDecimal humidity) {
String status = thresholdService.checkEnvironmentStatus(settingName, temperature, humidity);
return ResponseEntity.ok(status);
}
}

View File

@ -0,0 +1,44 @@
package com.xzzn.movecheck.wsd;
public class ClimateThresholdRequest {
private Double wdmin;
private Double wdmax;
private Double sdmin;
private Double sdmax;
public Double getWdmin() {
return wdmin;
}
public void setWdmin(Double wdmin) {
this.wdmin = wdmin;
}
public Double getWdmax() {
return wdmax;
}
public void setWdmax(Double wdmax) {
this.wdmax = wdmax;
}
public Double getSdmin() {
return sdmin;
}
public void setSdmin(Double sdmin) {
this.sdmin = sdmin;
}
public Double getSdmax() {
return sdmax;
}
public void setSdmax(Double sdmax) {
this.sdmax = sdmax;
}
}

View File

@ -0,0 +1,71 @@
package com.xzzn.movecheck.wsd;
import com.xzzn.movecheck.repo.SensorData;
import com.xzzn.movecheck.repo.SensorDataRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/data")
@CrossOrigin(origins = "*")
public class DataController {
@Autowired
private SensorDataRepository sensorDataRepository;
// 获取最新数据
@GetMapping("/latest")
public ResponseEntity<Map<String, Object>> getLatestData() {
List<SensorData> dataList = sensorDataRepository.findTop10ByOrderByCreateTimeDesc();
Map<String, Object> response = new HashMap<>();
if (!dataList.isEmpty()) {
SensorData latest = dataList.get(0);
response.put("sd", latest.getSd());
response.put("wd", latest.getWd());
response.put("pm", latest.getPm());
response.put("timestamp", latest.getCreateTime());
response.put("status", "success");
} else {
response.put("status", "no_data");
response.put("message", "暂无数据");
}
return ResponseEntity.ok(response);
}
// 获取历史数据
@PostMapping("/history")
public ResponseEntity<List<SensorData>> getHistoryData(@RequestBody TimeRangeRequest request) {
List<SensorData> dataList = sensorDataRepository.findByCreateTimeBetween(request.getStartTime(), request.getEndTime());
return ResponseEntity.ok(dataList);
}
// 获取指定数据
@GetMapping("/current")
public ResponseEntity<Map<String, Object>> getCurrentData() {
List<SensorData> dataList = sensorDataRepository.findTop10ByOrderByCreateTimeDesc();
Map<String, Object> response = new HashMap<>();
if (!dataList.isEmpty()) {
SensorData current = dataList.get(0);
response.put("sd", current.getSd());
response.put("wd", current.getWd());
response.put("pm", current.getWd());
} else {
response.put("sd", 0);
response.put("wd", 0);
response.put("pm", 0);
}
return ResponseEntity.ok(response);
}
}

View File

@ -0,0 +1,22 @@
// EmqxApiResponse.java
package com.xzzn.movecheck.wsd;
import com.xzzn.movecheck.repo.EmqxClientInfo;
import java.util.List;
public class EmqxApiResponse {
private Integer code;
private String message;
private List<EmqxClientInfo> data;
// Getter和Setter
public Integer getCode() { return code; }
public void setCode(Integer code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public List<EmqxClientInfo> getData() { return data; }
public void setData(List<EmqxClientInfo> data) { this.data = data; }
}

View File

@ -0,0 +1,190 @@
package com.xzzn.movecheck.wsd;
import com.xzzn.movecheck.service.EventAlertService;
import com.xzzn.movecheck.repo.AlertData;
import com.xzzn.movecheck.repo.EventData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "*")
@Validated
public class EventAlertController {
@Autowired
private EventAlertService eventAlertService;
// ==================== 事件相关API ====================
/**
* 保存事件数据
*/
@PostMapping("/events")
public ResponseEntity<Map<String, Object>> saveEvent( @RequestBody EventRequest request) {
Map<String, Object> result = eventAlertService.saveEvent(request);
return ResponseEntity.ok(result);
}
/**
* 获取最新10条事件
*/
@GetMapping("/events/latest")
public ResponseEntity<Map<String, Object>> getLatestEvents() {
List<EventData> events = eventAlertService.getLatestEvents();
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("data", events);
response.put("total", events.size());
return ResponseEntity.ok(response);
}
/**
* 分页获取事件数据
*/
@GetMapping("/events")
public ResponseEntity<PageResponse<EventData>> getEventsByPage(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<EventData> eventPage = eventAlertService.getEventsByPage(page, size);
PageResponse<EventData> response = PageResponse.success(eventPage);
return ResponseEntity.ok(response);
}
// ==================== 告警相关API ====================
/**
* 保存告警数据
*/
@PostMapping("/alerts")
public ResponseEntity<Map<String, Object>> saveAlert( @RequestBody AlertRequest request) {
Map<String, Object> result = eventAlertService.saveAlert(request);
return ResponseEntity.ok(result);
}
/**
* 获取最新10条告警
*/
@GetMapping("/alerts/latest")
public ResponseEntity<Map<String, Object>> getLatestAlerts() {
List<AlertData> alerts = eventAlertService.getLatestAlerts();
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("data", alerts);
response.put("total", alerts.size());
return ResponseEntity.ok(response);
}
/**
* 分页获取告警数据
*/
@GetMapping("/alerts")
public ResponseEntity<PageResponse<AlertData>> getAlertsByPage(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<AlertData> alertPage = eventAlertService.getAlertsByPage(page, size);
PageResponse<AlertData> response = PageResponse.success(alertPage);
return ResponseEntity.ok(response);
}
/**
* 根据创建时间范围查询事件数据
*/
@GetMapping("/events/byCreateTime")
public ResponseEntity<Map<String, Object>> getEventsByCreateTime(
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
// 将字符串时间转换为 LocalDateTime
LocalDateTime start = null;
LocalDateTime end = null;
if (startTime != null && !startTime.isEmpty()) {
start = parseTimeString(startTime);
}
if (endTime != null && !endTime.isEmpty()) {
end = parseTimeString(endTime);
}
Page<EventData> eventPage = eventAlertService.getEventsByCreateTime(start, end, page, size);
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("data", eventPage.getContent());
response.put("total", eventPage.getTotalElements());
response.put("currentPage", eventPage.getNumber());
response.put("totalPages", eventPage.getTotalPages());
return ResponseEntity.ok(response);
}
/**
* 根据创建时间范围查询告警数据
*/
@GetMapping("/alerts/byCreateTime")
public ResponseEntity<Map<String, Object>> getAlertsByCreateTime(
@RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
// 将字符串时间转换为 LocalDateTime
LocalDateTime start = null;
LocalDateTime end = null;
if (startTime != null && !startTime.isEmpty()) {
start = parseTimeString(startTime);
}
if (endTime != null && !endTime.isEmpty()) {
end = parseTimeString(endTime);
}
Page<AlertData> alertPage = eventAlertService.getAlertsByCreateTime(start, end, page, size);
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("data", alertPage.getContent());
response.put("total", alertPage.getTotalElements());
response.put("currentPage", alertPage.getNumber());
response.put("totalPages", alertPage.getTotalPages());
return ResponseEntity.ok(response);
}
/**
* 解析时间字符串为 LocalDateTime
*/
private LocalDateTime parseTimeString(String timeStr) {
try {
// 支持多种时间格式
if (timeStr.contains("T")) {
// ISO 格式: 2025-10-10T10:00:00
return LocalDateTime.parse(timeStr);
} else if (timeStr.contains(":")) {
// 格式: 2025-10-10 10:00:00
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(timeStr, formatter);
} else {
// 日期格式: 2025-10-10
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(timeStr, formatter);
return date.atStartOfDay();
}
} catch (Exception e) {
throw new IllegalArgumentException("无效的时间格式: " + timeStr);
}
}
}

View File

@ -0,0 +1,60 @@
package com.xzzn.movecheck.wsd;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class EventRequest {
private String eventType;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime eventTime;
private String status;
private String description;
private String deviceId;
// Getter 和 Setter
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public LocalDateTime getEventTime() {
return eventTime;
}
public void setEventTime(LocalDateTime eventTime) {
this.eventTime = eventTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
}

View File

@ -0,0 +1,38 @@
package com.xzzn.movecheck.wsd;
import com.xzzn.movecheck.repo.HealthDevice;
import com.xzzn.movecheck.service.HealthDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Optional;
// 在Controller中使用
@RestController
@RequestMapping("/api/health")
public class HealthDeviceController {
@Autowired
private HealthDeviceService healthDeviceService;
/**
* 获取最新的设备数据
*/
@GetMapping("/latest")
public ResponseEntity<?> getLatestRecord() {
List<HealthDevice> latestRecord = healthDeviceService.findLatestRecord();
if (latestRecord != null && !latestRecord.isEmpty()) {
return ResponseEntity.ok(latestRecord.get(0));
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("未找到设备数据");
}
}
}

View File

@ -0,0 +1,21 @@
// LoginRequest.java
package com.xzzn.movecheck.wsd;
public class LoginRequest {
private String username;
private String password;
public LoginRequest() {}
public LoginRequest(String username, String password) {
this.username = username;
this.password = password;
}
// Getter和Setter
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}

View File

@ -0,0 +1,44 @@
// LoginResponse.java
package com.xzzn.movecheck.wsd;
public class LoginResponse {
private boolean success;
private String message;
private String token;
private String username;
private String nickname;
// 无参构造
public LoginResponse() {}
// 只有success和message的构造
public LoginResponse(boolean success, String message) {
this.success = success;
this.message = message;
}
// 包含所有字段的构造
public LoginResponse(boolean success, String message, String token, String username, String nickname) {
this.success = success;
this.message = message;
this.token = token;
this.username = username;
this.nickname = nickname;
}
// Getter和Setter
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getToken() { return token; }
public void setToken(String token) { this.token = token; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getNickname() { return nickname; }
public void setNickname(String nickname) { this.nickname = nickname; }
}

View File

@ -0,0 +1,116 @@
package com.xzzn.movecheck.wsd;
import org.springframework.data.domain.Page;
import java.util.List;
public class PageResponse<T> {
private String status;
private String message;
private List<T> data;
private int currentPage;
private int pageSize;
private long totalElements;
private int totalPages;
private boolean hasNext;
private boolean hasPrevious;
// 构造函数
public PageResponse() {}
public PageResponse(String status, String message) {
this.status = status;
this.message = message;
}
// 从Spring Page对象创建响应
public static <T> PageResponse<T> success(Page<T> page) {
PageResponse<T> response = new PageResponse<>();
response.status = "success";
response.message = "查询成功";
response.data = page.getContent();
response.currentPage = page.getNumber();
response.pageSize = page.getSize();
response.totalElements = page.getTotalElements();
response.totalPages = page.getTotalPages();
response.hasNext = page.hasNext();
response.hasPrevious = page.hasPrevious();
return response;
}
public static <T> PageResponse<T> error(String message) {
return new PageResponse<>("error", message);
}
// Getter 和 Setter
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public long getTotalElements() {
return totalElements;
}
public void setTotalElements(long totalElements) {
this.totalElements = totalElements;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
public boolean isHasNext() {
return hasNext;
}
public void setHasNext(boolean hasNext) {
this.hasNext = hasNext;
}
public boolean isHasPrevious() {
return hasPrevious;
}
public void setHasPrevious(boolean hasPrevious) {
this.hasPrevious = hasPrevious;
}
}

View File

@ -0,0 +1,52 @@
package com.xzzn.movecheck.wsd;
// 订阅状态数据模型
public class SubscriptionStatus {
private String topic;
private boolean subscribed;
private int qos;
private long subscribeTime;
private String errorMessage;
// getters and setters
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public boolean isSubscribed() {
return subscribed;
}
public void setSubscribed(boolean subscribed) {
this.subscribed = subscribed;
}
public int getQos() {
return qos;
}
public void setQos(int qos) {
this.qos = qos;
}
public long getSubscribeTime() {
return subscribeTime;
}
public void setSubscribeTime(long subscribeTime) {
this.subscribeTime = subscribeTime;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}

View File

@ -0,0 +1,154 @@
package com.xzzn.movecheck.wsd;
import com.xzzn.movecheck.service.TemperatureHumidityService;
import com.xzzn.movecheck.repo.TemperatureHumidityData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/ac")
@CrossOrigin(origins = "*")
@Validated
public class TemperatureHumidityController {
@Autowired
private TemperatureHumidityService temperatureHumidityService;
/**
* 保存温湿度数据
*/
@PostMapping("/data")
public ResponseEntity<TemperatureHumidityResponse> saveTemperatureHumidity(
@RequestBody TemperatureHumidityRequest request) {
TemperatureHumidityResponse response = temperatureHumidityService.saveData(request);
return ResponseEntity.ok(response);
}
/**
* 获取最新温湿度数据
*/
@GetMapping("/data/latest")
public ResponseEntity<TemperatureHumidityResponse> getLatestTemperatureHumidity() {
TemperatureHumidityResponse response = temperatureHumidityService.getLatestData();
return ResponseEntity.ok(response);
}
/**
* 根据设备ID获取最新数据
*/
@GetMapping("/data/latest/device/{deviceId}")
public ResponseEntity<TemperatureHumidityResponse> getLatestTemperatureHumidityByDevice(
@PathVariable String deviceId) {
TemperatureHumidityResponse response = temperatureHumidityService.getLatestByDevice(deviceId);
return ResponseEntity.ok(response);
}
/**
* 获取最新10条温湿度数据
*/
@GetMapping("/data/recent")
public ResponseEntity<TemperatureHumidityResponse> getRecentTemperatureHumidity() {
TemperatureHumidityResponse response = temperatureHumidityService.getLatest10Data();
return ResponseEntity.ok(response);
}
/**
* 根据设备ID获取最新10条数据
*/
@GetMapping("/data/recent/device/{deviceId}")
public ResponseEntity<TemperatureHumidityResponse> getRecentTemperatureHumidityByDevice(
@PathVariable String deviceId) {
TemperatureHumidityResponse response = temperatureHumidityService.getLatest10ByDevice(deviceId);
return ResponseEntity.ok(response);
}
/**
* 分页查询温湿度数据
*/
@GetMapping("/data")
public ResponseEntity<Map<String, Object>> getTemperatureHumidityByPage(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<TemperatureHumidityData> dataPage = temperatureHumidityService.getDataByPage(page, size);
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("data", dataPage.getContent());
response.put("currentPage", dataPage.getNumber());
response.put("pageSize", dataPage.getSize());
response.put("totalElements", dataPage.getTotalElements());
response.put("totalPages", dataPage.getTotalPages());
response.put("hasNext", dataPage.hasNext());
response.put("hasPrevious", dataPage.hasPrevious());
return ResponseEntity.ok(response);
}
/**
* 根据设备ID分页查询
*/
@GetMapping("/data/device/{deviceId}")
public ResponseEntity<Map<String, Object>> getTemperatureHumidityByDeviceAndPage(
@PathVariable String deviceId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<TemperatureHumidityData> dataPage = temperatureHumidityService.getDataByDeviceAndPage(deviceId, page, size);
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("deviceId", deviceId);
response.put("data", dataPage.getContent());
response.put("currentPage", dataPage.getNumber());
response.put("pageSize", dataPage.getSize());
response.put("totalElements", dataPage.getTotalElements());
response.put("totalPages", dataPage.getTotalPages());
response.put("hasNext", dataPage.hasNext());
response.put("hasPrevious", dataPage.hasPrevious());
return ResponseEntity.ok(response);
}
/**
* 获取所有设备ID列表
*/
@GetMapping("/devices")
public ResponseEntity<Map<String, Object>> getAllDevices() {
List<String> deviceIds = temperatureHumidityService.getAllDeviceIds();
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("devices", deviceIds);
response.put("total", deviceIds.size());
return ResponseEntity.ok(response);
}
/**
* 获取数据统计
*/
@GetMapping("/stats")
public ResponseEntity<Map<String, Object>> getStats() {
long totalCount = temperatureHumidityService.getTotalCount();
List<String> deviceIds = temperatureHumidityService.getAllDeviceIds();
Map<String, Object> response = new HashMap<>();
response.put("status", "success");
response.put("totalRecords", totalCount);
response.put("totalDevices", deviceIds.size());
response.put("devices", deviceIds);
return ResponseEntity.ok(response);
}
}

View File

@ -0,0 +1,36 @@
package com.xzzn.movecheck.wsd;
public class TemperatureHumidityRequest {
private Double temperature;
private Double humidity;
private String deviceId;
// Getter 和 Setter
public Double getTemperature() {
return temperature;
}
public void setTemperature(Double temperature) {
this.temperature = temperature;
}
public Double getHumidity() {
return humidity;
}
public void setHumidity(Double humidity) {
this.humidity = humidity;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
}

View File

@ -0,0 +1,112 @@
package com.xzzn.movecheck.wsd;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.xzzn.movecheck.repo.TemperatureHumidityData;
import java.time.LocalDateTime;
import java.util.List;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TemperatureHumidityResponse {
private String status;
private String message;
private Double temperature;
private Double humidity;
private String deviceId;
private LocalDateTime createTime;
private List<TemperatureHumidityData> dataList;
private Long total;
// 构造函数
public TemperatureHumidityResponse() {}
public TemperatureHumidityResponse(String status, String message) {
this.status = status;
this.message = message;
}
// 从实体创建响应
public static TemperatureHumidityResponse fromEntity(TemperatureHumidityData entity) {
TemperatureHumidityResponse response = new TemperatureHumidityResponse();
response.status = "success";
response.message = "获取成功";
response.temperature = entity.getTemperature();
response.humidity = entity.getHumidity();
response.deviceId = entity.getDeviceId();
response.createTime = entity.getCreateTime();
return response;
}
public static TemperatureHumidityResponse success(String message) {
return new TemperatureHumidityResponse("success", message);
}
public static TemperatureHumidityResponse error(String message) {
return new TemperatureHumidityResponse("error", message);
}
// Getter 和 Setter
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Double getTemperature() {
return temperature;
}
public void setTemperature(Double temperature) {
this.temperature = temperature;
}
public Double getHumidity() {
return humidity;
}
public void setHumidity(Double humidity) {
this.humidity = humidity;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public List<TemperatureHumidityData> getDataList() {
return dataList;
}
public void setDataList(List<TemperatureHumidityData> dataList) {
this.dataList = dataList;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
}

View File

@ -0,0 +1,64 @@
package com.xzzn.movecheck.wsd;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class TimeRangeRequest {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime;
private String deviceId;
// 构造函数
public TimeRangeRequest() {}
public TimeRangeRequest(LocalDateTime startTime, LocalDateTime endTime) {
this.startTime = startTime;
this.endTime = endTime;
}
public TimeRangeRequest(LocalDateTime startTime, LocalDateTime endTime, String deviceId) {
this.startTime = startTime;
this.endTime = endTime;
this.deviceId = deviceId;
}
// Getter 和 Setter
public LocalDateTime getStartTime() {
return startTime;
}
public void setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
}
public LocalDateTime getEndTime() {
return endTime;
}
public void setEndTime(LocalDateTime endTime) {
this.endTime = endTime;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
// 验证时间范围是否有效
public boolean isValid() {
return startTime != null && endTime != null && !startTime.isAfter(endTime);
}
// 获取时间范围描述
public String getTimeRangeDescription() {
return String.format("%s 至 %s", startTime, endTime);
}
}

View File

@ -0,0 +1,23 @@
spring:
datasource:
url: jdbc:mysql://122.51.194.184:13306/movecheck?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: movecheck
password: 8a7c97e5c31c
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
mqtt:
broker: tcp://122.51.194.184:1883
clientId: movecheck-client
topics: HDYDCJ_01_DEVICE,HDYDCJ_01_UP
username: dmbroker
password: qwer1234
qos: 1,1
server:
port: 7999

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- 定义日志文件输出路径 -->
<property name="LOG_HOME" value="./logs"/>
<property name="APP_NAME" value="mqtt-config-api"/>
<!-- 控制台输出格式 -->
<property name="CONSOLE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %logger{36} - %msg%n"/>
<!-- 文件输出格式 -->
<property name="FILE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 信息日志文件 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}-info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/archive/${APP_NAME}-info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 错误日志文件 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/archive/${APP_NAME}-error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- API调用详细日志 -->
<appender name="API_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/${APP_NAME}-api.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/archive/${APP_NAME}-api-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>500MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 特定包级别的日志配置 -->
<logger name="com.xzzn.movecheck.controller" level="INFO" additivity="false">
<appender-ref ref="API_FILE"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ERROR_FILE"/>
</logger>
<logger name="com.xzzn.movecheck" level="INFO" additivity="false">
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ERROR_FILE"/>
</logger>
<!-- Spring框架日志级别 -->
<logger name="org.springframework" level="WARN"/>
<logger name="org.apache" level="WARN"/>
<logger name="com.zaxxer" level="WARN"/>
<!-- 根日志配置 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>