lvfengfree eebbacafde feat: 实现OS设备扫描和UUID绑定功能
- 添加OsDevice模型和OsDevicesController
- 实现WindowsScannerService用于网络扫描和WMI查询
- 添加AMT设备UUID查询功能(从CIM_ComputerSystemPackage获取PlatformGUID)
- 实现PlatformGUID到标准UUID格式的转换(字节序转换)
- 修复HardwareInfoRepository保存UUID的问题
- 前端添加OS设备管理页面和UUID获取/刷新按钮
- 添加数据库迁移脚本
2026-01-21 16:16:48 +08:00

208 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AmtScanner.Api.Data;
using AmtScanner.Api.Models;
using AmtScanner.Api.Repositories;
namespace AmtScanner.Api.Services;
public interface IHardwareInfoService
{
Task<HardwareInfoDto> GetHardwareInfoAsync(long deviceId, bool forceRefresh = false);
Task<List<BatchHardwareInfoResult>> GetBatchHardwareInfoAsync(List<long> deviceIds, bool forceRefresh = false);
}
public class HardwareInfoService : IHardwareInfoService
{
private readonly IHardwareInfoRepository _repository;
private readonly IAmtHardwareQueryService _hardwareQueryService;
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<HardwareInfoService> _logger;
private readonly TimeSpan _cacheExpiration = TimeSpan.FromHours(24);
public HardwareInfoService(
IHardwareInfoRepository repository,
IAmtHardwareQueryService hardwareQueryService,
IServiceScopeFactory scopeFactory,
ILogger<HardwareInfoService> logger)
{
_repository = repository;
_hardwareQueryService = hardwareQueryService;
_scopeFactory = scopeFactory;
_logger = logger;
}
public async Task<HardwareInfoDto> GetHardwareInfoAsync(long deviceId, bool forceRefresh = false)
{
// Check cache
var cached = await _repository.GetByDeviceIdAsync(deviceId);
if (cached != null && !forceRefresh &&
DateTime.UtcNow - cached.LastUpdated < _cacheExpiration)
{
_logger.LogInformation("Returning cached hardware info for device {DeviceId}", deviceId);
return MapToDto(cached);
}
// Query from device
_logger.LogInformation("Querying hardware info from device {DeviceId}", deviceId);
using var scope = _scopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var credentialService = scope.ServiceProvider.GetRequiredService<ICredentialService>();
var device = await context.AmtDevices.FindAsync(deviceId);
if (device == null)
{
throw new Exception($"Device {deviceId} not found");
}
var credential = await credentialService.GetDefaultCredentialAsync();
if (credential == null)
{
throw new Exception("No default credential found");
}
var password = credentialService.DecryptPassword(credential.Password);
// Determine open ports from device IP (simplified)
var openPorts = new List<int>();
if (device.IpAddress.EndsWith("111"))
{
openPorts.Add(16992); // HTTP
openPorts.Add(623);
}
else if (device.IpAddress.EndsWith("112"))
{
openPorts.Add(16993); // HTTPS
}
else
{
// Default: try both
openPorts.Add(16992);
openPorts.Add(16993);
}
var hardwareInfo = await _hardwareQueryService.QueryHardwareInfoAsync(
device.IpAddress,
credential.Username,
password,
openPorts);
hardwareInfo.DeviceId = deviceId;
// 如果查询到了 UUID保存到 AmtDevice
if (!string.IsNullOrEmpty(hardwareInfo.SystemUuid) && device.SystemUuid != hardwareInfo.SystemUuid)
{
device.SystemUuid = hardwareInfo.SystemUuid;
context.AmtDevices.Update(device);
await context.SaveChangesAsync();
_logger.LogInformation("Updated device {DeviceId} with UUID: {Uuid}", deviceId, hardwareInfo.SystemUuid);
}
// Save to cache
await _repository.SaveAsync(hardwareInfo);
// Reload to get the saved entity with ID
cached = await _repository.GetByDeviceIdAsync(deviceId);
return MapToDto(cached!);
}
public async Task<List<BatchHardwareInfoResult>> GetBatchHardwareInfoAsync(
List<long> deviceIds,
bool forceRefresh = false)
{
var results = new List<BatchHardwareInfoResult>();
var semaphore = new SemaphoreSlim(10); // Max 10 concurrent queries
var tasks = deviceIds.Select(async deviceId =>
{
await semaphore.WaitAsync();
try
{
var data = await GetHardwareInfoAsync(deviceId, forceRefresh);
return new BatchHardwareInfoResult
{
DeviceId = deviceId,
Success = true,
Data = data
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get hardware info for device {DeviceId}", deviceId);
return new BatchHardwareInfoResult
{
DeviceId = deviceId,
Success = false,
Error = ex.Message
};
}
finally
{
semaphore.Release();
}
});
results = (await Task.WhenAll(tasks)).ToList();
return results;
}
private HardwareInfoDto MapToDto(HardwareInfo hardwareInfo)
{
return new HardwareInfoDto
{
DeviceId = hardwareInfo.DeviceId,
IpAddress = hardwareInfo.Device.IpAddress,
LastUpdated = hardwareInfo.LastUpdated,
SystemInfo = new SystemInfoDto
{
Manufacturer = hardwareInfo.SystemManufacturer,
Model = hardwareInfo.SystemModel,
SerialNumber = hardwareInfo.SystemSerialNumber,
Uuid = hardwareInfo.SystemUuid
},
Processor = new ProcessorInfoDto
{
Model = hardwareInfo.ProcessorModel,
Cores = hardwareInfo.ProcessorCores,
Threads = hardwareInfo.ProcessorThreads,
MaxClockSpeed = hardwareInfo.ProcessorMaxClockSpeed,
CurrentClockSpeed = hardwareInfo.ProcessorCurrentClockSpeed
},
Memory = new MemoryInfoDto
{
TotalCapacity = hardwareInfo.TotalMemoryBytes,
TotalCapacityGB = hardwareInfo.TotalMemoryBytes.HasValue
? (int)(hardwareInfo.TotalMemoryBytes.Value / 1024 / 1024 / 1024)
: null,
Modules = hardwareInfo.MemoryModules.Select(m => new MemoryModuleDto
{
Slot = m.SlotLocation,
Capacity = m.CapacityBytes,
CapacityGB = m.CapacityBytes.HasValue
? (int)(m.CapacityBytes.Value / 1024 / 1024 / 1024)
: null,
Speed = m.SpeedMHz,
Type = m.MemoryType,
Manufacturer = m.Manufacturer,
PartNumber = m.PartNumber,
SerialNumber = m.SerialNumber
}).ToList()
},
Storage = new StorageInfoDto
{
Devices = hardwareInfo.StorageDevices.Select(d => new StorageDeviceDto
{
DeviceId = d.DeviceId,
Model = d.Model,
Capacity = d.CapacityBytes,
CapacityGB = d.CapacityBytes.HasValue
? (int)(d.CapacityBytes.Value / 1024 / 1024 / 1024)
: null,
InterfaceType = d.InterfaceType
}).ToList()
}
};
}
}