using System.Security.Cryptography; using System.Text; using System.Text.Json; using WorkCameraExport.Models; using WorkCameraExport.Services.Interfaces; namespace WorkCameraExport.Services { /// /// 配置服务实现 - 负责配置文件和登录凭证的管理 /// public class ConfigService : IConfigService { private readonly string _appDataPath; private readonly string _configFilePath; private readonly string _credentialsFilePath; private readonly string _tempPath; private readonly string _logPath; // 用于加密凭证的密钥(AES-256 需要 32 字节密钥,16 字节 IV) private static readonly byte[] EncryptionKey = Encoding.UTF8.GetBytes("WorkCameraExport2025SecretKey!!!"); // 32 bytes private static readonly byte[] EncryptionIV = Encoding.UTF8.GetBytes("WCE2025InitVect!"); // 16 bytes private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true, PropertyNameCaseInsensitive = true }; public ConfigService() { _appDataPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "WorkCameraExport"); _configFilePath = Path.Combine(_appDataPath, "config.json"); _credentialsFilePath = Path.Combine(_appDataPath, "credentials.dat"); _tempPath = Path.Combine(_appDataPath, "temp"); _logPath = Path.Combine(_appDataPath, "logs"); EnsureDirectoriesExist(); } /// /// 用于测试的构造函数,允许指定自定义路径 /// public ConfigService(string appDataPath) { _appDataPath = appDataPath; _configFilePath = Path.Combine(_appDataPath, "config.json"); _credentialsFilePath = Path.Combine(_appDataPath, "credentials.dat"); _tempPath = Path.Combine(_appDataPath, "temp"); _logPath = Path.Combine(_appDataPath, "logs"); EnsureDirectoriesExist(); } #region IConfigService 实现 public string AppDataPath => _appDataPath; public string TempPath => _tempPath; public string LogPath => _logPath; public bool HasSavedCredentials => File.Exists(_credentialsFilePath); public AppConfig LoadConfig() { try { if (File.Exists(_configFilePath)) { var json = File.ReadAllText(_configFilePath); var config = JsonSerializer.Deserialize(json, JsonOptions); if (config != null) { config.Validate(); return config; } } } catch { // 加载失败时返回默认配置 } return AppConfig.CreateDefault(); } public void SaveConfig(AppConfig config) { config.Validate(); var json = JsonSerializer.Serialize(config, JsonOptions); File.WriteAllText(_configFilePath, json); } public void SaveCredentials(string serverUrl, string token, string username) { var credentials = new CredentialsData { ServerUrl = serverUrl, Token = token, Username = username, SavedAt = DateTime.Now }; var json = JsonSerializer.Serialize(credentials, JsonOptions); var encrypted = Encrypt(json); File.WriteAllBytes(_credentialsFilePath, encrypted); } public (string ServerUrl, string Token, string Username)? LoadCredentials() { try { if (!File.Exists(_credentialsFilePath)) return null; var encrypted = File.ReadAllBytes(_credentialsFilePath); var json = Decrypt(encrypted); var credentials = JsonSerializer.Deserialize(json, JsonOptions); if (credentials != null && !string.IsNullOrEmpty(credentials.ServerUrl) && !string.IsNullOrEmpty(credentials.Token)) { return (credentials.ServerUrl, credentials.Token, credentials.Username); } } catch { // 解密失败时清除凭证 ClearCredentials(); } return null; } public void ClearCredentials() { try { if (File.Exists(_credentialsFilePath)) { File.Delete(_credentialsFilePath); } } catch { // 忽略删除错误 } } public void CleanTempFiles() { try { if (Directory.Exists(_tempPath)) { var directory = new DirectoryInfo(_tempPath); foreach (var file in directory.GetFiles()) { try { file.Delete(); } catch { } } foreach (var dir in directory.GetDirectories()) { try { dir.Delete(true); } catch { } } } } catch { // 忽略清理错误 } } #endregion #region 私有方法 private void EnsureDirectoriesExist() { if (!Directory.Exists(_appDataPath)) Directory.CreateDirectory(_appDataPath); if (!Directory.Exists(_tempPath)) Directory.CreateDirectory(_tempPath); if (!Directory.Exists(_logPath)) Directory.CreateDirectory(_logPath); } private static byte[] Encrypt(string plainText) { using var aes = Aes.Create(); aes.Key = EncryptionKey; aes.IV = EncryptionIV; using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using var ms = new MemoryStream(); using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) using (var sw = new StreamWriter(cs)) { sw.Write(plainText); } return ms.ToArray(); } private static string Decrypt(byte[] cipherText) { using var aes = Aes.Create(); aes.Key = EncryptionKey; aes.IV = EncryptionIV; using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using var ms = new MemoryStream(cipherText); using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); using var sr = new StreamReader(cs); return sr.ReadToEnd(); } #endregion #region 内部类 private class CredentialsData { public string ServerUrl { get; set; } = ""; public string Token { get; set; } = ""; public string Username { get; set; } = ""; public DateTime SavedAt { get; set; } } #endregion } }