文章目录

  • 实体类
  • service
  • Drools配置
  • 规则文件
  • controller
  • 改进:实现动态规划

要求:不同的货物重量有不同的金额,需要根据重量计算金额,且修改规则后动态写入到数据库中

实体类

AddressRule


/*** 封装计算订单价格所需的参数*/
public class AddressRule {/*** 货品总重量*/private double totalWeight;/*** 距离*/private double distance;/*** 续重价格*/private double continuedFee;/*** 首重*/private double firstWeight;/*** 首重价格*/private double firstFee;public double getFirstFee() {return firstFee;}public void setFirstFee(double firstFee) {this.firstFee = firstFee;}public double getFirstWeight() {return firstWeight;}public void setFirstWeight(double firstWeight) {this.firstWeight = firstWeight;}public double getTotalWeight() {return totalWeight;}public void setTotalWeight(double totalWeight) {this.totalWeight = totalWeight;}public double getDistance() {return distance;}public void setDistance(double distance) {this.distance = distance;}public double getContinuedFee() {return continuedFee;}public void setContinuedFee(double continuedFee) {this.continuedFee = continuedFee;}
}

AddressCheckResult


/*** 封装订单价格计算后的结果*/
public class AddressCheckResult {private boolean postCodeResult = false; // true:通过校验;false:未通过校验private String result;public String getResult() {return result;}public void setResult(String result) {this.result = result;}public boolean isPostCodeResult() {return postCodeResult;}public void setPostCodeResult(boolean postCodeResult) {this.postCodeResult = postCodeResult;}
}

service

public interface DroolsRulesService {/*** 根据条件计算订单价格* @param addressRule* @return*/String calcFee(AddressRule addressRule);
}

public class DroolsRulesServiceImpl implements DroolsRulesService {/*** 根据条件计算订单价格* @param addressRule* @return*/@Overridepublic String calcFee(AddressRule addressRule) {//货物总重量BigDecimal totalWeight = new BigDecimal(addressRule.getTotalWeight());//首重BigDecimal firstWeight = new BigDecimal(addressRule.getFirstWeight());//首重价格BigDecimal firstFee = new BigDecimal(addressRule.getFirstFee());//续重价格BigDecimal continuedFee = new BigDecimal((addressRule.getContinuedFee()));//超过首重部分重量=总重量-首重BigDecimal lost = totalWeight.subtract(firstWeight);//3.5//只保留整数部分lost = lost.setScale(0,BigDecimal.ROUND_DOWN);return continuedFee.multiply(lost).add(firstFee).toString();}
//这个main函数是用来测试的public static void main(String[] args) {AddressRule addressRule = new AddressRule();addressRule.setTotalWeight(10.3);addressRule.setFirstWeight(1);addressRule.setFirstFee(20);addressRule.setContinuedFee(6);String s = new DroolsRulesServiceImpl().calcFee(addressRule);System.out.println(s);}
}

Drools配置

@Configuration
public class DroolsAutoConfiguration {private static final String RULES_PATH = "rules/";@Bean@ConditionalOnMissingBean(KieFileSystem.class)public KieFileSystem kieFileSystem() throws IOException {KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();for (Resource file : getRuleFiles()) {kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));}return kieFileSystem;}private Resource[] getRuleFiles() throws IOException {ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");}@Bean@ConditionalOnMissingBean(KieContainer.class)public KieContainer kieContainer() throws IOException {final KieRepository kieRepository = getKieServices().getRepository();kieRepository.addKieModule(new KieModule() {@Overridepublic ReleaseId getReleaseId() {return kieRepository.getDefaultReleaseId();}});KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());kieBuilder.buildAll();KieContainer kieContainer=getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());return kieContainer;}private KieServices getKieServices() {return KieServices.Factory.get();}@Bean@ConditionalOnMissingBean(KieBase.class)public KieBase kieBase() throws IOException {return kieContainer().getKieBase();}@Bean@ConditionalOnMissingBean(KieSession.class)public KieSession kieSession() throws IOException {return kieContainer().newKieSession();}@Bean@ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)public KModuleBeanFactoryPostProcessor kiePostProcessor() {return new KModuleBeanFactoryPostProcessor();}
}

