多条件规则匹配实现思路咨询:Post字典与数据库规则校验
问题背景与现有局限
首先说明:我并非索要代码或寻求代劳,仅需此类规则的实现思路。
现有视图通过键值对向控制器提交数据,控制器将数据存入Dictionary<string,string> demoData;数据库含Products表(id、name)与Rules表(原存data_key、data_value、product_ID)。
当前实现代码:
public Dictionary<int, string> testC([FromBody]Dictionary<string,string> demoData) { var getRules = AsDynamic(App.Data["Rules"]); // contains Link_datakey, Link_datavalue, Link_product var getProducts = AsDynamic(App.Data["Products"]); // contains Product_id, Product_name Dictionary<int, string> testdict = new Dictionary<int, string>(); var product = 0; foreach(var r in getRules){ if (demoData.ContainsKey(r.Link_datakey)) { if (demoData[r.Link_datakey] == r.Link_datavalue) { product = Convert.ToInt32(r.Link_product); if (!testdict.ContainsKey(product)) { testdict.Add(product, getProducts.Where(i => i.id_source == r.Link_product).First().Product_name); } } } } return testdict; }
该实现存在两大局限:
- 仅支持“等于”规则,若需
>、<等操作符,需为每种操作编写单独判断; - 仅支持单条件校验,现需支持多条件组合校验(如
some_datakey < some_datavalue AND someother_datakey == someother_datavalue),条件数量可变(1-3个)。
我初步设想调整规则表结构为按rule_group_id分组,新增关联rule_group_id与对应产品的表,但不知如何实现多条件校验逻辑,恳请提供实现思路。
实现思路方案
首先你的方向完全没问题,按规则组分组就是实现多条件组合校验的核心,咱们一步步拆解思路:
一、数据库结构调整
先把表结构捋顺,这样后续逻辑才好落地:
- 新增
RuleGroups表:用来存规则组的核心信息,字段包括rule_group_id(主键)、combine_operator(组内条件的组合逻辑,比如AND/OR,毕竟你只需要1-3个条件,这俩足够覆盖大部分场景)、product_id(直接关联对应产品,省得额外建关联表)。 - 修改原
Rules表:新增rule_group_id(外键关联RuleGroups)、condition_operator字段(存单个条件的操作符,比如==、>、<、>=、<=、!=),保留原有的data_key、data_value。
这样结构就清晰了:一个产品对应一个规则组,一个规则组包含1-3个条件,每个条件有自己的判断逻辑,组内条件用AND/OR来组合。
二、核心逻辑实现
1. 封装通用的单条件校验方法
先把不同操作符的判断逻辑封装成一个方法,避免重复写一堆if-else,以后加新操作符直接扩展就行:
private bool IsConditionSatisfied(string inputValue, string ruleValue, string op) { // 先尝试转成数值类型,支持数字比较;转失败就按字符串处理 if (double.TryParse(inputValue, out double inputNum) && double.TryParse(ruleValue, out double ruleNum)) { return op switch { "==" => inputNum == ruleNum, ">" => inputNum > ruleNum, "<" => inputNum < ruleNum, ">=" => inputNum >= ruleNum, "<=" => inputNum <= ruleNum, "!=" => inputNum != ruleNum, _ => false }; } else { // 字符串比较,支持大小写不敏感(按需调整) return op switch { "==" => string.Equals(inputValue, ruleValue, StringComparison.OrdinalIgnoreCase), "!=" => !string.Equals(inputValue, ruleValue, StringComparison.OrdinalIgnoreCase), // 还能扩展比如"contains"、"starts_with"这类字符串操作,按需加 _ => false }; } }
2. 规则组的整体校验逻辑
接下来按规则组来处理,每个组代表一组需要一起校验的条件:
- 先从数据库取出所有规则组,并且关联每个组下的所有条件(也就是1-3条Rule记录)。
- 遍历每个规则组,先逐个校验组内的条件,收集每个条件的结果。
- 根据组的组合逻辑(
AND/OR)判断整个组是否满足:AND:必须所有条件都满足,才算组匹配。OR:只要有一个条件满足,就算组匹配。
- 最后把匹配的组对应的产品加入结果字典,注意去重。
大概的逻辑代码(贴合你现有代码的写法):
public Dictionary<int, string> testC([FromBody]Dictionary<string,string> demoData) { // 假设这里能关联查询出每个规则组及其下属的条件 var ruleGroups = AsDynamic(App.Data["RuleGroups"]).Include("Rules"); var products = AsDynamic(App.Data["Products"]); Dictionary<int, string> matchedProducts = new Dictionary<int, string>(); foreach(var group in ruleGroups) { bool isGroupMatched = false; List<bool> conditionResults = new List<bool>(); // 逐个校验组内的每个条件 foreach(var rule in group.Rules) { if(demoData.ContainsKey(rule.data_key)) { bool result = IsConditionSatisfied(demoData[rule.data_key], rule.data_value, rule.condition_operator); conditionResults.Add(result); } else { // 如果提交的数据里没有这个key,直接判定该条件不满足 conditionResults.Add(false); } } // 根据组合逻辑判断整个组是否匹配 if(group.combine_operator == "AND") { isGroupMatched = conditionResults.All(r => r); // 所有条件都为true } else if(group.combine_operator == "OR") { isGroupMatched = conditionResults.Any(r => r); // 至少一个条件为true } // 如果组匹配,添加对应的产品到结果(去重) if(isGroupMatched) { int productId = Convert.ToInt32(group.product_id); if(!matchedProducts.ContainsKey(productId)) { var targetProduct = products.FirstOrDefault(p => p.id == productId); if(targetProduct != null) { matchedProducts.Add(productId, targetProduct.name); } } } } return matchedProducts; }
三、后续扩展性建议
- 可以把操作符做成枚举类型(比如
ConditionOperator、CombineOperator),避免字符串硬编码,减少拼写错误的概率。 - 如果以后需要更复杂的条件嵌套(比如
(A AND B) OR C),可以考虑用表达式树动态构建判断逻辑,但对于1-3个条件的场景,上面的方法已经足够简单高效。 - 给数据库里的
rule_group_id、product_id字段加索引,提升查询性能,尤其是数据量变大的时候。
内容的提问来源于stack exchange,提问作者João Gomes
相关产品推荐
相关产品推荐

