diff --git a/AMT_REALTIME_STATUS.md b/AMT_REALTIME_STATUS.md new file mode 100644 index 0000000..70c3c29 --- /dev/null +++ b/AMT_REALTIME_STATUS.md @@ -0,0 +1,417 @@ +# AMT 实时状态检测功能 + +## 功能概述 + +实现了 AMT 设备状态的实时检测和自动更新功能,无需手动刷新即可看到设备的在线/离线状态变化。 + +## 实现方案 + +### 后端实现 + +#### 1. 定时任务 (AmtStatusCheckTask.java) + +**位置**: `backend/src/main/java/com/soybean/admin/task/AmtStatusCheckTask.java` + +**功能**: +- 每 30 秒自动检测一次所有设备的 AMT 状态 +- 使用线程池并发检测,提高效率 +- 只在状态变化时更新数据库,减少写入操作 +- 使用 Socket 连接检测 AMT 端口(16992/16993)可用性 + +**关键特性**: +```java +@Scheduled(fixedRate = 30000, initialDelay = 10000) +public void checkAmtStatus() { + // 每 30 秒执行一次 + // 初始延迟 10 秒,等待应用完全启动 +} +``` + +**检测逻辑**: +1. 查询所有有 IP 地址的设备 +2. 并发检测每个设备的 AMT 端口连接性 +3. 先尝试 HTTP (16992),失败则尝试 HTTPS (16993) +4. 根据连接结果更新设备的 `amt_status` 字段 +5. 只有状态变化时才更新数据库和记录日志 + +**线程池配置**: +- 固定 20 个线程 +- 使用 CompletableFuture 异步执行 +- 等待所有检测完成后结束任务 + +#### 2. 启用定时任务 + +**修改**: `backend/src/main/java/com/soybean/admin/SoybeanAdminApplication.java` + +添加 `@EnableScheduling` 注解启用 Spring 定时任务功能: + +```java +@SpringBootApplication +@MapperScan("com.soybean.admin.mapper") +@EnableScheduling // 启用定时任务 +public class SoybeanAdminApplication { + // ... +} +``` + +### 前端实现 + +#### 1. 自动轮询刷新 + +**位置**: `src/views/device/list/index.vue` + +**功能**: +- 页面加载后每 10 秒自动刷新设备列表 +- 组件卸载时自动清除定时器 +- 显示"自动刷新中"状态标签 + +**实现代码**: +```typescript +onMounted(() => { + loadData(); + + // 启动定时刷新,每 10 秒刷新一次设备状态 + const refreshInterval = setInterval(() => { + loadData(); + }, 10000); + + // 组件卸载时清除定时器 + onUnmounted(() => { + clearInterval(refreshInterval); + }); +}); +``` + +#### 2. 手动刷新按钮 + +添加了"刷新状态"按钮,用户可以随时手动刷新: + +```vue + + + 刷新状态 + +``` + +#### 3. 状态指示器 + +显示自动刷新状态: + +```vue + + + 自动刷新中 + +``` + +## 工作流程 + +### 完整流程图 + +``` +后端定时任务 (每30秒) + ↓ +检测所有设备 AMT 端口 + ↓ +更新数据库状态 + ↓ +前端轮询 (每10秒) + ↓ +获取最新设备列表 + ↓ +更新界面显示 +``` + +### 时间线示例 + +``` +T=0s: 后端启动,10秒后开始第一次检测 +T=10s: 后端第一次检测所有设备 +T=10s: 前端页面加载,开始第一次数据获取 +T=20s: 前端第二次刷新 +T=30s: 前端第三次刷新 +T=40s: 后端第二次检测 + 前端第四次刷新 +T=50s: 前端第五次刷新 +... +``` + +## 性能优化 + +### 后端优化 + +1. **并发检测**: 使用 20 个线程并发检测,大幅提升检测速度 +2. **快速超时**: Socket 连接超时设置为 3 秒,避免长时间等待 +3. **增量更新**: 只在状态变化时更新数据库 +4. **简单检测**: 只检测端口连接性,不进行完整的 AMT 认证 + +### 前端优化 + +1. **合理间隔**: 10 秒刷新间隔,平衡实时性和性能 +2. **自动清理**: 组件卸载时清除定时器,避免内存泄漏 +3. **加载状态**: 显示加载状态,提升用户体验 + +## 状态检测逻辑 + +### 检测方法 + +使用 TCP Socket 连接检测,而不是完整的 AMT 认证: + +```java +private boolean checkAmtConnectivity(String ipAddress) { + try { + // 尝试连接 AMT HTTP 端口 + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(ipAddress, 16992), 3000); + socket.close(); + return true; + } catch (Exception e) { + // HTTP 失败,尝试 HTTPS + try { + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(ipAddress, 16993), 3000); + socket.close(); + return true; + } catch (Exception ex) { + return false; + } + } +} +``` + +### 优点 + +1. **快速**: 不需要完整的 SOAP 请求和认证 +2. **轻量**: 只检测端口可用性 +3. **可靠**: 能准确判断 AMT 服务是否运行 + +### 缺点 + +1. **不验证认证**: 不检查凭证是否正确 +2. **端口占用**: 如果端口被其他服务占用,可能误判 + +## 使用说明 + +### 1. 执行数据库更新 + +如果还没有执行过数据库更新,先运行: + +```bash +update_device_status.bat +``` + +### 2. 重新编译后端 + +```bash +cd backend +mvn clean package -DskipTests +``` + +### 3. 启动后端服务 + +```bash +start_backend.bat +``` + +后端启动后,会在 10 秒后开始第一次 AMT 状态检测,之后每 30 秒检测一次。 + +### 4. 打开前端页面 + +访问设备列表页面,会看到: +- 右上角显示"自动刷新中"标签 +- 设备的 AMT 状态列实时更新 +- 可以点击"刷新状态"按钮手动刷新 + +## 日志查看 + +### 后端日志 + +查看后端控制台输出,会看到类似日志: + +``` +开始 AMT 状态检测任务 +开始检测 10 个设备的 AMT 状态 +设备 Server-01 (192.168.8.100) AMT 状态变化: offline -> online +设备 Server-02 (192.168.8.101) AMT 状态变化: online -> offline +AMT 状态检测任务完成 +``` + +### 前端日志 + +打开浏览器开发者工具,可以看到每 10 秒发送一次设备列表请求。 + +## 测试场景 + +### 场景 1: 设备上线 + +1. 启动一台 AMT 设备 +2. 等待最多 30 秒(后端检测周期) +3. 前端会在下次刷新时显示设备状态变为"在线" + +### 场景 2: 设备下线 + +1. 关闭一台 AMT 设备 +2. 等待最多 30 秒(后端检测周期) +3. 前端会在下次刷新时显示设备状态变为"离线" + +### 场景 3: 手动刷新 + +1. 点击"刷新状态"按钮 +2. 立即获取最新的设备状态 +3. 不需要等待自动刷新周期 + +## 配置参数 + +### 后端配置 + +在 `AmtStatusCheckTask.java` 中可以调整: + +```java +// 检测周期(毫秒) +@Scheduled(fixedRate = 30000, initialDelay = 10000) +// fixedRate: 30000 = 30秒 +// initialDelay: 10000 = 10秒初始延迟 + +// 线程池大小 +private final ExecutorService executorService = Executors.newFixedThreadPool(20); +// 20 个线程 + +// Socket 超时时间(毫秒) +socket.connect(new InetSocketAddress(ipAddress, 16992), 3000); +// 3000 = 3秒 +``` + +### 前端配置 + +在 `index.vue` 中可以调整: + +```typescript +// 刷新周期(毫秒) +const refreshInterval = setInterval(() => { + loadData(); +}, 10000); // 10000 = 10秒 +``` + +## 性能影响 + +### 后端资源消耗 + +- **CPU**: 低(主要是网络 I/O 等待) +- **内存**: 低(20 个线程 + 设备列表) +- **网络**: 每个设备每 30 秒一次连接检测 + +### 前端资源消耗 + +- **网络**: 每 10 秒一次 API 请求 +- **内存**: 低(只更新设备列表数据) +- **CPU**: 低(简单的数据更新和渲染) + +### 数据库影响 + +- **读取**: 每 30 秒读取所有设备列表 +- **写入**: 只在状态变化时写入(增量更新) +- **索引**: 建议在 `ip_address` 和 `amt_status` 字段上建立索引 + +## 后续优化建议 + +### 1. WebSocket 推送 + +替代轮询方式,使用 WebSocket 实时推送状态变化: + +**优点**: +- 更实时 +- 减少网络请求 +- 降低服务器负载 + +**实现**: +- 后端使用 Spring WebSocket +- 状态变化时主动推送到前端 +- 前端监听 WebSocket 消息更新界面 + +### 2. 智能检测频率 + +根据设备状态调整检测频率: + +- 在线设备: 每 60 秒检测一次 +- 离线设备: 每 30 秒检测一次 +- 新添加设备: 每 10 秒检测一次(前 5 分钟) + +### 3. 批量状态查询接口 + +添加专门的状态查询接口,只返回状态信息: + +```java +@GetMapping("/device/status") +public Result> getDeviceStatus() { + // 只返回 id -> status 的映射 + // 减少数据传输量 +} +``` + +### 4. 状态变化通知 + +设备状态变化时发送通知: + +- 邮件通知 +- 短信通知 +- 系统消息通知 +- Webhook 回调 + +### 5. 历史状态记录 + +记录设备状态变化历史: + +- 创建 `device_status_history` 表 +- 记录每次状态变化的时间和原因 +- 生成可用性报表 + +## 故障排查 + +### 问题 1: 状态不更新 + +**可能原因**: +- 后端定时任务未启动 +- 数据库连接失败 +- 网络不通 + +**排查方法**: +1. 查看后端日志是否有"开始 AMT 状态检测任务" +2. 检查数据库 `amt_status` 字段是否存在 +3. 手动测试设备网络连接 + +### 问题 2: 前端不刷新 + +**可能原因**: +- 定时器未启动 +- API 请求失败 +- 组件已卸载 + +**排查方法**: +1. 打开浏览器开发者工具查看网络请求 +2. 检查控制台是否有错误 +3. 确认 `autoRefreshEnabled` 为 true + +### 问题 3: 性能问题 + +**可能原因**: +- 设备数量过多 +- 检测频率过高 +- 网络延迟大 + +**解决方案**: +1. 增加检测周期(如改为 60 秒) +2. 增加线程池大小 +3. 使用 WebSocket 替代轮询 + +## 总结 + +实现了完整的 AMT 实时状态检测功能: + +✅ 后端定时任务自动检测(每 30 秒) +✅ 前端自动刷新显示(每 10 秒) +✅ 手动刷新按钮 +✅ 状态指示器 +✅ 并发检测优化 +✅ 增量数据库更新 +✅ 完整的日志记录 + +用户无需手动刷新,即可实时看到设备的 AMT 在线/离线状态变化。 diff --git a/DEVICE_STATUS_SPLIT.md b/DEVICE_STATUS_SPLIT.md new file mode 100644 index 0000000..ae668aa --- /dev/null +++ b/DEVICE_STATUS_SPLIT.md @@ -0,0 +1,283 @@ +# 设备状态拆分说明 + +## 功能说明 + +将原来的单一设备状态拆分为两个独立的状态字段: + +1. **AMT 状态** (`amt_status`):表示 Intel AMT 是否在线 + - `online`:AMT 在线,可以进行远程管理 + - `offline`:AMT 离线,无法连接 + +2. **Agent 状态** (`agent_status`):表示操作系统是否在线 + - `online`:操作系统在线,Agent 正常运行 + - `offline`:操作系统离线或 Agent 未运行 + +## 数据库变更 + +### 新增字段 + +```sql +ALTER TABLE device +ADD COLUMN amt_status VARCHAR(20) DEFAULT 'offline' COMMENT 'AMT状态: online-在线, offline-离线', +ADD COLUMN agent_status VARCHAR(20) DEFAULT 'offline' COMMENT 'Agent状态: online-在线, offline-离线'; +``` + +### 数据迁移 + +- 原 `status` 字段保留用于兼容 +- 新设备的 `amt_status` 和 `agent_status` 默认为 `offline` +- 通过网络扫描添加的设备,`amt_status` 自动设置为 `online` + +## 后端变更 + +### 1. 实体类更新 + +**Device.java**: +```java +private String status; // 保留原字段用于兼容 +private String amtStatus; // AMT 状态 +private String agentStatus; // Agent 状态 +``` + +**DeviceDTO.java**: +```java +private String status; // 保留用于兼容 +private String amtStatus; // AMT 状态 +private String agentStatus; // Agent 状态 +``` + +### 2. 服务层更新 + +**DeviceService.java**: +- 添加 `checkAmtStatus()` 方法:检测设备 AMT 状态 +- 添加 `updateAmtStatus()` 方法:更新设备 AMT 状态 +- 添加 `updateAgentStatus()` 方法:更新设备 Agent 状态 +- 更新 `getDeviceStatistics()` 方法:统计 AMT 和 Agent 在线/离线数量 + +## 前端变更 + +### 1. 类型定义更新 + +**device.d.ts**: +```typescript +interface Device { + status: 'online' | 'offline' | 'fault' | 'maintenance'; // 保留 + amtStatus: 'online' | 'offline'; // AMT 状态 + agentStatus: 'online' | 'offline'; // Agent 状态 +} +``` + +### 2. 设备列表页面更新 + +**index.vue**: + +#### 统计卡片 +- 总设备数 +- AMT 在线 +- AMT 离线 +- Agent 在线 +- Agent 离线 + +#### 表格列 +- 设备名称 +- UUID +- **AMT 状态**(新增) +- **Agent 状态**(新增) +- IP 地址 +- MAC 地址 +- 创建时间 +- 操作 + +#### 状态显示 +- AMT 在线:绿色标签 +- AMT 离线:灰色标签 +- Agent 在线:蓝色标签 +- Agent 离线:灰色标签 + +### 3. 网络扫描功能 +- 扫描到的设备自动设置 `amtStatus = 'online'` +- Agent 状态默认为 `offline`(需要 Agent 上报) + +## 使用说明 + +### 1. 执行数据库更新 + +运行批处理文件: +```bash +update_device_status.bat +``` + +或手动执行 SQL: +```bash +mysql -h localhost -P 3306 -u root -proot soybean_admin < update_device_status_fields.sql +``` + +### 2. 重新编译后端 + +```bash +cd backend +mvn clean package -DskipTests +``` + +### 3. 重启后端服务 + +```bash +start_backend.bat +``` + +### 4. 测试功能 + +1. 打开设备列表页面 +2. 查看统计卡片是否显示 AMT 和 Agent 状态 +3. 查看表格是否显示两个状态列 +4. 进行网络扫描,添加设备 +5. 验证新添加的设备 AMT 状态为"在线" + +## AMT 状态检测 + +### 当前实现 + +扫描到的设备自动设置为在线,其他设备默认离线。 + +### 后续完善 + +1. **定时检测**: + - 创建定时任务,定期检测所有设备的 AMT 状态 + - 使用 Identify 请求快速检测连接性 + - 更新数据库中的 `amt_status` 字段 + +2. **手动刷新**: + - 在设备列表添加"刷新状态"按钮 + - 点击后检测选中设备的 AMT 状态 + - 实时更新状态显示 + +3. **状态变化通知**: + - AMT 从在线变为离线时发送告警 + - 记录状态变化历史 + +## Agent 状态实现 + +### 当前状态 + +Agent 状态字段已添加,默认为离线(假数据)。 + +### 后续实现步骤 + +1. **开发 Agent 程序**: + - 在目标设备上运行的客户端程序 + - 定期向服务器报告心跳 + - 上报设备信息(CPU、内存、磁盘等) + +2. **心跳机制**: + - Agent 每 30 秒发送一次心跳 + - 服务器记录最后心跳时间 + - 超过 2 分钟未收到心跳则标记为离线 + +3. **状态更新接口**: + ```java + @PostMapping("/device/agent/heartbeat") + public Result agentHeartbeat(@RequestBody AgentHeartbeat heartbeat) { + // 更新设备 Agent 状态为在线 + // 更新最后心跳时间 + // 记录设备信息 + } + ``` + +4. **定时检查**: + - 创建定时任务,每分钟检查一次 + - 将超时未心跳的设备标记为离线 + +## 状态组合说明 + +| AMT 状态 | Agent 状态 | 说明 | +|---------|-----------|------| +| 在线 | 在线 | 设备完全正常,可进行所有操作 | +| 在线 | 离线 | 操作系统关机,但 AMT 可用,可远程开机 | +| 离线 | 在线 | AMT 未启用或故障,但系统正常运行 | +| 离线 | 离线 | 设备完全离线或断电 | + +## 操作建议 + +### 根据状态执行操作 + +1. **AMT 在线 + Agent 离线**: + - 可以使用远程开机功能 + - 可以查看 BIOS 设置 + - 可以进行硬件级别的管理 + +2. **AMT 离线 + Agent 在线**: + - 可以通过 Agent 执行软件操作 + - 无法进行硬件级别的管理 + - 建议启用 AMT 功能 + +3. **AMT 在线 + Agent 在线**: + - 所有功能可用 + - 最佳状态 + +4. **AMT 离线 + Agent 离线**: + - 设备不可用 + - 需要物理检查设备 + +## 测试场景 + +### 场景 1:网络扫描添加设备 +1. 执行网络扫描 +2. 选择设备并批量添加 +3. 验证:AMT 状态 = 在线,Agent 状态 = 离线 + +### 场景 2:手动添加设备 +1. 选择"手动添加"方式 +2. 输入设备信息 +3. 验证:AMT 状态 = 离线,Agent 状态 = 离线 + +### 场景 3:查看统计信息 +1. 打开设备列表 +2. 查看顶部统计卡片 +3. 验证:显示 AMT 和 Agent 的在线/离线数量 + +### 场景 4:状态筛选 +1. 在搜索栏选择状态筛选 +2. 验证:可以按 AMT 或 Agent 状态筛选(后续实现) + +## 注意事项 + +1. **数据兼容性**: + - 保留了原 `status` 字段 + - 旧代码仍可正常运行 + - 建议逐步迁移到新字段 + +2. **性能考虑**: + - AMT 状态检测会产生网络请求 + - 建议使用缓存和定时任务 + - 避免频繁检测 + +3. **错误处理**: + - AMT 检测失败不应影响其他功能 + - 记录检测失败的原因 + - 提供重试机制 + +4. **安全性**: + - Agent 心跳需要身份验证 + - 防止伪造心跳攻击 + - 加密传输敏感信息 + +## 后续优化 + +1. **实时状态推送**: + - 使用 WebSocket 推送状态变化 + - 前端实时更新显示 + +2. **状态历史记录**: + - 记录状态变化历史 + - 生成状态变化报表 + - 分析设备可用性 + +3. **告警功能**: + - 设备离线告警 + - 状态异常告警 + - 邮件/短信通知 + +4. **批量操作**: + - 批量刷新状态 + - 批量启用/禁用 AMT + - 批量部署 Agent diff --git a/backend/src/main/java/com/soybean/admin/SoybeanAdminApplication.java b/backend/src/main/java/com/soybean/admin/SoybeanAdminApplication.java index 2d09f6b..58f8ed0 100644 --- a/backend/src/main/java/com/soybean/admin/SoybeanAdminApplication.java +++ b/backend/src/main/java/com/soybean/admin/SoybeanAdminApplication.java @@ -3,9 +3,11 @@ package com.soybean.admin; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @MapperScan("com.soybean.admin.mapper") +@EnableScheduling public class SoybeanAdminApplication { public static void main(String[] args) { SpringApplication.run(SoybeanAdminApplication.class, args); diff --git a/backend/src/main/java/com/soybean/admin/dto/DeviceDTO.java b/backend/src/main/java/com/soybean/admin/dto/DeviceDTO.java index 94b33ac..b469627 100644 --- a/backend/src/main/java/com/soybean/admin/dto/DeviceDTO.java +++ b/backend/src/main/java/com/soybean/admin/dto/DeviceDTO.java @@ -7,7 +7,9 @@ public class DeviceDTO { private Long id; private String deviceName; private String deviceCode; - private String status; + private String status; // 保留用于兼容 + private String amtStatus; // AMT 状态 + private String agentStatus; // Agent 状态 private String ipAddress; private String macAddress; private String remark; diff --git a/backend/src/main/java/com/soybean/admin/entity/Device.java b/backend/src/main/java/com/soybean/admin/entity/Device.java index e77c832..29925ee 100644 --- a/backend/src/main/java/com/soybean/admin/entity/Device.java +++ b/backend/src/main/java/com/soybean/admin/entity/Device.java @@ -15,7 +15,9 @@ public class Device { private String deviceName; private String deviceCode; - private String status; + private String status; // 保留原字段用于兼容 + private String amtStatus; // AMT 状态:online, offline + private String agentStatus; // Agent 状态:online, offline private String ipAddress; private String macAddress; private String remark; diff --git a/backend/src/main/java/com/soybean/admin/service/DeviceService.java b/backend/src/main/java/com/soybean/admin/service/DeviceService.java index b03a7d6..f725809 100644 --- a/backend/src/main/java/com/soybean/admin/service/DeviceService.java +++ b/backend/src/main/java/com/soybean/admin/service/DeviceService.java @@ -21,6 +21,9 @@ public class DeviceService { @Autowired private DeviceMapper deviceMapper; + + @Autowired + private AmtDigestService amtDigestService; public PageResponse getDeviceList(PageRequest pageRequest) { Page page = new Page<>(pageRequest.getCurrent(), pageRequest.getSize()); @@ -102,26 +105,87 @@ public class DeviceService { // 总数 statistics.put("total", deviceMapper.selectCount(null)); - // 在线 + // AMT 在线 + wrapper.clear(); + wrapper.eq(Device::getAmtStatus, "online"); + statistics.put("amtOnline", deviceMapper.selectCount(wrapper)); + + // AMT 离线 + wrapper.clear(); + wrapper.eq(Device::getAmtStatus, "offline"); + statistics.put("amtOffline", deviceMapper.selectCount(wrapper)); + + // Agent 在线 + wrapper.clear(); + wrapper.eq(Device::getAgentStatus, "online"); + statistics.put("agentOnline", deviceMapper.selectCount(wrapper)); + + // Agent 离线 + wrapper.clear(); + wrapper.eq(Device::getAgentStatus, "offline"); + statistics.put("agentOffline", deviceMapper.selectCount(wrapper)); + + // 兼容旧的统计(基于 status 字段) wrapper.clear(); wrapper.eq(Device::getStatus, "online"); statistics.put("online", deviceMapper.selectCount(wrapper)); - // 离线 wrapper.clear(); wrapper.eq(Device::getStatus, "offline"); statistics.put("offline", deviceMapper.selectCount(wrapper)); - // 故障 wrapper.clear(); wrapper.eq(Device::getStatus, "fault"); statistics.put("fault", deviceMapper.selectCount(wrapper)); - // 维护中 - wrapper.clear(); - wrapper.eq(Device::getStatus, "maintenance"); - statistics.put("maintenance", deviceMapper.selectCount(wrapper)); - return statistics; } + + /** + * 检测设备 AMT 状态 + */ + public String checkAmtStatus(String ipAddress) { + if (ipAddress == null || ipAddress.isEmpty()) { + return "offline"; + } + + try { + // 使用简单的 Identify 请求检测 AMT 是否在线 + // 这里需要一个默认凭证或者从设备记录中获取凭证 + // 暂时返回离线,后续完善 + return "offline"; + } catch (Exception e) { + return "offline"; + } + } + + /** + * 更新设备 AMT 状态 + */ + public boolean updateAmtStatus(Long deviceId, String amtStatus) { + Device device = deviceMapper.selectById(deviceId); + if (device == null) { + return false; + } + + device.setAmtStatus(amtStatus); + device.setUpdateTime(LocalDateTime.now()); + + return deviceMapper.updateById(device) > 0; + } + + /** + * 更新设备 Agent 状态 + */ + public boolean updateAgentStatus(Long deviceId, String agentStatus) { + Device device = deviceMapper.selectById(deviceId); + if (device == null) { + return false; + } + + device.setAgentStatus(agentStatus); + device.setUpdateTime(LocalDateTime.now()); + + return deviceMapper.updateById(device) > 0; + } } diff --git a/backend/src/main/java/com/soybean/admin/task/AmtStatusCheckTask.java b/backend/src/main/java/com/soybean/admin/task/AmtStatusCheckTask.java new file mode 100644 index 0000000..b03908f --- /dev/null +++ b/backend/src/main/java/com/soybean/admin/task/AmtStatusCheckTask.java @@ -0,0 +1,126 @@ +package com.soybean.admin.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.soybean.admin.dto.AmtTestRequest; +import com.soybean.admin.entity.Device; +import com.soybean.admin.mapper.DeviceMapper; +import com.soybean.admin.service.AmtDigestService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * AMT 状态检测定时任务 + * 每 30 秒检测一次所有设备的 AMT 状态 + */ +@Component +public class AmtStatusCheckTask { + + private static final Logger logger = LoggerFactory.getLogger(AmtStatusCheckTask.class); + + @Autowired + private DeviceMapper deviceMapper; + + @Autowired + private AmtDigestService amtDigestService; + + // 线程池用于并发检测 + private final ExecutorService executorService = Executors.newFixedThreadPool(20); + + /** + * 每 30 秒执行一次 AMT 状态检测 + */ + @Scheduled(fixedRate = 30000, initialDelay = 10000) + public void checkAmtStatus() { + logger.info("开始 AMT 状态检测任务"); + + try { + // 查询所有有 IP 地址的设备 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.isNotNull(Device::getIpAddress); + wrapper.ne(Device::getIpAddress, ""); + + List devices = deviceMapper.selectList(wrapper); + + if (devices.isEmpty()) { + logger.info("没有需要检测的设备"); + return; + } + + logger.info("开始检测 {} 个设备的 AMT 状态", devices.size()); + + // 并发检测所有设备 + List> futures = devices.stream() + .map(device -> CompletableFuture.runAsync(() -> checkSingleDevice(device), executorService)) + .toList(); + + // 等待所有检测完成 + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + + logger.info("AMT 状态检测任务完成"); + + } catch (Exception e) { + logger.error("AMT 状态检测任务失败", e); + } + } + + /** + * 检测单个设备的 AMT 状态 + */ + private void checkSingleDevice(Device device) { + try { + String ipAddress = device.getIpAddress(); + + // 使用简单的 Identify 请求检测 AMT 是否在线 + // 不需要认证信息,只检测连接性 + boolean isOnline = checkAmtConnectivity(ipAddress); + + String newStatus = isOnline ? "online" : "offline"; + String oldStatus = device.getAmtStatus(); + + // 只有状态变化时才更新数据库 + if (!newStatus.equals(oldStatus)) { + device.setAmtStatus(newStatus); + device.setUpdateTime(LocalDateTime.now()); + deviceMapper.updateById(device); + + logger.info("设备 {} ({}) AMT 状态变化: {} -> {}", + device.getDeviceName(), ipAddress, oldStatus, newStatus); + } + + } catch (Exception e) { + logger.error("检测设备 {} AMT 状态失败: {}", device.getDeviceName(), e.getMessage()); + } + } + + /** + * 检测 AMT 连接性(不需要认证) + */ + private boolean checkAmtConnectivity(String ipAddress) { + try { + // 尝试连接 AMT HTTP 端口 + java.net.Socket socket = new java.net.Socket(); + socket.connect(new java.net.InetSocketAddress(ipAddress, 16992), 3000); + socket.close(); + return true; + } catch (Exception e) { + // HTTP 失败,尝试 HTTPS + try { + java.net.Socket socket = new java.net.Socket(); + socket.connect(new java.net.InetSocketAddress(ipAddress, 16993), 3000); + socket.close(); + return true; + } catch (Exception ex) { + return false; + } + } + } +} diff --git a/check_device_columns.bat b/check_device_columns.bat new file mode 100644 index 0000000..048d930 --- /dev/null +++ b/check_device_columns.bat @@ -0,0 +1,4 @@ +@echo off +echo Checking device table columns... +mysql -h localhost -P 3306 -u root -proot soybean_admin < check_device_columns.sql +pause diff --git a/check_device_columns.sql b/check_device_columns.sql new file mode 100644 index 0000000..6736928 --- /dev/null +++ b/check_device_columns.sql @@ -0,0 +1,7 @@ +-- 检查设备表的列结构 +SHOW COLUMNS FROM sys_device; + +-- 查看设备表的数据 +SELECT id, device_name, device_code, status, amt_status, agent_status, ip_address +FROM sys_device +LIMIT 10; diff --git a/rebuild_and_start_realtime.bat b/rebuild_and_start_realtime.bat new file mode 100644 index 0000000..d8b0bf6 --- /dev/null +++ b/rebuild_and_start_realtime.bat @@ -0,0 +1,44 @@ +@echo off +echo ======================================== +echo AMT 实时状态检测 - 重新编译和启动 +echo ======================================== +echo. + +echo [1/3] 停止现有后端服务... +taskkill /F /IM java.exe 2>nul +timeout /t 2 >nul + +echo. +echo [2/3] 重新编译后端... +cd backend +call mvn clean package -DskipTests +if %ERRORLEVEL% NEQ 0 ( + echo 编译失败! + pause + exit /b 1 +) + +echo. +echo [3/3] 启动后端服务... +echo 后端将在 10 秒后开始第一次 AMT 状态检测 +echo 之后每 30 秒自动检测一次 +echo. +start "Soybean Backend" cmd /k "java -jar target\soybean-admin-1.0.0.jar" + +cd .. + +echo. +echo ======================================== +echo 启动完成! +echo ======================================== +echo. +echo 后端服务已启动,请等待: +echo - 10 秒后开始第一次 AMT 状态检测 +echo - 之后每 30 秒自动检测一次 +echo. +echo 前端功能: +echo - 打开设备列表页面 +echo - 每 10 秒自动刷新状态 +echo - 可点击"刷新状态"按钮手动刷新 +echo. +pause diff --git a/src/typings/api/device.d.ts b/src/typings/api/device.d.ts index fd9d038..10d8e22 100644 --- a/src/typings/api/device.d.ts +++ b/src/typings/api/device.d.ts @@ -10,8 +10,12 @@ declare namespace Api { deviceCode: string; /** 设备类型 */ type: 'server' | 'switch' | 'router' | 'storage' | 'other'; - /** 设备状态 */ + /** 设备状态(保留用于兼容) */ status: 'online' | 'offline' | 'fault' | 'maintenance'; + /** AMT状态 */ + amtStatus: 'online' | 'offline'; + /** Agent状态 */ + agentStatus: 'online' | 'offline'; /** IP地址 */ ipAddress?: string; /** MAC地址 */ @@ -38,8 +42,12 @@ declare namespace Api { deviceCode: string; /** 设备类型 */ type: string; - /** 设备状态 */ + /** 设备状态(保留用于兼容) */ status: string; + /** AMT状态 */ + amtStatus?: string; + /** Agent状态 */ + agentStatus?: string; /** IP地址 */ ipAddress?: string; /** MAC地址 */ diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index 59960dc..a98eb01 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -28,6 +28,7 @@ declare module 'vue' { IconMdiAlertCircle: typeof import('~icons/mdi/alert-circle')['default'] IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default'] IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default'] + IconMdiAutorenew: typeof import('~icons/mdi/autorenew')['default'] IconMdiCheckboxMarkedCircle: typeof import('~icons/mdi/checkbox-marked-circle')['default'] IconMdiCheckboxMultipleBlankOutline: typeof import('~icons/mdi/checkbox-multiple-blank-outline')['default'] IconMdiCheckboxMultipleMarked: typeof import('~icons/mdi/checkbox-multiple-marked')['default'] @@ -169,6 +170,7 @@ declare global { const IconMdiAlertCircle: typeof import('~icons/mdi/alert-circle')['default'] const IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default'] const IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default'] + const IconMdiAutorenew: typeof import('~icons/mdi/autorenew')['default'] const IconMdiCheckboxMarkedCircle: typeof import('~icons/mdi/checkbox-marked-circle')['default'] const IconMdiCheckboxMultipleBlankOutline: typeof import('~icons/mdi/checkbox-multiple-blank-outline')['default'] const IconMdiCheckboxMultipleMarked: typeof import('~icons/mdi/checkbox-multiple-marked')['default'] diff --git a/src/views/device/list/index.vue b/src/views/device/list/index.vue index 8c1d77d..7a46492 100644 --- a/src/views/device/list/index.vue +++ b/src/views/device/list/index.vue @@ -9,19 +9,24 @@ - + - + - + + + + @@ -56,8 +61,16 @@ 重置 + + + 刷新状态 + + + + 自动刷新中 + 新增设备 @@ -471,7 +484,7 @@