规则文件

在rule包下写规则文件:

package rules;import com.itheima.pinda.entity.fact.AddressRule
import com.itheima.pinda.entity.fact.AddressCheckResult
import com.itheima.pinda.service.DroolsRulesService
import com.itheima.pinda.service.impl.DroolsRulesServiceImpldialect "java"rule "1千克以内20元"activation-group "mygroup"salience 10whenaddressRule : AddressRule(totalWeight != null && totalWeight <= 1.00)checkResult : AddressCheckResult()thencheckResult.setPostCodeResult(true);checkResult.setResult("20");System.out.println("1千克以内20元");
endrule "1千克以上,订单距离在200公里以下的,首重1千克,首重价格20元,续重每1千克资费为6元"activation-group "mygroup"salience 9whenaddressRule : AddressRule(totalWeight != null && totalWeight > 1.00 && distance <= 200.00)checkResult : AddressCheckResult()thenaddressRule.setFirstFee(20.00);addressRule.setFirstWeight(1.00);addressRule.setContinuedFee(6.00);DroolsRulesService droolsRulesService = new DroolsRulesServiceImpl();String orderAmount = droolsRulesService.calcFee(addressRule);checkResult.setPostCodeResult(true);checkResult.setResult(orderAmount);System.out.println("1千克以上,订单距离在200公里以下的,首重1千克,首重价格20元,续重每1千克资费为6元");
endrule "1千克以上,订单距离在200~500公里的,首重1千克,首重价格20元,续重每1千克资费为9元"activation-group "mygroup"salience 8whenaddressRule : AddressRule(totalWeight != null && totalWeight > 1.00 && distance <= 500.00)checkResult : AddressCheckResult()thenaddressRule.setFirstFee(20.00);addressRule.setFirstWeight(1.00);addressRule.setContinuedFee(9.00);DroolsRulesService droolsRulesService = new DroolsRulesServiceImpl();String orderAmount = droolsRulesService.calcFee(addressRule);checkResult.setPostCodeResult(true);checkResult.setResult(orderAmount);System.out.println("1千克以上,订单距离在200~500公里的,首重1千克,首重价格20元,续重每1千克资费为9元");
endrule "1千克以上,订单距离在500公里以上的,首重1千克,首重价格20元,续重每1千克资费为15元"activation-group "mygroup"salience 7whenaddressRule : AddressRule(totalWeight != null && totalWeight > 1.00 && distance > 500.00)checkResult : AddressCheckResult()thenaddressRule.setFirstFee(20.00);addressRule.setFirstWeight(1.00);addressRule.setContinuedFee(15.00);DroolsRulesService droolsRulesService = new DroolsRulesServiceImpl();String orderAmount = droolsRulesService.calcFee(addressRule);checkResult.setPostCodeResult(true);checkResult.setResult(orderAmount);System.out.println("1千克以上,订单距离在500公里以上的,首重1千克,首重价格20元,续重每1千克资费为15元");
end

controller

  /*** 新增订单** @param orderDTO 订单信息* @return 订单信息*/@PostMapping("")public OrderDTO save(@RequestBody OrderDTO orderDTO, HttpServletResponse res) {log.info("保存订单信息:{}", JSON.toJSONString(orderDTO));Order order = new Order();order.setEstimatedArrivalTime(LocalDateTime.now().plus(2, ChronoUnit.DAYS));Map map = orderService.calculateAmount(orderDTO);log.info("实时计算运费:{}", map);orderDTO = (OrderDTO) map.get("orderDto");BeanUtils.copyProperties(orderDTO, order);if ("send error msg".equals(orderDTO.getSenderAddress()) || "receive error msg".equals(orderDTO.getReceiverAddress())) {return orderDTO;}order.setAmount(new BigDecimal(map.getOrDefault("amount", "23").toString()));orderService.saveOrder(order);log.info("订单信息入库:{}", order);OrderDTO result = new OrderDTO();BeanUtils.copyProperties(order, result);return result;}

