2025-12-26 22:24:12 +08:00
|
|
|
|
using System.Text.Json;
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
namespace SHH.CameraSdk
|
2025-12-26 21:19:43 +08:00
|
|
|
|
{
|
2025-12-26 22:24:12 +08:00
|
|
|
|
public class FileStorageService : IStorageService
|
2025-12-26 21:19:43 +08:00
|
|
|
|
{
|
2025-12-26 22:24:12 +08:00
|
|
|
|
public int ProcessId { get; }
|
|
|
|
|
|
private readonly string _baseDir;
|
|
|
|
|
|
private readonly string _devicesPath;
|
|
|
|
|
|
private readonly SemaphoreSlim _fileLock = new SemaphoreSlim(1, 1);
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
// [关键修复] 配置序列化选项,解决“只存属性不存字段”的问题
|
|
|
|
|
|
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
|
|
|
|
|
|
{
|
|
|
|
|
|
WriteIndented = true, // 格式化 JSON,让人眼可读
|
|
|
|
|
|
IncludeFields = true, // [核心] 允许序列化 public int Id; 这种字段
|
|
|
|
|
|
PropertyNameCaseInsensitive = true, // 忽略大小写差异
|
|
|
|
|
|
NumberHandling = JsonNumberHandling.AllowReadingFromString // 允许 "8000" 读为 int 8000
|
|
|
|
|
|
};
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
public FileStorageService(int processId)
|
2025-12-26 21:19:43 +08:00
|
|
|
|
{
|
2025-12-26 22:24:12 +08:00
|
|
|
|
ProcessId = processId;
|
|
|
|
|
|
|
|
|
|
|
|
_baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", $"Process_{processId}");
|
|
|
|
|
|
_devicesPath = Path.Combine(_baseDir, "devices.json");
|
|
|
|
|
|
|
|
|
|
|
|
if (!Directory.Exists(_baseDir)) Directory.CreateDirectory(_baseDir);
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"[Storage] 路径: {_devicesPath}");
|
2025-12-26 21:19:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
public async Task SaveDevicesAsync(IEnumerable<VideoSourceConfig> configs)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _fileLock.WaitAsync();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// [调试] 打印正在保存的数量,确保 Manager 传过来的数据是对的
|
|
|
|
|
|
// Console.WriteLine($"[Debug] 正在保存 {configs.Count()} 台设备...");
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
var json = JsonSerializer.Serialize(configs, _jsonOptions);
|
|
|
|
|
|
await File.WriteAllTextAsync(_devicesPath, json);
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
// [调试] 打印部分 JSON 内容,验证是否为空对象 "{}"
|
|
|
|
|
|
// if (json.Length < 200) Console.WriteLine($"[Debug] JSON 内容: {json}");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"[Storage] ❌ 保存配置失败: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_fileLock.Release();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
public async Task<List<VideoSourceConfig>> LoadDevicesAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!File.Exists(_devicesPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("[Storage] ⚠️ 配置文件不存在,将使用空列表");
|
|
|
|
|
|
return new List<VideoSourceConfig>();
|
|
|
|
|
|
}
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
await _fileLock.WaitAsync();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var json = await File.ReadAllTextAsync(_devicesPath);
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
if (string.IsNullOrWhiteSpace(json)) return new List<VideoSourceConfig>();
|
|
|
|
|
|
|
|
|
|
|
|
// [调试] 打印读取到的原始 JSON
|
|
|
|
|
|
// Console.WriteLine($"[Debug] 读取文件内容: {json.Substring(0, Math.Min(json.Length, 100))}...");
|
|
|
|
|
|
|
|
|
|
|
|
var list = JsonSerializer.Deserialize<List<VideoSourceConfig>>(json, _jsonOptions);
|
|
|
|
|
|
|
|
|
|
|
|
// 二次校验:如果读出来列表不为空,但 ID 全是 0,说明序列化还是没对上
|
|
|
|
|
|
if (list != null && list.Count > 0 && list[0].Id == 0 && list[0].Port == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("[Storage] ⚠️ 警告:读取到设备,但字段似乎为空。请检查 VideoSourceConfig 是否使用了 private 属性?");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return list ?? new List<VideoSourceConfig>();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"[Storage] ❌ 读取配置失败: {ex.Message}");
|
|
|
|
|
|
// 出错时返回空列表,不要抛出异常,否则 StartAsync 会崩溃
|
|
|
|
|
|
return new List<VideoSourceConfig>();
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
_fileLock.Release();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
|
|
// 日志部分 (保持空实现以免干扰)
|
|
|
|
|
|
// ==================================================================
|
|
|
|
|
|
public Task AppendSystemLogAsync(string action, string ip, string path) => Task.CompletedTask;
|
|
|
|
|
|
public Task<List<string>> GetSystemLogsAsync(int count) => Task.FromResult(new List<string>());
|
|
|
|
|
|
public Task AppendDeviceLogAsync(int deviceId, string message) => Task.CompletedTask;
|
|
|
|
|
|
public Task<List<string>> GetDeviceLogsAsync(int deviceId, int count) => Task.FromResult(new List<string>());
|
2025-12-26 21:19:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|