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.
438 lines
15 KiB
C#
438 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Haoliang.Models.Models.System;
|
|
|
|
namespace Haoliang.Core.Services
|
|
{
|
|
public interface IRulesService
|
|
{
|
|
/// <summary>
|
|
/// Get all business rules
|
|
/// </summary>
|
|
Task<List<BusinessRuleConfig>> GetAllRulesAsync();
|
|
|
|
/// <summary>
|
|
/// Create or update business rule
|
|
/// </summary>
|
|
Task<BusinessRuleConfig> CreateOrUpdateRuleAsync(BusinessRuleConfig rule);
|
|
|
|
/// <summary>
|
|
/// Delete business rule
|
|
/// </summary>
|
|
Task<bool> DeleteRuleAsync(int ruleId);
|
|
|
|
/// <summary>
|
|
/// Get statistics rules
|
|
/// </summary>
|
|
Task<List<StatisticsRuleConfig>> GetStatisticsRulesAsync();
|
|
|
|
/// <summary>
|
|
/// Update statistics rules
|
|
/// </summary>
|
|
Task<bool> UpdateStatisticsRulesAsync(List<StatisticsRuleConfig> rules);
|
|
|
|
/// <summary>
|
|
/// Validate business rule expression
|
|
/// </summary>
|
|
Task<RuleValidationResult> ValidateRuleAsync(BusinessRuleConfig rule);
|
|
|
|
/// <summary>
|
|
/// Evaluate business rule against data
|
|
/// </summary>
|
|
Task<RuleEvaluationResult> EvaluateRuleAsync(BusinessRuleConfig rule, object data);
|
|
|
|
/// <summary>
|
|
/// Get rule execution history
|
|
/// </summary>
|
|
Task<List<RuleExecutionHistory>> 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<List<BusinessRuleConfig>> GetAllRulesAsync()
|
|
{
|
|
return await _cacheService.GetOrSetAllRulesAsync(() =>
|
|
_systemRepository.GetAllBusinessRulesAsync());
|
|
}
|
|
|
|
public async Task<BusinessRuleConfig> 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<bool> DeleteRuleAsync(int ruleId)
|
|
{
|
|
var result = await _systemRepository.DeleteBusinessRuleAsync(ruleId);
|
|
|
|
if (result)
|
|
{
|
|
_cacheService.InvalidateRulesCache();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<List<StatisticsRuleConfig>> GetStatisticsRulesAsync()
|
|
{
|
|
return await _cacheService.GetOrSetAllStatisticsRulesAsync(() =>
|
|
_systemRepository.GetAllStatisticsRulesAsync());
|
|
}
|
|
|
|
public async Task<bool> UpdateStatisticsRulesAsync(List<StatisticsRuleConfig> 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<RuleValidationResult> 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<RuleEvaluationResult> 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<List<RuleExecutionHistory>> 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<Dictionary<string, object>>(
|
|
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<string> Warnings { get; set; } = new List<string>();
|
|
}
|
|
|
|
#endregion
|
|
} |