You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
14 KiB
C#
375 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Haoliang.Models.System;
|
|
|
|
namespace Haoliang.Core.Services
|
|
{
|
|
public interface ISystemConfigService
|
|
{
|
|
Task<SystemConfig> GetConfigAsync(string configKey);
|
|
Task<Dictionary<string, string>> GetAllConfigsAsync();
|
|
Task<SystemConfig> SetConfigAsync(string configKey, string configValue);
|
|
Task<bool> DeleteConfigAsync(string configKey);
|
|
Task<bool> ConfigExistsAsync(string configKey);
|
|
Task<IEnumerable<SystemConfig>> GetConfigsByCategoryAsync(string category);
|
|
Task<bool> ValidateConfigAsync(SystemConfig config);
|
|
Task RefreshConfigCacheAsync();
|
|
Task<T> GetConfigValueAsync<T>(string configKey, T defaultValue = default);
|
|
}
|
|
|
|
public interface ILoggingService
|
|
{
|
|
Task LogAsync(LogLevel logLevel, string message, Exception exception = null, Dictionary<string, object> properties = null);
|
|
Task LogErrorAsync(string message, Exception exception = null, Dictionary<string, object> properties = null);
|
|
Task LogWarningAsync(string message, Dictionary<string, object> properties = null);
|
|
Task LogInfoAsync(string message, Dictionary<string, object> properties = null);
|
|
Task LogDebugAsync(string message, Dictionary<string, object> properties = null);
|
|
Task LogTraceAsync(string message, Dictionary<string, object> properties = null);
|
|
Task<IEnumerable<LogEntry>> GetLogsAsync(LogLevel? logLevel = null, DateTime? startDate = null, DateTime? endDate = null, string category = null);
|
|
Task<IEnumerable<LogEntry>> GetErrorLogsAsync(DateTime? startDate = null, DateTime? endDate = null);
|
|
Task<int> GetLogCountAsync(LogLevel? logLevel = null, DateTime? startDate = null, DateTime? endDate = null);
|
|
Task ArchiveLogsAsync(int daysToKeep = 30);
|
|
Task ClearLogsAsync();
|
|
}
|
|
|
|
public interface ISchedulerService
|
|
{
|
|
Task StartSchedulerAsync();
|
|
Task StopSchedulerAsync();
|
|
Task ScheduleTaskAsync(ScheduledTask task);
|
|
Task<bool> RemoveTaskAsync(string taskId);
|
|
Task<IEnumerable<ScheduledTask>> GetAllScheduledTasksAsync();
|
|
Task<ScheduledTask> GetTaskByIdAsync(string taskId);
|
|
Task ExecuteTaskAsync(string taskId);
|
|
Task<TaskExecutionResult> GetTaskExecutionResultAsync(string taskId);
|
|
Task<bool> IsTaskRunningAsync(string taskId);
|
|
Task<string> ScheduleRecurringTaskAsync(string taskName, Action taskAction, TimeSpan interval);
|
|
}
|
|
|
|
public interface ICachingService
|
|
{
|
|
Task<T> GetAsync<T>(string key);
|
|
Task SetAsync<T>(string key, T value, TimeSpan? expiration = null);
|
|
Task<bool> RemoveAsync(string key);
|
|
Task<bool> ExistsAsync(string key);
|
|
Task ClearAsync();
|
|
Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null);
|
|
Task<IEnumerable<string>> GetAllKeysAsync();
|
|
Task<bool> RefreshAsync<T>(string key);
|
|
}
|
|
|
|
public class SystemConfigManager : ISystemConfigService
|
|
{
|
|
private readonly ISystemConfigRepository _configRepository;
|
|
private readonly ICachingService _cachingService;
|
|
|
|
public SystemConfigManager(
|
|
ISystemConfigRepository configRepository,
|
|
ICachingService cachingService)
|
|
{
|
|
_configRepository = configRepository;
|
|
_cachingService = cachingService;
|
|
}
|
|
|
|
public async Task<SystemConfig> GetConfigAsync(string configKey)
|
|
{
|
|
// 先从缓存获取
|
|
var cachedConfig = await _cachingService.GetAsync<SystemConfig>($"config_{configKey}");
|
|
if (cachedConfig != null)
|
|
{
|
|
return cachedConfig;
|
|
}
|
|
|
|
// 缓存未命中,从数据库获取
|
|
var config = await _configRepository.GetByKeyAsync(configKey);
|
|
if (config != null)
|
|
{
|
|
// 存入缓存
|
|
await _cachingService.SetAsync($"config_{configKey}", config, TimeSpan.FromMinutes(30));
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
public async Task<Dictionary<string, string>> GetAllConfigsAsync()
|
|
{
|
|
var configs = await _configRepository.GetAllAsync();
|
|
var configDict = new Dictionary<string, string>();
|
|
|
|
foreach (var config in configs)
|
|
{
|
|
configDict[config.ConfigKey] = config.ConfigValue;
|
|
}
|
|
|
|
return configDict;
|
|
}
|
|
|
|
public async Task<SystemConfig> SetConfigAsync(string configKey, string configValue)
|
|
{
|
|
// 验证配置
|
|
var config = new SystemConfig
|
|
{
|
|
ConfigKey = configKey,
|
|
ConfigValue = configValue,
|
|
Category = "General",
|
|
LastUpdated = DateTime.Now
|
|
};
|
|
|
|
var validationErrors = await ValidateConfigAsync(config);
|
|
if (validationErrors.Count > 0)
|
|
{
|
|
throw new InvalidOperationException($"Config validation failed: {string.Join(", ", validationErrors)}");
|
|
}
|
|
|
|
// 检查配置是否已存在
|
|
var existingConfig = await _configRepository.GetByKeyAsync(configKey);
|
|
if (existingConfig != null)
|
|
{
|
|
config.ConfigId = existingConfig.ConfigId;
|
|
config.CreateTime = existingConfig.CreateTime;
|
|
}
|
|
|
|
config.LastUpdated = DateTime.Now;
|
|
var updatedConfig = await _configRepository.UpsertAsync(config);
|
|
|
|
// 更新缓存
|
|
await _cachingService.SetAsync($"config_{configKey}", updatedConfig, TimeSpan.FromMinutes(30));
|
|
await _cachingService.RemoveAsync("all_configs");
|
|
|
|
return updatedConfig;
|
|
}
|
|
|
|
public async Task<bool> DeleteConfigAsync(string configKey)
|
|
{
|
|
var result = await _configRepository.DeleteByKeyAsync(configKey);
|
|
if (result)
|
|
{
|
|
// 清除缓存
|
|
await _cachingService.RemoveAsync($"config_{configKey}");
|
|
await _cachingService.RemoveAsync("all_configs");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<bool> ConfigExistsAsync(string configKey)
|
|
{
|
|
// 先检查缓存
|
|
var existsInCache = await _cachingService.ExistsAsync($"config_{configKey}");
|
|
if (existsInCache)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return await _configRepository.KeyExistsAsync(configKey);
|
|
}
|
|
|
|
public async Task<IEnumerable<SystemConfig>> GetConfigsByCategoryAsync(string category)
|
|
{
|
|
return await _configRepository.GetByCategoryAsync(category);
|
|
}
|
|
|
|
public async Task<bool> ValidateConfigAsync(SystemConfig config)
|
|
{
|
|
var errors = new List<string>();
|
|
|
|
// 验证配置键
|
|
if (string.IsNullOrWhiteSpace(config.ConfigKey))
|
|
{
|
|
errors.Add("Config key cannot be empty");
|
|
}
|
|
|
|
// 验证配置值
|
|
if (config.ConfigValue == null)
|
|
{
|
|
errors.Add("Config value cannot be null");
|
|
}
|
|
|
|
// 根据不同的配置键进行特定验证
|
|
switch (config.ConfigKey)
|
|
{
|
|
case "Database.ConnectionString":
|
|
if (!IsValidConnectionString(config.ConfigValue))
|
|
{
|
|
errors.Add("Invalid database connection string");
|
|
}
|
|
break;
|
|
|
|
case "Logging.Level":
|
|
if (!IsValidLogLevel(config.ConfigValue))
|
|
{
|
|
errors.Add("Invalid log level");
|
|
}
|
|
break;
|
|
|
|
case "Collection.Interval":
|
|
if (!IsValidInterval(config.ConfigValue))
|
|
{
|
|
errors.Add("Invalid collection interval");
|
|
}
|
|
break;
|
|
|
|
case "Security.JwtSecret":
|
|
if (string.IsNullOrWhiteSpace(config.ConfigValue) || config.ConfigValue.Length < 16)
|
|
{
|
|
errors.Add("JWT secret must be at least 16 characters long");
|
|
}
|
|
break;
|
|
}
|
|
|
|
return errors.Count == 0;
|
|
}
|
|
|
|
public async Task RefreshConfigCacheAsync()
|
|
{
|
|
// 清除所有配置缓存
|
|
await _cachingService.RemoveAsync("all_configs");
|
|
|
|
// 重新加载常用配置
|
|
var commonKeys = new[] { "Database.ConnectionString", "Logging.Level", "Collection.Interval" };
|
|
foreach (var key in commonKeys)
|
|
{
|
|
await GetConfigAsync(key);
|
|
}
|
|
}
|
|
|
|
public async Task<T> GetConfigValueAsync<T>(string configKey, T defaultValue = default)
|
|
{
|
|
var config = await GetConfigAsync(configKey);
|
|
if (config == null)
|
|
{
|
|
return defaultValue;
|
|
}
|
|
|
|
try
|
|
{
|
|
return (T)Convert.ChangeType(config.ConfigValue, typeof(T));
|
|
}
|
|
catch
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
private bool IsValidConnectionString(string connectionString)
|
|
{
|
|
// 简单的连接字符串验证
|
|
return !string.IsNullOrWhiteSpace(connectionString) &&
|
|
(connectionString.Contains("Server=") || connectionString.Contains("Host="));
|
|
}
|
|
|
|
private bool IsValidLogLevel(string logLevel)
|
|
{
|
|
var validLevels = new[] { "Trace", "Debug", "Information", "Warning", "Error", "Critical", "None" };
|
|
return Array.Exists(validLevels, level => level.Equals(logLevel, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
private bool IsValidInterval(string interval)
|
|
{
|
|
if (int.TryParse(interval, out int seconds))
|
|
{
|
|
return seconds >= 5 && seconds <= 3600; // 5秒到1小时
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class LoggingManager : ILoggingService
|
|
{
|
|
private readonly ILogRepository _logRepository;
|
|
private readonly ICachingService _cachingService;
|
|
|
|
public LoggingManager(
|
|
ILogRepository logRepository,
|
|
ICachingService cachingService)
|
|
{
|
|
_logRepository = logRepository;
|
|
_cachingService = cachingService;
|
|
}
|
|
|
|
public async Task LogAsync(LogLevel logLevel, string message, Exception exception = null, Dictionary<string, object> properties = null)
|
|
{
|
|
var logEntry = new LogEntry
|
|
{
|
|
LogLevel = logLevel,
|
|
Message = message,
|
|
ExceptionMessage = exception?.Message,
|
|
StackTrace = exception?.StackTrace,
|
|
Properties = properties,
|
|
Timestamp = DateTime.Now,
|
|
Category = "General"
|
|
};
|
|
|
|
// 异步保存到数据库
|
|
await _logRepository.AddAsync(logEntry);
|
|
|
|
// 控制台输出(开发环境)
|
|
if (IsDevelopmentEnvironment())
|
|
{
|
|
Console.WriteLine($"[{logLevel}] {message}");
|
|
if (exception != null)
|
|
{
|
|
Console.WriteLine(exception.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task LogErrorAsync(string message, Exception exception = null, Dictionary<string, object> properties = null)
|
|
{
|
|
await LogAsync(LogLevel.Error, message, exception, properties);
|
|
}
|
|
|
|
public async Task LogWarningAsync(string message, Dictionary<string, object> properties = null)
|
|
{
|
|
await LogAsync(LogLevel.Warning, message, null, properties);
|
|
}
|
|
|
|
public async Task LogInfoAsync(string message, Dictionary<string, object> properties = null)
|
|
{
|
|
await LogAsync(LogLevel.Information, message, null, properties);
|
|
}
|
|
|
|
public async Task LogDebugAsync(string message, Dictionary<string, object> properties = null)
|
|
{
|
|
await LogAsync(LogLevel.Debug, message, null, properties);
|
|
}
|
|
|
|
public async Task LogTraceAsync(string message, Dictionary<string, object> properties = null)
|
|
{
|
|
await LogAsync(LogLevel.Trace, message, null, properties);
|
|
}
|
|
|
|
public async Task<IEnumerable<LogEntry>> GetLogsAsync(LogLevel? logLevel = null, DateTime? startDate = null, DateTime? endDate = null, string category = null)
|
|
{
|
|
return await _logRepository.GetLogsAsync(logLevel, startDate, endDate, category);
|
|
}
|
|
|
|
public async Task<IEnumerable<LogEntry>> GetErrorLogsAsync(DateTime? startDate = null, DateTime? endDate = null)
|
|
{
|
|
return await _logRepository.GetLogsAsync(LogLevel.Error, startDate, endDate, null);
|
|
}
|
|
|
|
public async Task<int> GetLogCountAsync(LogLevel? logLevel = null, DateTime? startDate = null, DateTime? endDate = null)
|
|
{
|
|
return await _logRepository.GetLogCountAsync(logLevel, startDate, endDate);
|
|
}
|
|
|
|
public async Task ArchiveLogsAsync(int daysToKeep = 30)
|
|
{
|
|
var cutoffDate = DateTime.Now.AddDays(-daysToKeep);
|
|
await _logRepository.ArchiveLogsAsync(cutoffDate);
|
|
}
|
|
|
|
public async Task ClearLogsAsync()
|
|
{
|
|
await _logRepository.ClearLogsAsync();
|
|
}
|
|
|
|
private bool IsDevelopmentEnvironment()
|
|
{
|
|
// 简单判断是否为开发环境
|
|
return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";
|
|
}
|
|
}
|
|
} |