Files
system-design/software-copyright/01-writech-cloud-platform/service/DeviceService.java
T
2026-03-22 15:24:40 +08:00

281 lines
9.9 KiB
Java

/**
* 自然写互动课堂教学管理云平台软件 V1.0
*
* 设备管理服务
* 管理点阵笔、网关、终端设备、算力盒的全生命周期
*/
package com.writech.cloud.service;
import com.writech.cloud.model.Device;
import com.writech.cloud.controller.DeviceController.ClassroomTopology;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* 设备服务类
*
* 管理互动课堂中所有硬件设备的注册、绑定、状态监控
* 设备类型:pen(点阵笔) / gateway(网关) / terminal(终端) / edge_box(算力盒)
*/
@Service
public class DeviceService {
@Autowired
private StringRedisTemplate redisTemplate;
/** 设备在线超时时间(秒),超过此时间未收到心跳视为离线 */
private static final long DEVICE_ONLINE_TIMEOUT = 120;
/** 网关设备心跳间隔(秒) */
private static final long GATEWAY_HEARTBEAT_INTERVAL = 30;
/** 笔设备心跳间隔(秒) */
private static final long PEN_HEARTBEAT_INTERVAL = 300;
/**
* 保存设备信息
*/
@Transactional
public void save(Device device) {
// deviceRepository.save(device);
// 更新Redis中的设备在线状态缓存
updateDeviceOnlineStatus(device.getId(), true);
}
/**
* 根据ID查询设备
*/
public Device findById(String deviceId) {
// return deviceRepository.findById(deviceId).orElse(null);
return null;
}
/**
* 根据MAC地址查询设备
*/
public Device findByMacAddr(String macAddr) {
// return deviceRepository.findByMacAddr(macAddr);
return null;
}
/**
* 校验设备证书(X.509)
* 首次注册时网关设备需提供预置的设备证书进行身份校验
*
* @param macAddr MAC地址
* @param certPem PEM格式的X.509证书
* @return 校验通过返回true
*/
public boolean validateDeviceCertificate(String macAddr, String certPem) {
if (certPem == null || certPem.isEmpty()) {
return false;
}
try {
// 解析X.509证书
java.security.cert.CertificateFactory cf =
java.security.cert.CertificateFactory.getInstance("X.509");
java.io.ByteArrayInputStream bis =
new java.io.ByteArrayInputStream(certPem.getBytes());
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
// 检查证书有效期
cert.checkValidity();
// 验证证书签名(使用CA根证书公钥)
// cert.verify(caCertificate.getPublicKey());
// 从证书CN字段提取MAC地址,与请求中的MAC地址比对
String cn = cert.getSubjectX500Principal().getName();
if (!cn.contains(macAddr.replace(":", "").toUpperCase())) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* 设备绑定
* 将设备绑定至指定用户和教室
*/
@Transactional
public void bindDevice(String deviceId, String userId, String classroomId) {
// deviceRepository.updateBinding(deviceId, userId, classroomId);
}
/**
* 设备解绑
*/
@Transactional
public void unbindDevice(String deviceId) {
// deviceRepository.clearBinding(deviceId);
}
/**
* 分页查询设备列表
* 支持按学校、教室、类型、状态多维度过滤
*/
public Page<Device> queryDevices(String schoolId, String classroomId,
String deviceType, Integer status,
Pageable pageable) {
// return deviceRepository.queryByConditions(schoolId, classroomId,
// deviceType, status, pageable);
return null;
}
/**
* 更新设备心跳
* 心跳数据写入MySQL并更新Redis在线状态缓存
*/
public void updateHeartbeat(Device device) {
// deviceRepository.updateHeartbeat(device.getId(),
// device.getLastHeartbeat(), device.getBatteryLevel(),
// device.getConnectedPenCount(), device.getCpuUsage(),
// device.getMemoryUsage());
// 更新Redis在线状态(设置过期时间为心跳超时时间)
updateDeviceOnlineStatus(device.getId(), true);
}
/**
* 构建教室设备拓扑
* 查询教室内所有设备,按类型分组并建立连接关系
*
* @param classroomId 教室ID
* @return 拓扑结构(网关/算力盒/终端/笔)
*/
public ClassroomTopology buildClassroomTopology(String classroomId) {
// 查询教室下所有设备
// List<Device> devices = deviceRepository.findByClassroomId(classroomId);
List<Device> devices = new ArrayList<>();
ClassroomTopology topology = new ClassroomTopology();
topology.setClassroomId(classroomId);
// 按设备类型分组
Map<String, List<Device>> grouped = devices.stream()
.collect(Collectors.groupingBy(Device::getType));
topology.setGateways(grouped.getOrDefault("gateway", new ArrayList<>()));
topology.setEdgeBoxes(grouped.getOrDefault("edge_box", new ArrayList<>()));
topology.setTerminals(grouped.getOrDefault("terminal", new ArrayList<>()));
topology.setPens(grouped.getOrDefault("pen", new ArrayList<>()));
topology.setTotalDeviceCount(devices.size());
return topology;
}
/**
* 批量检查设备在线状态
* 通过Redis缓存快速判断设备是否在线
*/
public Map<String, Boolean> checkOnlineStatus(List<String> deviceIds) {
Map<String, Boolean> result = new HashMap<>();
for (String deviceId : deviceIds) {
String key = "writech:device:online:" + deviceId;
result.put(deviceId, Boolean.TRUE.equals(redisTemplate.hasKey(key)));
}
return result;
}
/**
* 发送远程指令至设备
* 通过MQTT向指定设备下发控制指令(重启/配置更新/OTA等)
*/
public void sendCommand(String deviceId, String command, Map<String, Object> params) {
// 构建MQTT消息
Map<String, Object> message = new HashMap<>();
message.put("command", command);
message.put("params", params);
message.put("timestamp", System.currentTimeMillis());
// 根据设备类型确定Topic
Device device = findById(deviceId);
if (device == null) return;
String topic;
switch (device.getType()) {
case "gateway":
topic = "gateway/" + deviceId + "/command";
break;
case "edge_box":
topic = "edgebox/" + deviceId + "/command";
break;
default:
topic = "device/" + deviceId + "/command";
}
// mqttTemplate.convertAndSend(topic, message);
}
/**
* 统计学校设备概况
*/
public DeviceOverview getSchoolDeviceOverview(String schoolId) {
DeviceOverview overview = new DeviceOverview();
// 各类型设备数量统计
// overview.setTotalPens(deviceRepository.countBySchoolAndType(schoolId, "pen"));
// overview.setTotalGateways(deviceRepository.countBySchoolAndType(schoolId, "gateway"));
// overview.setOnlinePens(countOnlineDevices(schoolId, "pen"));
// overview.setOnlineGateways(countOnlineDevices(schoolId, "gateway"));
return overview;
}
// ==================== 内部方法 ====================
/** 更新Redis中设备在线状态 */
private void updateDeviceOnlineStatus(String deviceId, boolean online) {
String key = "writech:device:online:" + deviceId;
if (online) {
redisTemplate.opsForValue().set(key, "1",
DEVICE_ONLINE_TIMEOUT, java.util.concurrent.TimeUnit.SECONDS);
} else {
redisTemplate.delete(key);
}
}
// ==================== 内部类 ====================
/** 设备概况统计 */
public static class DeviceOverview {
private int totalPens;
private int totalGateways;
private int totalEdgeBoxes;
private int totalTerminals;
private int onlinePens;
private int onlineGateways;
private int onlineEdgeBoxes;
private double averageBatteryLevel;
public int getTotalPens() { return totalPens; }
public void setTotalPens(int c) { this.totalPens = c; }
public int getTotalGateways() { return totalGateways; }
public void setTotalGateways(int c) { this.totalGateways = c; }
public int getTotalEdgeBoxes() { return totalEdgeBoxes; }
public void setTotalEdgeBoxes(int c) { this.totalEdgeBoxes = c; }
public int getTotalTerminals() { return totalTerminals; }
public void setTotalTerminals(int c) { this.totalTerminals = c; }
public int getOnlinePens() { return onlinePens; }
public void setOnlinePens(int c) { this.onlinePens = c; }
public int getOnlineGateways() { return onlineGateways; }
public void setOnlineGateways(int c) { this.onlineGateways = c; }
public int getOnlineEdgeBoxes() { return onlineEdgeBoxes; }
public void setOnlineEdgeBoxes(int c) { this.onlineEdgeBoxes = c; }
public double getAverageBatteryLevel() { return averageBatteryLevel; }
public void setAverageBatteryLevel(double l) { this.averageBatteryLevel = l; }
}
}