问题描述
我遵循了 Drools 的教程,并且也实现了相同的内容。我试图了解我可以通过前端更改 .drl 文件中的值的方法。下面是我使用的名为 order.drl 的 Drools 文件。
package KieRule;
import com.example.demo.Order;
rule "HDFC"
when
orderObject : Order(cardType=="HDFC" && price>10000);
then
orderObject.setdiscount(10);
end;
rule "ICICI"
when
orderObject : Order(cardType=="ICICI" && price>15000);
then
orderObject.setdiscount(8);
end;
rule "DBS"
when
orderObject : Order(cardType=="DBS" && price>15000);
then
orderObject.setdiscount(15);
end;
基本上,其逻辑是针对所购商品的每种卡类型的折扣百分比建立规则。
因此向 http://localhost:8080/orders 发出如下的 post 请求
{
"name":"Mobile","cardType":"HDFC","price" : 11000
}
在确定折扣的情况下给出如下输出
{
"name": "Mobile","cardType": "HDFC","discount": 10,"price": 11000
}
我创建了 Spring Starter 项目,下面是我的文件夹结构
将当前硬编码在 .drl 文件中的值对我来说是否可行,例如从 JSP 或 Reactjs 前端捕获并更新“HDFC”和“price>10000”?我希望应用程序的管理员用户在需要时更改规则。我见过在 .drl 中使用 $ 符号但无法完全掌握它们的示例。我们可以在 Java 中实现这一点吗?
谢谢 SM
解决方法
假设您想要做的是保持基本结构不变并简单地改变约束,那么两个最简单的解决方案是使用规则模板或将约束本身传递给您的输入的规则。
传入约束
这是最简单的解决方案。基本上的想法是,您的约束的值是一等公民,以及您的规则输入。
查看您提供的规则,构成您的约束的两个数据是卡片类型和最低价格。这些都有一个结果。我们可以简单地建模:
class OrderConstraints {
private String cardType;
private Integer minimumPrice;
private Integer discount;
// Getters
}
您可以将这些传递到对象中的规则中,如下所示:
{
"cardType": "HDFC","minimumPrice": 10000,"discount": 10
},{
"cardType": "ICICI","minimumPrice": 15000,"discount": 8
},{
"cardType": "DBS","discount": 15
}
现在您的所有用例都可以在一个规则中处理:
rule "Apply order discount"
when
OrderConstraints( $cardType: cardType,$minimumPrice: minimumPrice,$discount: discount)
$order: Order( cardType == $cardType,price > $minimumPrice )
then
$order.setDiscount($discount);
end
(旁注:我清理了你的语法。你的原始规则中有很多不必要的分号和奇怪的空格。)
工作流程基本上如下:
- 用户在 UI/前端创建约束,指定所需信息(卡类型、最低价格、折扣)。
- 用户的约束被发送到服务器并保存到您的持久层(数据库等)
- 进行新查询时,约束会从持久层读出,并与规则输入一起传递到规则中。
规则模板
第二种解决方案是使用rule templates(链接到Drools文档。)基本思想是你提供一个数据表和一个DRL的模板,Drools框架会将数据制作成模板并为您生成 DRL。当您有非常重复的规则时(例如您的规则),这很有用 - 在其他情况下,您基本上应用具有各种不同约束的相同规则。
与其他场景类似,您的工作流程如下:
- 用户在 UI/前端创建约束,指定所需信息(卡类型、最低价格、折扣)。
- 用户的约束被发送到服务器。
- 服务器将请求重新格式化为表格形式(而不是 JSON 或任何原始格式)。
- 服务器使用数据表(第 3 步)和模板来生成规则。
您的模板可能看起来像这样,假设列标有“cardType”、“minPrice”和“discount”):
template header
cardType
minPrice
discount
package com.example.template;
import com.example.Order;
template "orderdiscounts"
rule "Apply order discount for @{cardType}"
when
$order: Order( cardType == "@{cardType}",price > @{minPrice} )
then
$order.setDiscount(@{discount});
end
end template
格式非常简单。首先是标题,我们在其中按顺序定义列。第一个空行表示标题的结尾。接下来是包声明和导入,因为它们对于文件来说是静态的。然后是模板。列值使用 @{ column name }
模式进行插值;请注意,您需要将其括在字符串的引号中。
Drools 文档非常好,所以我不会过多介绍细节,但您应该能够了解其要点。
设计注意事项
既然您在谈论 React 前端,我将假设您正在构建一个现代 Web 应用程序。在实施解决方案时,请牢记持久性和数据完整性问题。
如果您将后端应用程序扩展到负载均衡的多个实例,您需要确保用户应用的约束传播到所有实例。此外,如果您应用更改,它们需要实时可见/传播——您不能让一个节点或集群使用陈旧的约束或值。
虽然从表面上看,规则模板似乎是解决此问题的完美内置解决方案,但事实并非如此。规则模板是围绕存储在平面文件中的数据表设计的——根据定义,这不是一种非常现代或分布式的方法。如果您的用户更新了节点 A 上的约束,并且更新了节点 A 上的数据表,则需要确保相同的数据表 file 传播到所有其他节点。此外,如果您启动一个新节点,您将需要设计一种机制来获取“当前”数据表。然后,如果发生灾难性的事情并且您丢失所有节点怎么办?
“将约束传递到内存中”解决方案是老派,但它的好处是由传统的持久层支持。如果您使用某种分布式数据源,这可能是您的事实来源。只要您的所有应用程序实例在提交规则时(或使用某些逻辑缓存层)从数据库中查询您的约束,您就无需担心您的实例不同步。如果您丢失了所有节点,那么您的数据源将受到限制,以便您的新应用程序实例启动并需要使用它。当然,缺点是您确实需要执行这些数据库查询或缓存读取,这可能会增加延迟。然后您需要将这些数据传递到规则中,这会增加您的内存使用量和潜在的 CPU(具体多少取决于您传入的数据量)。
我已将您链接到 Drools documentation,我强烈建议您阅读它。它很长,但非常值得。这可能是我访问量最大的书签,尽管我什至不再每天流口水了。还有其他可用的解决方案,如那里记录的那样——例如,您的用户请求可以生成规则,将它们打包在 kjar 中,然后将它们发布到 Maven 存储库,然后您的所有实例都可以下载和使用——但是 适合您的用例解决方案是您需要根据自己的要求自行决定的。