设备管理层详细设计与实现
# 设备管理层详细设计与实现
# 背景故事
想象一下,你是一家智能城市解决方案公司的技术架构师。公司刚刚接到一个大型项目:为整个城市部署10万台各种类型的物联网设备,包括智能路灯、环境监测站、交通摄像头、停车传感器等。
面对如此庞大的设备规模,你意识到需要一个强大的设备管理层来统一管理这些设备。就像管理一个庞大的军队一样,你需要:
- 设备注册:每个新设备都需要"报到"并获得身份证
- 设备认证:确保只有合法设备能够接入系统
- 状态管理:实时监控每个设备的健康状况
- 分组管理:按区域、类型等维度对设备进行分组管理
这就是我们今天要构建的设备管理层的核心功能。
# 1. 设备注册 (Device Registration)
# 1.1 设计原理
设备注册就像是给每个新员工办理入职手续。每个设备都需要提供自己的"简历"(设备信息),系统会为其分配一个唯一的"工号"(设备ID),并建立完整的档案。
# 1.2 核心接口设计
/**
* 设备注册服务接口
* 负责处理设备的注册、注销和信息更新
*/
public interface DeviceRegistrationService {
/**
* 注册新设备
* @param registrationRequest 设备注册请求
* @return 注册结果,包含设备ID和认证信息
*/
DeviceRegistrationResult registerDevice(DeviceRegistrationRequest registrationRequest);
/**
* 注销设备
* @param deviceId 设备ID
* @return 注销结果
*/
boolean unregisterDevice(String deviceId);
/**
* 更新设备信息
* @param deviceId 设备ID
* @param updateRequest 更新请求
* @return 更新结果
*/
boolean updateDeviceInfo(String deviceId, DeviceUpdateRequest updateRequest);
/**
* 查询设备注册信息
* @param deviceId 设备ID
* @return 设备信息
*/
Optional<DeviceInfo> getDeviceInfo(String deviceId);
}
# 1.3 设备注册请求封装
/**
* 设备注册请求
* 包含设备注册所需的所有信息
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceRegistrationRequest {
/**
* 设备序列号(设备的唯一硬件标识)
*/
@NotBlank(message = "设备序列号不能为空")
private String serialNumber;
/**
* 设备类型(如:SENSOR, CAMERA, GATEWAY等)
*/
@NotNull(message = "设备类型不能为空")
private DeviceType deviceType;
/**
* 设备型号
*/
@NotBlank(message = "设备型号不能为空")
private String deviceModel;
/**
* 制造商信息
*/
@NotBlank(message = "制造商不能为空")
private String manufacturer;
/**
* 固件版本
*/
private String firmwareVersion;
/**
* 硬件版本
*/
private String hardwareVersion;
/**
* 设备位置信息
*/
private DeviceLocation location;
/**
* 设备能力描述(支持的协议、功能等)
*/
private DeviceCapabilities capabilities;
/**
* 设备标签(用于分组和查询)
*/
private Map<String, String> tags;
/**
* 注册时间戳
*/
private LocalDateTime registrationTime;
}
# 1.4 设备信息模型
/**
* 设备信息实体
* 存储设备的完整信息
*/
@Entity
@Table(name = "device_info")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceInfo {
/**
* 设备唯一ID(系统生成)
*/
@Id
@Column(name = "device_id", length = 64)
private String deviceId;
/**
* 设备序列号(设备硬件标识)
*/
@Column(name = "serial_number", unique = true, nullable = false)
private String serialNumber;
/**
* 设备类型
*/
@Enumerated(EnumType.STRING)
@Column(name = "device_type", nullable = false)
private DeviceType deviceType;
/**
* 设备型号
*/
@Column(name = "device_model", nullable = false)
private String deviceModel;
/**
* 制造商
*/
@Column(name = "manufacturer", nullable = false)
private String manufacturer;
/**
* 固件版本
*/
@Column(name = "firmware_version")
private String firmwareVersion;
/**
* 硬件版本
*/
@Column(name = "hardware_version")
private String hardwareVersion;
/**
* 设备状态
*/
@Enumerated(EnumType.STRING)
@Column(name = "device_status", nullable = false)
private DeviceStatus deviceStatus;
/**
* 设备位置信息(JSON格式存储)
*/
@Column(name = "location", columnDefinition = "JSON")
private String location;
/**
* 设备能力(JSON格式存储)
*/
@Column(name = "capabilities", columnDefinition = "JSON")
private String capabilities;
/**
* 设备标签(JSON格式存储)
*/
@Column(name = "tags", columnDefinition = "JSON")
private String tags;
/**
* 注册时间
*/
@Column(name = "registration_time", nullable = false)
private LocalDateTime registrationTime;
/**
* 最后更新时间
*/
@Column(name = "last_updated_time")
private LocalDateTime lastUpdatedTime;
/**
* 创建时间
*/
@CreationTimestamp
@Column(name = "created_time", nullable = false)
private LocalDateTime createdTime;
/**
* 更新时间
*/
@UpdateTimestamp
@Column(name = "updated_time", nullable = false)
private LocalDateTime updatedTime;
}
# 1.5 设备注册服务实现
/**
* 设备注册服务实现
* 提供完整的设备注册功能
*/
@Service
@Slf4j
@Transactional
public class DeviceRegistrationServiceImpl implements DeviceRegistrationService {
@Autowired
private DeviceInfoRepository deviceInfoRepository;
@Autowired
private DeviceIdGenerator deviceIdGenerator;
@Autowired
private DeviceValidator deviceValidator;
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 注册新设备
* 实现设备注册的完整流程
*/
@Override
public DeviceRegistrationResult registerDevice(DeviceRegistrationRequest request) {
log.info("开始注册设备,序列号: {}, 类型: {}", request.getSerialNumber(), request.getDeviceType());
try {
// 1. 验证设备信息
validateDeviceRegistration(request);
// 2. 检查设备是否已注册
if (deviceInfoRepository.existsBySerialNumber(request.getSerialNumber())) {
throw new DeviceAlreadyRegisteredException(
"设备已注册,序列号: " + request.getSerialNumber());
}
// 3. 生成设备ID
String deviceId = deviceIdGenerator.generateDeviceId(request.getDeviceType());
// 4. 构建设备信息
DeviceInfo deviceInfo = buildDeviceInfo(deviceId, request);
// 5. 保存设备信息
deviceInfo = deviceInfoRepository.save(deviceInfo);
// 6. 生成认证凭据
DeviceCredentials credentials = generateDeviceCredentials(deviceId);
// 7. 发布设备注册事件
publishDeviceRegisteredEvent(deviceInfo);
// 8. 构建注册结果
DeviceRegistrationResult result = DeviceRegistrationResult.builder()
.deviceId(deviceId)
.credentials(credentials)
.registrationTime(LocalDateTime.now())
.success(true)
.message("设备注册成功")
.build();
log.info("设备注册成功,设备ID: {}, 序列号: {}", deviceId, request.getSerialNumber());
return result;
} catch (Exception e) {
log.error("设备注册失败,序列号: {}", request.getSerialNumber(), e);
return DeviceRegistrationResult.builder()
.success(false)
.message("设备注册失败: " + e.getMessage())
.build();
}
}
/**
* 验证设备注册信息
*/
private void validateDeviceRegistration(DeviceRegistrationRequest request) {
// 验证设备序列号格式
if (!deviceValidator.isValidSerialNumber(request.getSerialNumber())) {
throw new InvalidDeviceInfoException("无效的设备序列号格式");
}
// 验证设备类型是否支持
if (!deviceValidator.isSupportedDeviceType(request.getDeviceType())) {
throw new UnsupportedDeviceTypeException("不支持的设备类型: " + request.getDeviceType());
}
// 验证制造商是否在白名单中
if (!deviceValidator.isApprovedManufacturer(request.getManufacturer())) {
throw new UnauthorizedManufacturerException("未授权的制造商: " + request.getManufacturer());
}
}
/**
* 构建设备信息实体
*/
private DeviceInfo buildDeviceInfo(String deviceId, DeviceRegistrationRequest request) {
return DeviceInfo.builder()
.deviceId(deviceId)
.serialNumber(request.getSerialNumber())
.deviceType(request.getDeviceType())
.deviceModel(request.getDeviceModel())
.manufacturer(request.getManufacturer())
.firmwareVersion(request.getFirmwareVersion())
.hardwareVersion(request.getHardwareVersion())
.deviceStatus(DeviceStatus.REGISTERED)
.location(JsonUtils.toJson(request.getLocation()))
.capabilities(JsonUtils.toJson(request.getCapabilities()))
.tags(JsonUtils.toJson(request.getTags()))
.registrationTime(LocalDateTime.now())
.build();
}
/**
* 生成设备认证凭据
*/
private DeviceCredentials generateDeviceCredentials(String deviceId) {
// 生成设备证书和密钥
String certificate = certificateGenerator.generateCertificate(deviceId);
String privateKey = certificateGenerator.generatePrivateKey(deviceId);
return DeviceCredentials.builder()
.deviceId(deviceId)
.certificate(certificate)
.privateKey(privateKey)
.expirationTime(LocalDateTime.now().plusYears(1))
.build();
}
/**
* 发布设备注册事件
*/
private void publishDeviceRegisteredEvent(DeviceInfo deviceInfo) {
DeviceRegisteredEvent event = new DeviceRegisteredEvent(
deviceInfo.getDeviceId(),
deviceInfo.getSerialNumber(),
deviceInfo.getDeviceType(),
LocalDateTime.now()
);
eventPublisher.publishEvent(event);
}
}
# 1.6 设备ID生成器
/**
* 设备ID生成器
* 生成全局唯一的设备标识符
*/
@Component
@Slf4j
public class DeviceIdGenerator {
private static final String DEVICE_ID_PREFIX = "DEV";
private static final Map<DeviceType, String> TYPE_PREFIXES = Map.of(
DeviceType.SENSOR, "SEN",
DeviceType.CAMERA, "CAM",
DeviceType.GATEWAY, "GW",
DeviceType.ACTUATOR, "ACT"
);
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 生成设备ID
* 格式: DEV-{TYPE}-{TIMESTAMP}-{SEQUENCE}
* 例如: DEV-SEN-20240101-000001
*/
public String generateDeviceId(DeviceType deviceType) {
String typePrefix = TYPE_PREFIXES.getOrDefault(deviceType, "UNK");
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
// 使用Redis生成序列号,确保唯一性
String sequenceKey = String.format("device:sequence:%s:%s", typePrefix, timestamp);
Long sequence = redisTemplate.opsForValue().increment(sequenceKey);
// 设置序列号过期时间(第二天凌晨)
LocalDateTime tomorrow = LocalDate.now().plusDays(1).atStartOfDay();
Duration expiration = Duration.between(LocalDateTime.now(), tomorrow);
redisTemplate.expire(sequenceKey, expiration);
String deviceId = String.format("%s-%s-%s-%06d",
DEVICE_ID_PREFIX, typePrefix, timestamp, sequence);
log.debug("生成设备ID: {}", deviceId);
return deviceId;
}
}
# 2. 设备认证 (Device Authentication)
# 2.1 设计原理
设备认证就像是公司的门禁系统。每个设备都需要出示自己的"工作证"(认证凭据),门禁系统会验证证件的真伪,确认身份后才允许进入。我们支持多种认证方式:证书认证、令牌认证、签名认证等。
# 2.2 认证服务接口
/**
* 设备认证服务接口
* 提供多种设备认证方式
*/
public interface DeviceAuthenticationService {
/**
* 证书认证
* @param certificateAuthRequest 证书认证请求
* @return 认证结果
*/
AuthenticationResult authenticateWithCertificate(CertificateAuthRequest certificateAuthRequest);
/**
* 令牌认证
* @param tokenAuthRequest 令牌认证请求
* @return 认证结果
*/
AuthenticationResult authenticateWithToken(TokenAuthRequest tokenAuthRequest);
/**
* 签名认证
* @param signatureAuthRequest 签名认证请求
* @return 认证结果
*/
AuthenticationResult authenticateWithSignature(SignatureAuthRequest signatureAuthRequest);
/**
* 刷新认证令牌
* @param refreshTokenRequest 刷新令牌请求
* @return 新的认证令牌
*/
AuthenticationResult refreshToken(RefreshTokenRequest refreshTokenRequest);
/**
* 撤销设备认证
* @param deviceId 设备ID
* @return 撤销结果
*/
boolean revokeAuthentication(String deviceId);
}
### 2.3 认证请求封装
```java
/**
* 证书认证请求
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CertificateAuthRequest {
/**
* 设备ID
*/
@NotBlank(message = "设备ID不能为空")
private String deviceId;
/**
* 设备证书(PEM格式)
*/
@NotBlank(message = "设备证书不能为空")
private String certificate;
/**
* 挑战字符串(防重放攻击)
*/
@NotBlank(message = "挑战字符串不能为空")
private String challenge;
/**
* 签名(使用私钥对挑战字符串的签名)
*/
@NotBlank(message = "签名不能为空")
private String signature;
/**
* 认证时间戳
*/
private LocalDateTime timestamp;
}
/**
* 令牌认证请求
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TokenAuthRequest {
/**
* 设备ID
*/
@NotBlank(message = "设备ID不能为空")
private String deviceId;
/**
* 访问令牌
*/
@NotBlank(message = "访问令牌不能为空")
private String accessToken;
/**
* 令牌类型(Bearer、Basic等)
*/
private String tokenType;
/**
* 认证时间戳
*/
private LocalDateTime timestamp;
}
# 2.4 认证结果封装
/**
* 认证结果
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthenticationResult {
/**
* 认证是否成功
*/
private boolean success;
/**
* 设备ID
*/
private String deviceId;
/**
* 访问令牌
*/
private String accessToken;
/**
* 刷新令牌
*/
private String refreshToken;
/**
* 令牌过期时间
*/
private LocalDateTime expirationTime;
/**
* 认证级别(LOW、MEDIUM、HIGH)
*/
private AuthenticationLevel authLevel;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 认证失败原因
*/
private String failureReason;
/**
* 认证时间
*/
private LocalDateTime authTime;
}
# 2.5 设备认证服务实现
/**
* 设备认证服务实现
* 提供多种认证方式的完整实现
*/
@Service
@Slf4j
public class DeviceAuthenticationServiceImpl implements DeviceAuthenticationService {
@Autowired
private DeviceInfoRepository deviceInfoRepository;
@Autowired
private DeviceCredentialsRepository credentialsRepository;
@Autowired
private CertificateValidator certificateValidator;
@Autowired
private TokenManager tokenManager;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 证书认证实现
* 这是最安全的认证方式,类似于使用身份证+指纹验证
*/
@Override
public AuthenticationResult authenticateWithCertificate(CertificateAuthRequest request) {
log.info("开始证书认证,设备ID: {}", request.getDeviceId());
try {
// 1. 验证设备是否存在且状态正常
DeviceInfo deviceInfo = validateDeviceStatus(request.getDeviceId());
// 2. 获取设备存储的证书
DeviceCredentials credentials = credentialsRepository.findByDeviceId(request.getDeviceId())
.orElseThrow(() -> new DeviceCredentialsNotFoundException("设备认证凭据不存在"));
// 3. 验证证书有效性
if (!certificateValidator.validateCertificate(request.getCertificate(), credentials.getCertificate())) {
return buildFailureResult(request.getDeviceId(), "证书验证失败");
}
// 4. 验证签名(防止重放攻击)
if (!certificateValidator.validateSignature(request.getChallenge(), request.getSignature(), request.getCertificate())) {
return buildFailureResult(request.getDeviceId(), "签名验证失败");
}
// 5. 检查认证频率限制
if (!checkRateLimit(request.getDeviceId())) {
return buildFailureResult(request.getDeviceId(), "认证频率过高,请稍后重试");
}
// 6. 生成访问令牌
String accessToken = tokenManager.generateAccessToken(request.getDeviceId(), AuthenticationLevel.HIGH);
String refreshToken = tokenManager.generateRefreshToken(request.getDeviceId());
// 7. 记录认证成功
recordSuccessfulAuth(request.getDeviceId(), AuthenticationType.CERTIFICATE);
// 8. 构建成功结果
return AuthenticationResult.builder()
.success(true)
.deviceId(request.getDeviceId())
.accessToken(accessToken)
.refreshToken(refreshToken)
.expirationTime(LocalDateTime.now().plusHours(24))
.authLevel(AuthenticationLevel.HIGH)
.permissions(getDevicePermissions(deviceInfo))
.authTime(LocalDateTime.now())
.build();
} catch (Exception e) {
log.error("证书认证失败,设备ID: {}", request.getDeviceId(), e);
recordFailedAuth(request.getDeviceId(), AuthenticationType.CERTIFICATE, e.getMessage());
return buildFailureResult(request.getDeviceId(), e.getMessage());
}
}
/**
* 令牌认证实现
* 适用于已经通过初始认证的设备的后续访问
*/
@Override
public AuthenticationResult authenticateWithToken(TokenAuthRequest request) {
log.debug("开始令牌认证,设备ID: {}", request.getDeviceId());
try {
// 1. 验证令牌格式和有效性
if (!tokenManager.validateToken(request.getAccessToken())) {
return buildFailureResult(request.getDeviceId(), "无效的访问令牌");
}
// 2. 从令牌中提取设备信息
String tokenDeviceId = tokenManager.extractDeviceId(request.getAccessToken());
if (!request.getDeviceId().equals(tokenDeviceId)) {
return buildFailureResult(request.getDeviceId(), "令牌与设备ID不匹配");
}
// 3. 检查令牌是否过期
if (tokenManager.isTokenExpired(request.getAccessToken())) {
return buildFailureResult(request.getDeviceId(), "访问令牌已过期");
}
// 4. 检查令牌是否被撤销
if (isTokenRevoked(request.getAccessToken())) {
return buildFailureResult(request.getDeviceId(), "访问令牌已被撤销");
}
// 5. 验证设备状态
DeviceInfo deviceInfo = validateDeviceStatus(request.getDeviceId());
// 6. 更新令牌使用记录
updateTokenUsage(request.getAccessToken());
// 7. 构建成功结果
return AuthenticationResult.builder()
.success(true)
.deviceId(request.getDeviceId())
.accessToken(request.getAccessToken())
.expirationTime(tokenManager.getTokenExpiration(request.getAccessToken()))
.authLevel(tokenManager.getTokenAuthLevel(request.getAccessToken()))
.permissions(getDevicePermissions(deviceInfo))
.authTime(LocalDateTime.now())
.build();
} catch (Exception e) {
log.error("令牌认证失败,设备ID: {}", request.getDeviceId(), e);
return buildFailureResult(request.getDeviceId(), e.getMessage());
}
}
/**
* 验证设备状态
*/
private DeviceInfo validateDeviceStatus(String deviceId) {
DeviceInfo deviceInfo = deviceInfoRepository.findByDeviceId(deviceId)
.orElseThrow(() -> new DeviceNotFoundException("设备不存在: " + deviceId));
if (deviceInfo.getDeviceStatus() != DeviceStatus.ACTIVE) {
throw new DeviceInactiveException("设备状态异常: " + deviceInfo.getDeviceStatus());
}
return deviceInfo;
}
/**
* 检查认证频率限制
* 防止暴力破解攻击
*/
private boolean checkRateLimit(String deviceId) {
String rateLimitKey = "auth:rate_limit:" + deviceId;
String currentCount = redisTemplate.opsForValue().get(rateLimitKey);
if (currentCount == null) {
// 第一次认证,设置计数器
redisTemplate.opsForValue().set(rateLimitKey, "1", Duration.ofMinutes(5));
return true;
}
int count = Integer.parseInt(currentCount);
if (count >= 10) { // 5分钟内最多10次认证尝试
return false;
}
redisTemplate.opsForValue().increment(rateLimitKey);
return true;
}
/**
* 构建认证失败结果
*/
private AuthenticationResult buildFailureResult(String deviceId, String reason) {
return AuthenticationResult.builder()
.success(false)
.deviceId(deviceId)
.failureReason(reason)
.authTime(LocalDateTime.now())
.build();
}
}
# 3. 设备状态管理 (Device Status Management)
# 3.1 设计原理
设备状态管理就像是医院的健康监控系统。每个设备都有自己的"健康档案",系统会实时监控设备的各项"生命体征"(CPU使用率、内存占用、网络连接状态等),一旦发现异常就会立即"报警"并采取相应的"治疗"措施。
# 3.2 设备状态枚举
/**
* 设备状态枚举
* 定义设备的各种状态
*/
public enum DeviceStatus {
/**
* 已注册 - 设备已完成注册但尚未激活
*/
REGISTERED("已注册", "设备已完成注册流程"),
/**
* 活跃 - 设备正常工作中
*/
ACTIVE("活跃", "设备正常工作中"),
/**
* 离线 - 设备失去连接
*/
OFFLINE("离线", "设备失去网络连接"),
/**
* 故障 - 设备出现故障
*/
FAULTY("故障", "设备出现硬件或软件故障"),
/**
* 维护中 - 设备正在维护
*/
MAINTENANCE("维护中", "设备正在进行维护操作"),
/**
* 已停用 - 设备被管理员停用
*/
DISABLED("已停用", "设备被管理员停用"),
/**
* 已注销 - 设备已从系统中移除
*/
DEREGISTERED("已注销", "设备已从系统中注销");
private final String displayName;
private final String description;
DeviceStatus(String displayName, String description) {
this.displayName = displayName;
this.description = description;
}
public String getDisplayName() {
return displayName;
}
public String getDescription() {
return description;
}
}
# 3.3 设备状态管理服务接口
/**
* 设备状态管理服务接口
* 提供设备状态的查询、更新和监控功能
*/
public interface DeviceStatusService {
/**
* 更新设备状态
* @param deviceId 设备ID
* @param newStatus 新状态
* @param reason 状态变更原因
* @return 更新结果
*/
boolean updateDeviceStatus(String deviceId, DeviceStatus newStatus, String reason);
/**
* 获取设备当前状态
* @param deviceId 设备ID
* @return 设备状态信息
*/
Optional<DeviceStatusInfo> getDeviceStatus(String deviceId);
/**
* 批量获取设备状态
* @param deviceIds 设备ID列表
* @return 设备状态映射
*/
Map<String, DeviceStatusInfo> getBatchDeviceStatus(List<String> deviceIds);
/**
* 获取设备状态历史
* @param deviceId 设备ID
* @param startTime 开始时间
* @param endTime 结束时间
* @return 状态历史列表
*/
List<DeviceStatusHistory> getDeviceStatusHistory(String deviceId, LocalDateTime startTime, LocalDateTime endTime);
/**
* 设备心跳上报
* @param heartbeatRequest 心跳请求
* @return 心跳响应
*/
HeartbeatResponse reportHeartbeat(HeartbeatRequest heartbeatRequest);
/**
* 获取异常设备列表
* @param statusFilter 状态过滤器
* @return 异常设备列表
*/
List<DeviceStatusInfo> getAbnormalDevices(Set<DeviceStatus> statusFilter);
}
# 3.4 设备状态信息封装
/**
* 设备状态信息
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceStatusInfo {
/**
* 设备ID
*/
private String deviceId;
/**
* 当前状态
*/
private DeviceStatus currentStatus;
/**
* 上一个状态
*/
private DeviceStatus previousStatus;
/**
* 状态变更时间
*/
private LocalDateTime statusChangeTime;
/**
* 状态变更原因
*/
private String changeReason;
/**
* 最后心跳时间
*/
private LocalDateTime lastHeartbeatTime;
/**
* 设备健康度评分(0-100)
*/
private Integer healthScore;
/**
* 设备性能指标
*/
private DeviceMetrics metrics;
/**
* 在线时长(秒)
*/
private Long onlineDuration;
}
/**
* 设备性能指标
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceMetrics {
/**
* CPU使用率(百分比)
*/
private Double cpuUsage;
/**
* 内存使用率(百分比)
*/
private Double memoryUsage;
/**
* 磁盘使用率(百分比)
*/
private Double diskUsage;
/**
* 网络延迟(毫秒)
*/
private Long networkLatency;
/**
* 信号强度(dBm)
*/
private Integer signalStrength;
/**
* 电池电量(百分比)
*/
private Integer batteryLevel;
/**
* 温度(摄氏度)
*/
private Double temperature;
/**
* 指标采集时间
*/
private LocalDateTime collectionTime;
}
# 3.5 心跳机制实现
/**
* 设备心跳请求
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HeartbeatRequest {
/**
* 设备ID
*/
@NotBlank(message = "设备ID不能为空")
private String deviceId;
/**
* 心跳时间戳
*/
private LocalDateTime timestamp;
/**
* 设备当前状态
*/
private DeviceStatus deviceStatus;
/**
* 设备性能指标
*/
private DeviceMetrics metrics;
/**
* 设备位置信息(如果支持GPS)
*/
private DeviceLocation location;
/**
* 错误信息(如果有)
*/
private List<String> errors;
/**
* 心跳序列号(用于检测丢包)
*/
private Long sequenceNumber;
}
/**
* 设备心跳响应
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HeartbeatResponse {
/**
* 响应是否成功
*/
private boolean success;
/**
* 服务器时间戳
*/
private LocalDateTime serverTimestamp;
/**
* 下次心跳间隔(秒)
*/
private Integer nextHeartbeatInterval;
/**
* 服务器指令(如重启、更新配置等)
*/
private List<DeviceCommand> commands;
/**
* 配置更新(如果有)
*/
private Map<String, Object> configUpdates;
/**
* 响应消息
*/
private String message;
}
# 3.6 设备状态服务实现
/**
* 设备状态管理服务实现
* 提供完整的设备状态监控和管理功能
*/
@Service
@Slf4j
public class DeviceStatusServiceImpl implements DeviceStatusService {
@Autowired
private DeviceInfoRepository deviceInfoRepository;
@Autowired
private DeviceStatusHistoryRepository statusHistoryRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private DeviceCommandService commandService;
// 心跳超时时间(秒)
private static final int HEARTBEAT_TIMEOUT = 300;
/**
* 设备心跳上报处理
* 这是设备状态管理的核心方法,类似于医生检查病人的生命体征
*/
@Override
public HeartbeatResponse reportHeartbeat(HeartbeatRequest request) {
log.debug("收到设备心跳,设备ID: {}", request.getDeviceId());
try {
// 1. 验证设备是否存在
DeviceInfo deviceInfo = deviceInfoRepository.findByDeviceId(request.getDeviceId())
.orElseThrow(() -> new DeviceNotFoundException("设备不存在: " + request.getDeviceId()));
// 2. 更新设备最后心跳时间
updateLastHeartbeatTime(request.getDeviceId(), request.getTimestamp());
// 3. 分析设备健康状况
DeviceHealthAnalysis healthAnalysis = analyzeDeviceHealth(request);
// 4. 更新设备状态(如果需要)
updateDeviceStatusIfNeeded(request.getDeviceId(), healthAnalysis);
// 5. 存储设备指标
storeDeviceMetrics(request.getDeviceId(), request.getMetrics());
// 6. 检查是否有待执行的命令
List<DeviceCommand> pendingCommands = commandService.getPendingCommands(request.getDeviceId());
// 7. 计算下次心跳间隔
int nextInterval = calculateNextHeartbeatInterval(healthAnalysis);
// 8. 构建响应
HeartbeatResponse response = HeartbeatResponse.builder()
.success(true)
.serverTimestamp(LocalDateTime.now())
.nextHeartbeatInterval(nextInterval)
.commands(pendingCommands)
.message("心跳处理成功")
.build();
// 9. 发布心跳事件
publishHeartbeatEvent(request);
return response;
} catch (Exception e) {
log.error("处理设备心跳失败,设备ID: {}", request.getDeviceId(), e);
return HeartbeatResponse.builder()
.success(false)
.serverTimestamp(LocalDateTime.now())
.nextHeartbeatInterval(60) // 出错时缩短心跳间隔
.message("心跳处理失败: " + e.getMessage())
.build();
}
}
/**
* 分析设备健康状况
* 类似于医生根据各项指标判断病人的健康状况
*/
private DeviceHealthAnalysis analyzeDeviceHealth(HeartbeatRequest request) {
DeviceMetrics metrics = request.getMetrics();
int healthScore = 100; // 初始健康分数
List<String> issues = new ArrayList<>();
DeviceStatus recommendedStatus = DeviceStatus.ACTIVE;
if (metrics != null) {
// CPU使用率检查
if (metrics.getCpuUsage() != null && metrics.getCpuUsage() > 90) {
healthScore -= 20;
issues.add("CPU使用率过高: " + metrics.getCpuUsage() + "%");
}
// 内存使用率检查
if (metrics.getMemoryUsage() != null && metrics.getMemoryUsage() > 85) {
healthScore -= 15;
issues.add("内存使用率过高: " + metrics.getMemoryUsage() + "%");
}
// 电池电量检查
if (metrics.getBatteryLevel() != null && metrics.getBatteryLevel() < 10) {
healthScore -= 30;
issues.add("电池电量过低: " + metrics.getBatteryLevel() + "%");
if (metrics.getBatteryLevel() < 5) {
recommendedStatus = DeviceStatus.FAULTY;
}
}
// 温度检查
if (metrics.getTemperature() != null) {
if (metrics.getTemperature() > 70 || metrics.getTemperature() < -10) {
healthScore -= 25;
issues.add("设备温度异常: " + metrics.getTemperature() + "°C");
if (metrics.getTemperature() > 85) {
recommendedStatus = DeviceStatus.FAULTY;
}
}
}
// 网络延迟检查
if (metrics.getNetworkLatency() != null && metrics.getNetworkLatency() > 1000) {
healthScore -= 10;
issues.add("网络延迟过高: " + metrics.getNetworkLatency() + "ms");
}
}
// 检查错误信息
if (request.getErrors() != null && !request.getErrors().isEmpty()) {
healthScore -= request.getErrors().size() * 10;
issues.addAll(request.getErrors());
if (request.getErrors().size() > 3) {
recommendedStatus = DeviceStatus.FAULTY;
}
}
// 确保健康分数不低于0
healthScore = Math.max(0, healthScore);
return DeviceHealthAnalysis.builder()
.healthScore(healthScore)
.issues(issues)
.recommendedStatus(recommendedStatus)
.analysisTime(LocalDateTime.now())
.build();
}
/**
* 更新设备状态(如果需要)
*/
private void updateDeviceStatusIfNeeded(String deviceId, DeviceHealthAnalysis analysis) {
DeviceInfo currentDevice = deviceInfoRepository.findByDeviceId(deviceId).orElse(null);
if (currentDevice == null) return;
DeviceStatus currentStatus = currentDevice.getDeviceStatus();
DeviceStatus recommendedStatus = analysis.getRecommendedStatus();
// 如果推荐状态与当前状态不同,且健康分数较低,则更新状态
if (!currentStatus.equals(recommendedStatus) && analysis.getHealthScore() < 60) {
String reason = "健康检查异常,健康分数: " + analysis.getHealthScore() +
", 问题: " + String.join(", ", analysis.getIssues());
updateDeviceStatus(deviceId, recommendedStatus, reason);
}
}
/**
* 更新设备状态
*/
@Override
@Transactional
public boolean updateDeviceStatus(String deviceId, DeviceStatus newStatus, String reason) {
try {
DeviceInfo deviceInfo = deviceInfoRepository.findByDeviceId(deviceId)
.orElseThrow(() -> new DeviceNotFoundException("设备不存在: " + deviceId));
DeviceStatus oldStatus = deviceInfo.getDeviceStatus();
// 如果状态没有变化,直接返回
if (oldStatus.equals(newStatus)) {
return true;
}
// 更新设备状态
deviceInfo.setDeviceStatus(newStatus);
deviceInfo.setLastUpdatedTime(LocalDateTime.now());
deviceInfoRepository.save(deviceInfo);
// 记录状态变更历史
DeviceStatusHistory history = DeviceStatusHistory.builder()
.deviceId(deviceId)
.previousStatus(oldStatus)
.newStatus(newStatus)
.changeReason(reason)
.changeTime(LocalDateTime.now())
.build();
statusHistoryRepository.save(history);
// 发布状态变更事件
publishStatusChangeEvent(deviceId, oldStatus, newStatus, reason);
log.info("设备状态更新成功,设备ID: {}, {} -> {}, 原因: {}",
deviceId, oldStatus, newStatus, reason);
return true;
} catch (Exception e) {
log.error("更新设备状态失败,设备ID: {}", deviceId, e);
return false;
}
}
/**
* 定时检查离线设备
* 类似于医院的巡房制度,定期检查病人状况
*/
@Scheduled(fixedRate = 60000) // 每分钟执行一次
public void checkOfflineDevices() {
log.debug("开始检查离线设备");
try {
LocalDateTime cutoffTime = LocalDateTime.now().minusSeconds(HEARTBEAT_TIMEOUT);
// 查询所有活跃设备
List<DeviceInfo> activeDevices = deviceInfoRepository.findByDeviceStatus(DeviceStatus.ACTIVE);
for (DeviceInfo device : activeDevices) {
// 检查最后心跳时间
LocalDateTime lastHeartbeat = getLastHeartbeatTime(device.getDeviceId());
if (lastHeartbeat != null && lastHeartbeat.isBefore(cutoffTime)) {
// 设备超时,标记为离线
updateDeviceStatus(device.getDeviceId(), DeviceStatus.OFFLINE,
"心跳超时,最后心跳时间: " + lastHeartbeat);
log.warn("设备离线,设备ID: {}, 最后心跳: {}", device.getDeviceId(), lastHeartbeat);
}
}
} catch (Exception e) {
log.error("检查离线设备失败", e);
}
}
/**
* 获取最后心跳时间
*/
private LocalDateTime getLastHeartbeatTime(String deviceId) {
String key = "device:heartbeat:" + deviceId;
Object timestamp = redisTemplate.opsForValue().get(key);
if (timestamp instanceof String) {
return LocalDateTime.parse((String) timestamp);
}
return null;
}
/**
* 更新最后心跳时间
*/
private void updateLastHeartbeatTime(String deviceId, LocalDateTime timestamp) {
String key = "device:heartbeat:" + deviceId;
redisTemplate.opsForValue().set(key, timestamp.toString(), Duration.ofSeconds(HEARTBEAT_TIMEOUT * 2));
}
}
# 4. 设备分组管理 (Device Group Management)
# 4.1 设计原理
设备分组管理就像是学校的班级管理系统。我们可以按照不同的维度(年级、专业、地区等)将学生分到不同的班级中,每个班级有自己的班主任(管理员)、课程表(配置)和规章制度(策略)。同样,我们可以按照设备类型、地理位置、功能等维度对设备进行分组管理。
# 4.2 设备分组服务接口
/**
* 设备分组管理服务接口
* 提供设备分组的创建、管理和查询功能
*/
public interface DeviceGroupService {
/**
* 创建设备分组
* @param createRequest 创建分组请求
* @return 分组信息
*/
DeviceGroup createGroup(CreateGroupRequest createRequest);
/**
* 更新分组信息
* @param groupId 分组ID
* @param updateRequest 更新请求
* @return 更新结果
*/
boolean updateGroup(String groupId, UpdateGroupRequest updateRequest);
/**
* 删除分组
* @param groupId 分组ID
* @return 删除结果
*/
boolean deleteGroup(String groupId);
/**
* 添加设备到分组
* @param groupId 分组ID
* @param deviceIds 设备ID列表
* @return 添加结果
*/
boolean addDevicesToGroup(String groupId, List<String> deviceIds);
/**
* 从分组中移除设备
* @param groupId 分组ID
* @param deviceIds 设备ID列表
* @return 移除结果
*/
boolean removeDevicesFromGroup(String groupId, List<String> deviceIds);
/**
* 获取分组信息
* @param groupId 分组ID
* @return 分组信息
*/
Optional<DeviceGroup> getGroup(String groupId);
/**
* 获取分组下的设备列表
* @param groupId 分组ID
* @param pageable 分页参数
* @return 设备列表
*/
Page<DeviceInfo> getGroupDevices(String groupId, Pageable pageable);
/**
* 根据条件查询分组
* @param queryRequest 查询请求
* @return 分组列表
*/
List<DeviceGroup> queryGroups(GroupQueryRequest queryRequest);
/**
* 获取设备所属的分组
* @param deviceId 设备ID
* @return 分组列表
*/
List<DeviceGroup> getDeviceGroups(String deviceId);
/**
* 批量操作分组设备
* @param groupId 分组ID
* @param operation 操作类型
* @param parameters 操作参数
* @return 操作结果
*/
BatchOperationResult batchOperateGroupDevices(String groupId, GroupOperation operation, Map<String, Object> parameters);
}
# 4.3 设备分组实体模型
/**
* 设备分组实体
*/
@Entity
@Table(name = "device_group")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceGroup {
/**
* 分组ID
*/
@Id
@Column(name = "group_id", length = 64)
private String groupId;
/**
* 分组名称
*/
@Column(name = "group_name", nullable = false)
private String groupName;
/**
* 分组描述
*/
@Column(name = "description")
private String description;
/**
* 分组类型
*/
@Enumerated(EnumType.STRING)
@Column(name = "group_type", nullable = false)
private GroupType groupType;
/**
* 父分组ID(支持层级结构)
*/
@Column(name = "parent_group_id")
private String parentGroupId;
/**
* 分组路径(如:/root/region/building)
*/
@Column(name = "group_path")
private String groupPath;
/**
* 分组级别(0为根级别)
*/
@Column(name = "group_level")
private Integer groupLevel;
/**
* 分组标签(JSON格式)
*/
@Column(name = "tags", columnDefinition = "JSON")
private String tags;
/**
* 分组配置(JSON格式)
*/
@Column(name = "group_config", columnDefinition = "JSON")
private String groupConfig;
/**
* 分组策略(JSON格式)
*/
@Column(name = "group_policies", columnDefinition = "JSON")
private String groupPolicies;
/**
* 设备数量
*/
@Column(name = "device_count")
private Integer deviceCount;
/**
* 分组状态
*/
@Enumerated(EnumType.STRING)
@Column(name = "group_status", nullable = false)
private GroupStatus groupStatus;
/**
* 创建者ID
*/
@Column(name = "creator_id")
private String creatorId;
/**
* 管理员列表(JSON格式)
*/
@Column(name = "administrators", columnDefinition = "JSON")
private String administrators;
/**
* 创建时间
*/
@CreationTimestamp
@Column(name = "created_time", nullable = false)
private LocalDateTime createdTime;
/**
* 更新时间
*/
@UpdateTimestamp
@Column(name = "updated_time", nullable = false)
private LocalDateTime updatedTime;
}
/**
* 分组类型枚举
*/
public enum GroupType {
/**
* 地理位置分组(按区域、建筑物等)
*/
GEOGRAPHIC("地理位置", "按地理位置划分的分组"),
/**
* 设备类型分组(按传感器、摄像头等)
*/
DEVICE_TYPE("设备类型", "按设备类型划分的分组"),
/**
* 功能分组(按监控、控制等功能)
*/
FUNCTIONAL("功能分组", "按功能划分的分组"),
/**
* 业务分组(按业务线、部门等)
*/
BUSINESS("业务分组", "按业务划分的分组"),
/**
* 自定义分组
*/
CUSTOM("自定义", "用户自定义的分组");
private final String displayName;
private final String description;
GroupType(String displayName, String description) {
this.displayName = displayName;
this.description = description;
}
public String getDisplayName() {
return displayName;
}
public String getDescription() {
return description;
}
}
/**
* 分组状态枚举
*/
public enum GroupStatus {
ACTIVE("活跃"),
INACTIVE("非活跃"),
ARCHIVED("已归档");
private final String displayName;
GroupStatus(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
# 4.4 设备分组关系管理
/**
* 设备分组关系实体
* 管理设备与分组的多对多关系
*/
@Entity
@Table(name = "device_group_relation")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeviceGroupRelation {
/**
* 关系ID
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 设备ID
*/
@Column(name = "device_id", nullable = false)
private String deviceId;
/**
* 分组ID
*/
@Column(name = "group_id", nullable = false)
private String groupId;
/**
* 关系类型(主分组、次分组等)
*/
@Enumerated(EnumType.STRING)
@Column(name = "relation_type")
private RelationType relationType;
/**
* 加入时间
*/
@Column(name = "joined_time", nullable = false)
private LocalDateTime joinedTime;
/**
* 操作者ID
*/
@Column(name = "operator_id")
private String operatorId;
/**
* 备注
*/
@Column(name = "remarks")
private String remarks;
}
/**
* 关系类型枚举
*/
public enum RelationType {
PRIMARY("主分组"),
SECONDARY("次分组"),
TEMPORARY("临时分组");
private final String displayName;
RelationType(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
# 4.5 设备分组服务实现
/**
* 设备分组管理服务实现
* 提供完整的分组管理功能
*/
@Service
@Slf4j
@Transactional
public class DeviceGroupServiceImpl implements DeviceGroupService {
@Autowired
private DeviceGroupRepository groupRepository;
@Autowired
private DeviceGroupRelationRepository relationRepository;
@Autowired
private DeviceInfoRepository deviceInfoRepository;
@Autowired
private GroupIdGenerator groupIdGenerator;
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 创建设备分组
* 就像创建一个新的班级,需要指定班级名称、类型、管理员等信息
*/
@Override
public DeviceGroup createGroup(CreateGroupRequest request) {
log.info("开始创建设备分组,名称: {}, 类型: {}", request.getGroupName(), request.getGroupType());
try {
// 1. 验证分组名称唯一性
if (groupRepository.existsByGroupNameAndParentGroupId(request.getGroupName(), request.getParentGroupId())) {
throw new GroupNameConflictException("分组名称已存在: " + request.getGroupName());
}
// 2. 验证父分组是否存在(如果指定了父分组)
DeviceGroup parentGroup = null;
if (StringUtils.hasText(request.getParentGroupId())) {
parentGroup = groupRepository.findByGroupId(request.getParentGroupId())
.orElseThrow(() -> new GroupNotFoundException("父分组不存在: " + request.getParentGroupId()));
}
// 3. 生成分组ID
String groupId = groupIdGenerator.generateGroupId(request.getGroupType());
// 4. 计算分组路径和级别
String groupPath = calculateGroupPath(parentGroup, request.getGroupName());
Integer groupLevel = calculateGroupLevel(parentGroup);
// 5. 构建分组实体
DeviceGroup deviceGroup = DeviceGroup.builder()
.groupId(groupId)
.groupName(request.getGroupName())
.description(request.getDescription())
.groupType(request.getGroupType())
.parentGroupId(request.getParentGroupId())
.groupPath(groupPath)
.groupLevel(groupLevel)
.tags(JsonUtils.toJson(request.getTags()))
.groupConfig(JsonUtils.toJson(request.getGroupConfig()))
.groupPolicies(JsonUtils.toJson(request.getGroupPolicies()))
.deviceCount(0)
.groupStatus(GroupStatus.ACTIVE)
.creatorId(request.getCreatorId())
.administrators(JsonUtils.toJson(request.getAdministrators()))
.build();
// 6. 保存分组
deviceGroup = groupRepository.save(deviceGroup);
// 7. 发布分组创建事件
publishGroupCreatedEvent(deviceGroup);
log.info("设备分组创建成功,分组ID: {}, 名称: {}", groupId, request.getGroupName());
return deviceGroup;
} catch (Exception e) {
log.error("创建设备分组失败,名称: {}", request.getGroupName(), e);
throw e;
}
}
/**
* 添加设备到分组
* 就像给学生分配班级,需要检查学生是否存在、班级是否有空位等
*/
@Override
public boolean addDevicesToGroup(String groupId, List<String> deviceIds) {
log.info("开始添加设备到分组,分组ID: {}, 设备数量: {}", groupId, deviceIds.size());
try {
// 1. 验证分组是否存在
DeviceGroup group = groupRepository.findByGroupId(groupId)
.orElseThrow(() -> new GroupNotFoundException("分组不存在: " + groupId));
// 2. 验证设备是否存在
List<DeviceInfo> devices = deviceInfoRepository.findByDeviceIdIn(deviceIds);
if (devices.size() != deviceIds.size()) {
Set<String> foundDeviceIds = devices.stream()
.map(DeviceInfo::getDeviceId)
.collect(Collectors.toSet());
List<String> notFoundDevices = deviceIds.stream()
.filter(id -> !foundDeviceIds.contains(id))
.collect(Collectors.toList());
throw new DeviceNotFoundException("设备不存在: " + notFoundDevices);
}
// 3. 检查设备是否已在分组中
List<String> existingDevices = relationRepository.findDeviceIdsByGroupId(groupId);
List<String> newDevices = deviceIds.stream()
.filter(deviceId -> !existingDevices.contains(deviceId))
.collect(Collectors.toList());
if (newDevices.isEmpty()) {
log.warn("所有设备都已在分组中,分组ID: {}", groupId);
return true;
}
// 4. 批量创建分组关系
List<DeviceGroupRelation> relations = newDevices.stream()
.map(deviceId -> DeviceGroupRelation.builder()
.deviceId(deviceId)
.groupId(groupId)
.relationType(RelationType.PRIMARY)
.joinedTime(LocalDateTime.now())
.build())
.collect(Collectors.toList());
relationRepository.saveAll(relations);
// 5. 更新分组设备数量
updateGroupDeviceCount(groupId);
// 6. 发布设备加入分组事件
publishDevicesAddedToGroupEvent(groupId, newDevices);
log.info("设备添加到分组成功,分组ID: {}, 新增设备数量: {}", groupId, newDevices.size());
return true;
} catch (Exception e) {
log.error("添加设备到分组失败,分组ID: {}", groupId, e);
return false;
}
}
/**
* 批量操作分组设备
* 就像班主任对整个班级进行统一管理(如统一发通知、统一考试等)
*/
@Override
public BatchOperationResult batchOperateGroupDevices(String groupId, GroupOperation operation, Map<String, Object> parameters) {
log.info("开始批量操作分组设备,分组ID: {}, 操作: {}", groupId, operation);
try {
// 1. 验证分组是否存在
DeviceGroup group = groupRepository.findByGroupId(groupId)
.orElseThrow(() -> new GroupNotFoundException("分组不存在: " + groupId));
// 2. 获取分组下的所有设备
List<String> deviceIds = relationRepository.findDeviceIdsByGroupId(groupId);
if (deviceIds.isEmpty()) {
return BatchOperationResult.builder()
.success(true)
.totalCount(0)
.successCount(0)
.failureCount(0)
.message("分组中没有设备")
.build();
}
// 3. 根据操作类型执行相应的批量操作
BatchOperationResult result = executeGroupOperation(deviceIds, operation, parameters);
// 4. 记录操作日志
recordGroupOperationLog(groupId, operation, parameters, result);
// 5. 发布批量操作事件
publishBatchOperationEvent(groupId, operation, result);
log.info("批量操作分组设备完成,分组ID: {}, 成功: {}, 失败: {}",
groupId, result.getSuccessCount(), result.getFailureCount());
return result;
} catch (Exception e) {
log.error("批量操作分组设备失败,分组ID: {}, 操作: {}", groupId, operation, e);
return BatchOperationResult.builder()
.success(false)
.message("批量操作失败: " + e.getMessage())
.build();
}
}
/**
* 执行分组操作
*/
private BatchOperationResult executeGroupOperation(List<String> deviceIds, GroupOperation operation, Map<String, Object> parameters) {
int totalCount = deviceIds.size();
int successCount = 0;
int failureCount = 0;
List<String> failureReasons = new ArrayList<>();
switch (operation) {
case RESTART_DEVICES:
// 重启设备
for (String deviceId : deviceIds) {
try {
deviceCommandService.sendRestartCommand(deviceId);
successCount++;
} catch (Exception e) {
failureCount++;
failureReasons.add(deviceId + ": " + e.getMessage());
}
}
break;
case UPDATE_CONFIG:
// 更新配置
Map<String, Object> config = (Map<String, Object>) parameters.get("config");
for (String deviceId : deviceIds) {
try {
deviceConfigService.updateDeviceConfig(deviceId, config);
successCount++;
} catch (Exception e) {
failureCount++;
failureReasons.add(deviceId + ": " + e.getMessage());
}
}
break;
case CHANGE_STATUS:
// 变更状态
DeviceStatus newStatus = DeviceStatus.valueOf((String) parameters.get("status"));
String reason = (String) parameters.get("reason");
for (String deviceId : deviceIds) {
try {
deviceStatusService.updateDeviceStatus(deviceId, newStatus, reason);
successCount++;
} catch (Exception e) {
failureCount++;
failureReasons.add(deviceId + ": " + e.getMessage());
}
}
break;
default:
throw new UnsupportedOperationException("不支持的操作类型: " + operation);
}
return BatchOperationResult.builder()
.success(failureCount == 0)
.totalCount(totalCount)
.successCount(successCount)
.failureCount(failureCount)
.failureReasons(failureReasons)
.message(String.format("批量操作完成,成功: %d, 失败: %d", successCount, failureCount))
.build();
}
/**
* 计算分组路径
*/
private String calculateGroupPath(DeviceGroup parentGroup, String groupName) {
if (parentGroup == null) {
return "/" + groupName;
}
return parentGroup.getGroupPath() + "/" + groupName;
}
/**
* 计算分组级别
*/
private Integer calculateGroupLevel(DeviceGroup parentGroup) {
if (parentGroup == null) {
return 0;
}
return parentGroup.getGroupLevel() + 1;
}
/**
* 更新分组设备数量
*/
private void updateGroupDeviceCount(String groupId) {
int deviceCount = relationRepository.countByGroupId(groupId);
groupRepository.updateDeviceCount(groupId, deviceCount);
}
}
# 5. 实际应用示例
# 5.1 智能城市场景
让我们通过一个智能城市的实际案例来看看设备管理层是如何工作的:
/**
* 智能城市设备管理示例
* 展示如何管理一个城市的10万台物联网设备
*/
@Component
@Slf4j
public class SmartCityDeviceManagementExample {
@Autowired
private DeviceRegistrationService registrationService;
@Autowired
private DeviceAuthenticationService authenticationService;
@Autowired
private DeviceStatusService statusService;
@Autowired
private DeviceGroupService groupService;
/**
* 场景1:新建智能路灯批量注册
* 某个新建小区安装了100盏智能路灯,需要批量注册到系统中
*/
public void registerSmartStreetLights() {
log.info("开始批量注册智能路灯");
// 1. 创建地理位置分组
CreateGroupRequest groupRequest = CreateGroupRequest.builder()
.groupName("阳光小区")
.description("阳光小区智能设备分组")
.groupType(GroupType.GEOGRAPHIC)
.parentGroupId("GROUP-GEO-CITY-001") // 城市级分组
.creatorId("admin")
.tags(Map.of(
"region", "东城区",
"type", "residential",
"phase", "new_construction"
))
.build();
DeviceGroup communityGroup = groupService.createGroup(groupRequest);
// 2. 批量注册智能路灯
List<String> registeredDeviceIds = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
DeviceRegistrationRequest request = DeviceRegistrationRequest.builder()
.serialNumber(String.format("SL-YG-%03d", i))
.deviceType(DeviceType.SMART_LIGHT)
.deviceModel("SL-2024-Pro")
.manufacturer("智慧照明科技")
.firmwareVersion("v2.1.0")
.location(DeviceLocation.builder()
.latitude(39.9042 + i * 0.0001)
.longitude(116.4074 + i * 0.0001)
.address(String.format("阳光小区%d号路灯", i))
.build())
.capabilities(DeviceCapabilities.builder()
.supportedProtocols(List.of("MQTT", "HTTP"))
.features(List.of("brightness_control", "color_temperature", "motion_detection"))
.build())
.tags(Map.of(
"zone", "A区",
"pole_height", "6m",
"power_rating", "50W"
))
.build();
DeviceRegistrationResult result = registrationService.registerDevice(request);
if (result.isSuccess()) {
registeredDeviceIds.add(result.getDeviceId());
log.info("路灯注册成功: {}", result.getDeviceId());
}
}
// 3. 将设备添加到分组
groupService.addDevicesToGroup(communityGroup.getGroupId(), registeredDeviceIds);
log.info("智能路灯批量注册完成,成功注册: {} 盏", registeredDeviceIds.size());
}
/**
* 场景2:设备认证和状态监控
* 模拟设备上线认证和心跳监控过程
*/
public void deviceAuthenticationAndMonitoring() {
String deviceId = "DEV-SL-20240101-000001";
// 1. 设备证书认证
CertificateAuthRequest authRequest = CertificateAuthRequest.builder()
.deviceId(deviceId)
.certificate("-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----")
.challenge("challenge_" + System.currentTimeMillis())
.signature("signature_data")
.timestamp(LocalDateTime.now())
.build();
AuthenticationResult authResult = authenticationService.authenticateWithCertificate(authRequest);
if (authResult.isSuccess()) {
log.info("设备认证成功: {}", deviceId);
// 2. 模拟设备心跳上报
simulateDeviceHeartbeat(deviceId);
}
}
/**
* 模拟设备心跳上报
*/
private void simulateDeviceHeartbeat(String deviceId) {
// 模拟正常工作状态的心跳
HeartbeatRequest heartbeatRequest = HeartbeatRequest.builder()
.deviceId(deviceId)
.timestamp(LocalDateTime.now())
.deviceStatus(DeviceStatus.ACTIVE)
.metrics(DeviceMetrics.builder()
.cpuUsage(25.5)
.memoryUsage(60.2)
.temperature(45.0)
.signalStrength(-65)
.batteryLevel(null) // 路灯通常不使用电池
.networkLatency(50L)
.collectionTime(LocalDateTime.now())
.build())
.sequenceNumber(1L)
.build();
HeartbeatResponse response = statusService.reportHeartbeat(heartbeatRequest);
log.info("心跳上报结果: {}", response.getMessage());
}
/**
* 场景3:分组批量操作
* 夜间自动调节所有路灯亮度
*/
@Scheduled(cron = "0 0 22 * * ?") // 每天晚上10点执行
public void adjustNightLighting() {
log.info("开始执行夜间照明调节");
// 获取所有路灯分组
GroupQueryRequest queryRequest = GroupQueryRequest.builder()
.groupType(GroupType.DEVICE_TYPE)
.tags(Map.of("device_category", "lighting"))
.build();
List<DeviceGroup> lightingGroups = groupService.queryGroups(queryRequest);
for (DeviceGroup group : lightingGroups) {
// 批量调节亮度到80%
Map<String, Object> parameters = Map.of(
"config", Map.of(
"brightness", 80,
"color_temperature", 4000,
"motion_sensitivity", "high"
)
);
BatchOperationResult result = groupService.batchOperateGroupDevices(
group.getGroupId(),
GroupOperation.UPDATE_CONFIG,
parameters
);
log.info("分组 {} 夜间照明调节完成,成功: {}, 失败: {}",
group.getGroupName(), result.getSuccessCount(), result.getFailureCount());
}
}
}
# 5.2 工业物联网场景
/**
* 工业物联网设备管理示例
* 展示如何管理工厂的生产设备
*/
@Component
@Slf4j
public class IndustrialIoTDeviceManagementExample {
/**
* 场景:生产线设备健康监控
* 实时监控生产线上的各种设备状态
*/
public void productionLineHealthMonitoring() {
// 创建生产线分组
CreateGroupRequest lineGroupRequest = CreateGroupRequest.builder()
.groupName("生产线A")
.description("汽车零部件生产线A")
.groupType(GroupType.FUNCTIONAL)
.tags(Map.of(
"workshop", "车间1",
"product_line", "engine_parts",
"shift", "day_shift"
))
.groupPolicies(Map.of(
"health_check_interval", 30, // 30秒检查一次
"alert_threshold", Map.of(
"temperature", 80,
"vibration", 5.0,
"pressure", 10.0
)
))
.build();
DeviceGroup productionLine = groupService.createGroup(lineGroupRequest);
// 注册生产设备
registerProductionEquipment(productionLine.getGroupId());
// 启动健康监控
startHealthMonitoring(productionLine.getGroupId());
}
private void registerProductionEquipment(String groupId) {
// 注册各种生产设备
String[] equipmentTypes = {"CNC_MACHINE", "ROBOT_ARM", "CONVEYOR", "QUALITY_SCANNER"};
for (int i = 0; i < equipmentTypes.length; i++) {
DeviceRegistrationRequest request = DeviceRegistrationRequest.builder()
.serialNumber(String.format("PROD-A-%s-%02d", equipmentTypes[i], i + 1))
.deviceType(DeviceType.valueOf(equipmentTypes[i]))
.deviceModel("Industrial-Pro-2024")
.manufacturer("工业自动化公司")
.capabilities(DeviceCapabilities.builder()
.supportedProtocols(List.of("OPC-UA", "MQTT", "Modbus"))
.features(List.of("real_time_monitoring", "predictive_maintenance", "remote_control"))
.build())
.tags(Map.of(
"criticality", "high",
"maintenance_schedule", "weekly",
"warranty_expiry", "2025-12-31"
))
.build();
DeviceRegistrationResult result = registrationService.registerDevice(request);
if (result.isSuccess()) {
groupService.addDevicesToGroup(groupId, List.of(result.getDeviceId()));
}
}
}
/**
* 启动健康监控
*/
private void startHealthMonitoring(String groupId) {
// 这里可以启动一个定时任务,定期检查设备健康状况
// 实际实现中可能会使用Spring的@Scheduled注解
log.info("生产线健康监控已启动,分组ID: {}", groupId);
}
}
# 6. 最佳实践和总结
# 6.1 设计原则
- 统一性原则:所有设备都遵循统一的注册、认证、状态管理流程
- 安全性原则:多层次的安全认证机制,确保只有合法设备能够接入
- 可扩展性原则:支持不同类型设备的接入,支持分组的层级结构
- 高可用性原则:通过心跳机制实时监控设备状态,及时发现和处理异常
- 易管理性原则:通过分组管理实现设备的批量操作和统一配置
# 6.2 性能优化建议
/**
* 设备管理层性能优化示例
*/
@Component
public class DeviceManagementOptimization {
/**
* 1. 批量操作优化
* 使用批量操作减少数据库访问次数
*/
@Transactional
public void batchRegisterDevices(List<DeviceRegistrationRequest> requests) {
// 批量验证
validateBatchRequests(requests);
// 批量生成ID
List<String> deviceIds = generateBatchDeviceIds(requests);
// 批量构建实体
List<DeviceInfo> deviceInfos = buildBatchDeviceInfos(requests, deviceIds);
// 批量保存
deviceInfoRepository.saveAll(deviceInfos);
// 批量发布事件
publishBatchRegistrationEvents(deviceInfos);
}
/**
* 2. 缓存优化
* 使用Redis缓存热点数据
*/
@Cacheable(value = "device_status", key = "#deviceId")
public DeviceStatusInfo getCachedDeviceStatus(String deviceId) {
return deviceStatusService.getDeviceStatus(deviceId).orElse(null);
}
/**
* 3. 异步处理优化
* 使用异步处理提高响应速度
*/
@Async
public CompletableFuture<Void> processHeartbeatAsync(HeartbeatRequest request) {
return CompletableFuture.runAsync(() -> {
statusService.reportHeartbeat(request);
});
}
}
# 6.3 监控和告警
/**
* 设备管理层监控指标
*/
@Component
public class DeviceManagementMetrics {
private final MeterRegistry meterRegistry;
private final Counter deviceRegistrationCounter;
private final Timer authenticationTimer;
private final Gauge activeDeviceGauge;
public DeviceManagementMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.deviceRegistrationCounter = Counter.builder("device.registration.total")
.description("设备注册总数")
.register(meterRegistry);
this.authenticationTimer = Timer.builder("device.authentication.duration")
.description("设备认证耗时")
.register(meterRegistry);
this.activeDeviceGauge = Gauge.builder("device.active.count")
.description("活跃设备数量")
.register(meterRegistry, this, DeviceManagementMetrics::getActiveDeviceCount);
}
public void recordDeviceRegistration() {
deviceRegistrationCounter.increment();
}
public void recordAuthenticationTime(Duration duration) {
authenticationTimer.record(duration);
}
private double getActiveDeviceCount() {
// 实际实现中从数据库或缓存中获取活跃设备数量
return deviceInfoRepository.countByDeviceStatus(DeviceStatus.ACTIVE);
}
}
# 6.4 总结
设备管理层是物联网系统的"大脑中枢",它就像一个智能的设备管家,负责:
- 设备注册:为每个新设备办理"身份证",建立完整档案
- 设备认证:确保只有合法设备能够接入系统,就像门禁系统
- 状态管理:实时监控设备健康状况,就像医院的监护系统
- 分组管理:按不同维度对设备进行分类管理,就像学校的班级制度
通过这四大核心功能的协同工作,设备管理层能够:
- 支持海量设备的统一管理
- 提供多层次的安全保障
- 实现设备的智能监控和预警
- 支持灵活的分组策略和批量操作
- 提供完整的设备生命周期管理
这样的设计不仅满足了当前的业务需求,还为未来的扩展和优化奠定了坚实的基础。