using System; using System.Collections.Generic; using System.Threading.Tasks; using Haoliang.Models.System; namespace Haoliang.Core.Services { public interface IRulesService { /// /// Get all business rules /// Task> GetAllRulesAsync(); /// /// Create or update business rule /// Task CreateOrUpdateRuleAsync(BusinessRuleConfig rule); /// /// Delete business rule /// Task DeleteRuleAsync(int ruleId); /// /// Get statistics rules /// Task> GetStatisticsRulesAsync(); /// /// Update statistics rules /// Task UpdateStatisticsRulesAsync(List rules); /// /// Validate business rule expression /// Task ValidateRuleAsync(BusinessRuleConfig rule); /// /// Evaluate business rule against data /// Task EvaluateRuleAsync(BusinessRuleConfig rule, object data); /// /// Get rule execution history /// Task> GetRuleExecutionHistoryAsync(int ruleId, DateTime? startDate = null, DateTime? endDate = null); } public class RulesService : IRulesService { private readonly ISystemRepository _systemRepository; private readonly IProductionRepository _productionRepository; private readonly IAlarmRepository _alarmRepository; private readonly ICacheService _cacheService; public RulesService( ISystemRepository systemRepository, IProductionRepository productionRepository, IAlarmRepository alarmRepository, ICacheService cacheService) { _systemRepository = systemRepository; _productionRepository = productionRepository; _alarmRepository = alarmRepository; _cacheService = cacheService; } public async Task> GetAllRulesAsync() { return await _cacheService.GetOrSetAllRulesAsync(() => _systemRepository.GetAllBusinessRulesAsync()); } public async Task CreateOrUpdateRuleAsync(BusinessRuleConfig rule) { // Validate rule before saving var validationResult = await ValidateRuleAsync(rule); if (!validationResult.IsValid) { throw new ArgumentException($"Invalid rule: {validationResult.ErrorMessage}"); } // Save rule to repository var savedRule = await _systemRepository.SaveBusinessRuleAsync(rule); // Clear cache _cacheService.InvalidateRulesCache(); return savedRule; } public async Task DeleteRuleAsync(int ruleId) { var result = await _systemRepository.DeleteBusinessRuleAsync(ruleId); if (result) { _cacheService.InvalidateRulesCache(); } return result; } public async Task> GetStatisticsRulesAsync() { return await _cacheService.GetOrSetAllStatisticsRulesAsync(() => _systemRepository.GetAllStatisticsRulesAsync()); } public async Task UpdateStatisticsRulesAsync(List rules) { // Validate all rules foreach (var rule in rules) { var validationResult = await ValidateStatisticsRuleAsync(rule); if (!validationResult.IsValid) { throw new ArgumentException($"Invalid statistics rule: {validationResult.ErrorMessage}"); } } // Save all rules var result = await _systemRepository.SaveStatisticsRulesAsync(rules); if (result) { _cacheService.InvalidateRulesCache(); } return result; } public async Task ValidateRuleAsync(BusinessRuleConfig rule) { var result = new RuleValidationResult { IsValid = true }; if (rule == null) { result.IsValid = false; result.ErrorMessage = "Rule cannot be null"; return result; } if (string.IsNullOrWhiteSpace(rule.RuleName)) { result.IsValid = false; result.ErrorMessage = "Rule name cannot be empty"; return result; } if (string.IsNullOrWhiteSpace(rule.RuleExpression)) { result.IsValid = false; result.ErrorMessage = "Rule expression cannot be empty"; return result; } // Validate rule syntax if (!IsValidRuleExpression(rule.RuleExpression)) { result.IsValid = false; result.ErrorMessage = "Invalid rule expression syntax"; return result; } // Test rule with sample data try { var sampleData = GetSampleDataForRule(rule); var testResult = await EvaluateRuleAsync(rule, sampleData); if (!testResult.Success) { result.IsValid = false; result.ErrorMessage = $"Rule evaluation failed: {testResult.ErrorMessage}"; } } catch (Exception ex) { result.IsValid = false; result.ErrorMessage = $"Rule validation error: {ex.Message}"; } return result; } public async Task EvaluateRuleAsync(BusinessRuleConfig rule, object data) { var result = new RuleEvaluationResult { Success = true }; try { // Parse and evaluate the rule expression var evaluator = new RuleExpressionEvaluator(); var evaluationResult = evaluator.Evaluate(rule.RuleExpression, data); result.Success = evaluationResult.Success; result.Result = evaluationResult.Result; result.EvaluationTime = evaluationResult.EvaluationTime; result.ErrorMessage = evaluationResult.ErrorMessage; // Log rule execution if successful if (result.Success && rule.Enabled) { await LogRuleExecutionAsync(rule, data, result); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = ex.Message; } return result; } public async Task> GetRuleExecutionHistoryAsync(int ruleId, DateTime? startDate = null, DateTime? endDate = null) { return await _systemRepository.GetRuleExecutionHistoryAsync(ruleId, startDate, endDate); } #region Private Methods private bool IsValidRuleExpression(string expression) { // Basic validation - in a real implementation, you would use a proper expression parser return !string.IsNullOrWhiteSpace(expression) && !expression.Contains("DELETE") && !expression.Contains("DROP") && !expression.Contains("TRUNCATE"); } private object GetSampleDataForRule(BusinessRuleConfig rule) { // Return sample data based on rule type return new { Production = new { Quantity = 100, Target = 120, Quality = 95 }, Device = new { Status = "Running", Efficiency = 85 }, Time = DateTime.Now }; } private async Task ValidateStatisticsRuleAsync(StatisticsRuleConfig rule) { // Implementation for validating statistics rules if (string.IsNullOrWhiteSpace(rule.RuleName)) { throw new ArgumentException("Statistics rule name cannot be empty"); } if (string.IsNullOrWhiteSpace(rule.CalculationExpression)) { throw new ArgumentException("Statistics rule calculation expression cannot be empty"); } } private async Task LogRuleExecutionAsync(BusinessRuleConfig rule, object data, RuleEvaluationResult result) { var execution = new RuleExecutionHistory { RuleId = rule.RuleId, RuleName = rule.RuleName, InputDataJson = System.Text.Json.JsonSerializer.Serialize(data), Result = result.Result?.ToString(), Success = result.Success, ErrorMessage = result.ErrorMessage, ExecutionTime = DateTime.UtcNow }; await _systemRepository.LogRuleExecutionAsync(execution); } #endregion } #region Supporting Classes public class RuleExpressionEvaluator { public RuleEvaluationResult Evaluate(string expression, object data) { var result = new RuleEvaluationResult(); var startTime = DateTime.UtcNow; try { // Parse the expression and evaluate against data // This is a simplified implementation // In a real scenario, you would use a proper expression parser or scripting engine var parser = new ExpressionParser(); var evaluationResult = parser.ParseAndEvaluate(expression, data); result.Success = evaluationResult.Success; result.Result = evaluationResult.Value; result.ErrorMessage = evaluationResult.Error; } catch (Exception ex) { result.Success = false; result.ErrorMessage = ex.Message; } result.EvaluationTime = DateTime.UtcNow - startTime; return result; } } public class ExpressionParser { public ParseResult ParseAndEvaluate(string expression, object data) { var result = new ParseResult(); try { // Simple expression evaluation // In a real implementation, you would use a proper expression parser // like NCalc, System.Linq.Dynamic.Core, or a custom parser if (expression.Contains(">")) { var parts = expression.Split('>'); if (parts.Length == 2) { var left = EvaluateExpression(parts[0].Trim(), data); var right = EvaluateExpression(parts[1].Trim(), data); if (left != null && right != null) { result.Value = Convert.ToDecimal(left) > Convert.ToDecimal(right); } } } else if (expression.Contains("<")) { var parts = expression.Split('<'); if (parts.Length == 2) { var left = EvaluateExpression(parts[0].Trim(), data); var right = EvaluateExpression(parts[1].Trim(), data); if (left != null && right != null) { result.Value = Convert.ToDecimal(left) < Convert.ToDecimal(right); } } } else if (expression.Contains("=")) { var parts = expression.Split('='); if (parts.Length == 2) { var left = EvaluateExpression(parts[0].Trim(), data); var right = EvaluateExpression(parts[1].Trim(), data); if (left != null && right != null) { result.Value = left.ToString() == right.ToString(); } } } else { // Simple value evaluation result.Value = EvaluateExpression(expression, data); } result.Success = true; } catch (Exception ex) { result.Success = false; result.Error = ex.Message; } return result; } private object EvaluateExpression(string expression, object data) { // Simple property extraction from data object // In a real implementation, this would be more sophisticated if (data is null) return null; // Handle simple property access if (expression.Contains(".")) { var parts = expression.Split('.'); if (parts.Length == 2) { var property = parts[1]; var dataDict = System.Text.Json.JsonSerializer.Deserialize>( System.Text.Json.JsonSerializer.Serialize(data)); return dataDict?.TryGetValue(property, out var value) == true ? value : null; } } // Handle simple numeric comparison if (decimal.TryParse(expression, out var numericValue)) { return numericValue; } return null; } } public class ParseResult { public bool Success { get; set; } public object Value { get; set; } public string Error { get; set; } } public class RuleEvaluationResult { public bool Success { get; set; } public object Result { get; set; } public TimeSpan EvaluationTime { get; set; } public string ErrorMessage { get; set; } } public class RuleValidationResult { public bool IsValid { get; set; } public string ErrorMessage { get; set; } public List Warnings { get; set; } = new List(); } #endregion }