orderService.calculateAmount方法:

   /*** 计算订单价格* @param orderDTO* @return*/@Overridepublic Map calculateAmount(OrderDTO orderDTO) {//计算订单距离orderDTO = this.getDistance(orderDTO);if("sender error msg".equals(orderDTO.getSenderAddress()) || "receiver error msg".equals(orderDTO.getReceiverAddress())){//地址解析失败,直接返回Map map = new HashMap();map.put("amount","0");map.put("errorMsg","无法计算订单距离和订单价格,请输入真实地址");map.put("orderDto",orderDTO);return map;}KieSession session = KieContainer.newKieSession();//ReloadDroolsRulesService.kieContainer.newKieSession();//设置Fact对象AddressRule addressRule = new AddressRule();addressRule.setTotalWeight(orderDTO.getOrderCargoDto().getTotalWeight().doubleValue());addressRule.setDistance(orderDTO.getDistance().doubleValue());//将对象加入到工作内存session.insert(addressRule);AddressCheckResult addressCheckResult = new AddressCheckResult();session.insert(addressCheckResult);int i = session.fireAllRules();System.out.println("触发了" + i + "条规则");session.destroy();if(addressCheckResult.isPostCodeResult()){System.out.println("规则匹配成功,订单价格为:" + addressCheckResult.getResult());orderDTO.setAmount(new BigDecimal(addressCheckResult.getResult()));Map map = new HashMap();map.put("orderDto",orderDTO);map.put("amount",addressCheckResult.getResult());return map;}return null;}/*** 调用百度地图服务接口,根据寄件人地址和收件人地址计算订单距离* @param orderDTO* @return*/public OrderDTO getDistance(OrderDTO orderDTO){//调用百度地图服务接口获取寄件人地址对应的坐标经纬度String begin = BaiduMapUtils.getCoordinate(orderDTO.getSenderAddress());if(begin == null){orderDTO.setSenderAddress("sender error msg");return orderDTO;}//调用百度地图服务接口获取收件人地址对应的坐标经纬度String end = BaiduMapUtils.getCoordinate(orderDTO.getReceiverAddress());if(end == null){orderDTO.setReceiverAddress("receiver error msg");return orderDTO;}Double distance = BaiduMapUtils.getDistance(begin, end);DecimalFormat decimalFormat = new DecimalFormat("#.##");String distanceStr = decimalFormat.format(distance/1000);orderDTO.setDistance(new BigDecimal(distanceStr));return orderDTO;}

改进:实现动态规划

思路:
1、将规则文件的内容存储在数据库中

2、Drools相关对象(例如KieContainer对象)的创建都基于数据库中存储的规则来创建

3、提供HTTP访问接口,当规则发生变化时调用此接口重新加载数据库中的规则,重新创建KieContainer等对象
第一步:在pd_oms数据库中创建数据表rule,用于存储规则内容

CREATE TABLE `rule`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`create_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`last_modify_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`rule_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`version` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `UK_9yepjak9olg92holwkr8p3l0f`(`rule_key`) USING BTREE,UNIQUE INDEX `UK_ilmbp99kyt6gy10224pc9bl6n`(`version`) USING BTREE,UNIQUE INDEX `UK_ei48upwykmhx9r5p7p4ndxvgn`(`last_modify_time`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

第二步:创建Rule实体类,和上面的rule表对应

package com.itheima.pinda.entity;
​
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.io.Serializable;
​
@Data
@ToString
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("rule")
@ApiModel
public class Rule implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.INPUT)private Long id;private String ruleKey;private String content;private String version;private String lastModifyTime;private String createTime;
}

第三步:创建RuleMapper接口

package com.itheima.pinda.mapper;
​
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.pinda.entity.Order;
import com.itheima.pinda.entity.Rule;
import org.apache.ibatis.annotations.Mapper;
​
/*** 规则*/
@Mapper
public interface RuleMapper extends BaseMapper<Rule> {}

第四步:创建ReloadDroolsRulesService,用于重新加载数据库中的规则

package com.itheima.pinda.service.impl;
​
import com.itheima.pinda.entity.Address;
import com.itheima.pinda.entity.Rule;
import com.itheima.pinda.mapper.RuleMapper;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.Message;
import org.kie.api.runtime.KieContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
​
/***重新加载规则*/
@Service
public class ReloadDroolsRulesService {public static KieContainer kieContainer;
​@Autowiredprivate RuleMapper ruleMapper;
​public void reload() {KieContainer kieContainer = loadContainerFromString(loadRules());this.kieContainer = kieContainer;}
​private List<Rule> loadRules() {List<Rule> rules = ruleMapper.selectList(null);return rules;}
​public KieContainer loadContainerFromString(List<Rule> rules) {long startTime = System.currentTimeMillis();KieServices ks = KieServices.Factory.get();KieRepository kr = ks.getRepository();KieFileSystem kfs = ks.newKieFileSystem();
​for (Rule rule : rules) {String drl = rule.getContent();kfs.write("src/main/resources/" + drl.hashCode() + ".drl", drl);}
​KieBuilder kb = ks.newKieBuilder(kfs);
​kb.buildAll();if (kb.getResults().hasMessages(Message.Level.ERROR)) {throw new RuntimeException("Build Errors:\n" + kb.getResults().toString());}long endTime = System.currentTimeMillis();System.out.println("Time to build rules : " + (endTime - startTime) + " ms");startTime = System.currentTimeMillis();KieContainer kContainer = ks.newKieContainer(kr.getDefaultReleaseId());endTime = System.currentTimeMillis();System.out.println("Time to load container: " + (endTime - startTime) + " ms");return kContainer;}
}

第五步:创建CommandLineRunnerImpl,在项目启动时加载数据库中最新规则

package com.itheima.pinda.service.impl;
​
import com.itheima.pinda.service.ReloadDroolsRulesService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
​
/*** 项目启动时加载最新规则**/
@Component
@Slf4j
public class CommandLineRunnerImpl implements CommandLineRunner {@Resourceprivate ReloadDroolsRulesService rules;
​@Overridepublic void run(String... args) throws Exception {rules.reload();}
}

第六步:创建RulesReloadController

package com.itheima.pinda.controller;
​
import com.itheima.pinda.service.ReloadDroolsRulesService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.io.IOException;
​
@RequestMapping("/rules")
@Controller
public class RulesReloadController {@Resourceprivate ReloadDroolsRulesService rules;
​/*** 从数据库加载最新规则** @return* @throws IOException*/@ResponseBody@RequestMapping("/reload")public String reload() throws IOException {rules.reload();return "ok";}
}

第七步:注释掉DroolsAutoConfiguration上面的configuration注解

第八步:修改OrderServiceImpl的calculateAmount方法,修改KieSession的获取方式
KieSession session = KieContainer.newKieSession();
改为
KieSession session = ReloadDroolsRulesService.kieContainer.newKieSession();

【代码示例】springboot使用drools实现动态规划相关推荐

  1. php 配置文件加密工具类,SpringBoot集成Jasypt安全框架以及配置文件内容加密(代码示例)...

    本篇文章给大家带来的内容是关于SpringBoot集成Jasypt安全框架以及配置文件内容加密(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 我们在SpringBoot项 ...

  2. db2 springboot 整合_springboot的yml配置文件通过db2的方式整合mysql代码示例

    本篇文章小编给大家分享一下springboot的yml配置文件通过db2的方式整合mysql代码示例,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. s ...

  3. 送书 | 你一定能看懂的算法基础书(代码示例基于Python)

    本文引自图灵教育<算法图解> 你一定能看懂的算法基础书:代码示例基于Python:400多个示意图,生动介绍算法执行过程:展示不同算法在性能方面的优缺点:教会你用常见算法解决每天面临的实际 ...

  4. 写了一个 SSO 单点登录的代码示例给胖友!

    发危~ " 摘要: 原创出处 http://www.iocoder.cn/Spring-Security/OAuth2-learning-sso/ 「芋道源码」欢迎转载,保留摘要,谢谢! 1 ...

  5. SpringBoot整合Drools

    文章目录 关于Drools 整合Drools 一般流程 快速整合 关于Drools 博客: 规则引擎 Drools 上面那篇博客详细的介绍了Drools,还要语法规则,关于什么是Drools,看这篇博 ...

  6. Mac,VSCode编写Python代码示例

    Mac,VSCode编写Python代码示例 之前写Python代码一直用的是Pycharm,后来发现VSCode更加直(jian)观(dan)简(cu)洁(bao) ,于是赶紧在官网下载好VSCod ...

  7. 用户自定义协议client/server代码示例

    用户自定义协议client/server代码示例 代码参考链接:https://github.com/sogou/workflow message.h message.cc server.cc cli ...

  8. 2021年大数据Flink(二十六):​​​​​​​State代码示例

    目录 State代码示例 Keyed State 官网代码示例 需求: 编码步骤 代码示例 Operator State 官网代码示例 需求: 编码步骤: 代码示例 State代码示例 Keyed S ...

  9. TensorFlow常用操作:代码示例

    1,定义矩阵代码示例: import tensorflow as tftf.zeros([3,4]) #定义3行4列元素均为0的矩阵tensor=tf.constant([1,2,3,4])#定义一维 ...

最新文章

  1. 设计模式: 自己手动实现一个观察者设计模式
  2. Java设计模式(二):观察者设计模式
  3. 《火球——UML大战需求分析》(第2章 耗尽脑汁的需求分析工作)——2.3 给客户带来价值,需求分析之正路...
  4. VC6.0: fatal error C1010: unexpected end of file while looking for precompiled head
  5. SSM项目使用GoEasy 获取客户端上下线实时状态变化及在线客户列表
  6. 【站点部署】解析二级域名并部署站点
  7. 2007-11-22 21:24 大端(Big Endian)与小端(Little Endian)详解
  8. [...]ubuntu rvm rails 安装完之后消失的解决方法
  9. libsuperuser
  10. 厂商为什么喜欢使用堆叠?
  11. Java GC种类以及触发时机
  12. amd玄冰400怎么拆图解_【装机帮扶站】第735期:“无货”当道的京东年底大预售来了(AMD篇)...
  13. 事件元素JS的event对象--知识点总结
  14. 外点惩罚函数法·约束优化问题
  15. SAP MM模块库存结存报表
  16. Mstar方案软件运行基本原理
  17. A Visual, Intuitive Guide to Imaginary Numbers
  18. 第6天:分割处理与中断处理
  19. 史上最简洁明了的,字符串拼接关于单引号和双引号的用法解析
  20. 传统安防监控摄像头Onvif云台控制直播流如何转换成GB/T28181对接到国标视频平台公安内网

热门文章

  1. 在WPS中提取出的照片在哪找_WPS技巧 | 找不到合适的配图?教你一招搞定
  2. win10浏览器_Win10系统中ie浏览器的证书错误应该如何解决?
  3. python 列表拼接_【Python杂货铺】速学python基础
  4. java redirect 跨域_碰到了跨域问题, Redirect is not allowed for a preflight request
  5. 2020 年最全 Python 面试题汇总 (四)
  6. 十一、深入Java的判断语句
  7. Vue 第九天学习
  8. 降低百倍时间步,精度媲美传统神经网络:上交等机构提出ANN-SNN转换框架
  9. 今日arXiv精选 | 35篇顶会论文:ICCV/ CIKM/ ACM MM
  10. 2019年“计算法学”夏令营即日起接收报名申请