hello~大家好啊,uu们好久不见,首先感谢大家的观看~~~,话不多说~
本期我们学习如下内容:
- 掌握腾讯位置服务API接口,封装最优路线规划
- 学习规则引擎 Drools入门
- 理解代驾费用规则,封装代驾费用接口
- 完成预估订单数据接口
乘客选择代驾起始点与终点,乘客下单前,先做代驾订单数据的预估,预估代驾里程、代驾时间、代驾金额及代驾的最佳驾驶线路。
预估订单数据,根据乘客选择的代驾起始点与终点,服务端需计算最佳驾驶线路与代驾预估金额。
- 最佳驾驶线路包含:腾讯地图线路数据、距离及时间。
- 代驾预估金额:根据预估里程和代驾时间,结合费用规则,计算代驾金额,代驾金额的计算比较复杂,涉及规则引擎,后续讲到。
预估订单数据-查找乘客当前订单
- 如果乘客有代驾订单正在进行中,这个时候乘客不能再次下单了,只有等当前订单完成之后,才可以下单
- 这个功能因为订单相关,后续再进行处理,目前为了后续测试,临时跳过去,假设当前乘客没有订单,web-driver中
@Tag(name = "订单API接口管理")
@RestController
@RequestMapping("/order")
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderController {
//TODO 后续完善,目前假设乘客当前没有订单
@Operation(summary = "查找乘客端当前订单")
@GuiguLogin
@GetMapping("/searchCustomerCurrentOrder")
public Result<CurrentOrderInfoVo> searchCustomerCurrentOrder() {
CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
currentOrderInfoVo.setIsHasCurrentOrder(false);
return Result.ok(currentOrderInfoVo);
}
}
预估订单数据-预估驾驶线路
前端选点
前端乘客选点时,跳入选点页面,页面空白,报如下错误
使用“腾讯位置服务地图选点”插件时,我们要开通“腾讯位置服务”,并且设置授权APP ID,这样才能使用正确使用选点服务。
开通腾讯位置服务
腾讯位置服务服务器端API文档https://so.csdn.net/so/search?q=API%E6%96%87%E6%A1%A3&spm=1001.2101.3001.7020:https://lbs.qq.com/service/webService/webServiceGuide/webServiceOverview
代驾订单的时候,后端Java项目要预估里程和时间、计算代驾线路,需要调用腾讯位置服务来实现。
- 第一步 访问腾讯官网 https://lbs.qq.com/
- 第二步 进行注册,手机号或者微信或者其他方式
- 第三步 使用注册账号进行登录,找到控制台
- 第四步 在应用管理 – 我的应用 ,创建应用
- 第五步 在创建应用中,添加key
第六步 找到添加key的值
开发腾讯地图服务接口
- 封装地图服务接口:代驾路线距离,线路规划,时间
- 修改Nacos配置文件,修改腾讯位置服务key
前端修改配置
在service-map中
MapController
@Tag(name = "地图API接口管理")
@RestController
@RequestMapping("/map")
@SuppressWarnings({"unchecked", "rawtypes"})
public class MapController {
@Autowired
private MapService mapService;
@Operation(summary = "计算驾驶线路")
@PostMapping("/calculateDrivingLine")
public Result<DrivingLineVo> calculateDrivingLine(@RequestBody CalculateDrivingLineForm
calculateDrivingLineForm) {
DrivingLineVo drivingLineVo = mapService.calculateDrivingLine(calculateDrivingLineForm);
return Result.ok(drivingLineVo);
}
}
MapServiceImpl
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class MapServiceImpl implements MapService {
@Autowired
private RestTemplate restTemplate;
@Value("${tencent.map.key}")
private String key;
//计算驾驶线路
@Override
public DrivingLineVo calculateDrivingLine(CalculateDrivingLineForm calculateDrivingLineForm) {
//请求腾讯提供接口,按照接口要求传递相关参数,返回需要结果
//使用HttpClient,目前Spring封装调用工具使用RestTemplate
//定义调用腾讯地址
String url = "https://apis.map.qq.com/ws/direction/v1/driving/?from={from}&to={to}&key={key}";
//封装传递参数
Map<String,String> map = new HashMap();
//开始位置
// 经纬度:比如 北纬40 东京116
map.put("from",calculateDrivingLineForm.getStartPointLatitude()+","+calculateDrivingLineForm.getStartPointLongitude());
//结束位置
map.put("to",calculateDrivingLineForm.getEndPointLatitude()+","+calculateDrivingLineForm.getEndPointLongitude());
//key
map.put("key",key);
//使用RestTemplate调用 GET
JSONObject result = restTemplate.getForObject(url, JSONObject.class, map);
//处理返回结果
//判断调用是否成功
int status = result.getIntValue("status");
if(status != 0) {//失败
throw new GuiguException(ResultCodeEnum.MAP_FAIL);
}
//获取返回路线信息
JSONObject route =
result.getJSONObject("result").getJSONArray("routes").getJSONObject(0);
//创建vo对象
DrivingLineVo drivingLineVo = new DrivingLineVo();
//预估时间
drivingLineVo.setDuration(route.getBigDecimal("duration"));
//距离 6.583 == 6.58 / 6.59
drivingLineVo.setDistance(route.getBigDecimal("distance")
.divide(new BigDecimal(1000))
.setScale(2, RoundingMode.HALF_UP));
//路线
drivingLineVo.setPolyline(route.getJSONArray("polyline"));
return drivingLineVo;
}
}
配置类config
@Configuration
public class MapConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
远程调用定义
@FeignClient(value = "service-map")
public interface MapFeignClient {
/**
* 计算驾驶线路
* @param calculateDrivingLineForm
* @return
*/
@PostMapping("/map/calculateDrivingLine")
Result<DrivingLineVo> calculateDrivingLine(@RequestBody CalculateDrivingLineForm calculateDrivingLineForm);
}
预估订单数据-预估订单金额
乘客选择了出发和结束的地点,计算预估金额是多少,根据约定规则进行金额计算。
费用计算
代驾费用=里程费 + 等候费 + 远途费
- 里程费
里程费=基础里程费+超出起步里程费
- 等待费
- 远途费
-
规则虽然是上面这样的,但是实际中,规则可能进行随时调整,比如油价上涨收取燃油附加费,比如大雪天气,费用增加等等
-
对于不经常变化的业务,我们通常是硬编码到程序中。但是经常变化的业务,我们就得把业务流程从代码中剥离出来,我们怎么从程序中剥离出去?这里就需要用到规则引擎了。
引入规则引擎Drools
在service-rule中引入依赖
<dependencies>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-mvel</artifactId>
</dependency>
</dependencies>
创建Drools配置类
@Configuration
public class DroolsConfig {
// 制定规则文件的路径
private static final String RULES_CUSTOMER_RULES_DRL = "rules/FeeRule.drl";
@Bean
public KieContainer kieContainer() {
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL));
KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);
kb.buildAll();
KieModule kieModule = kb.getKieModule();
KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());
return kieContainer;
}
}
规则文件在resource里面已经有了。
service-rules
FeeRuleController
@RestController
@RequestMapping("/rules/fee")
@SuppressWarnings({"unchecked", "rawtypes"})
public class FeeRuleController {
@Autowired
private FeeRuleService feeRuleService;
@Operation(summary = "计算订单费用")
@PostMapping("/calculateOrderFee")
public Result<FeeRuleResponseVo> calculateOrderFee(@RequestBody FeeRuleRequestForm calculateOrderFeeForm) {
FeeRuleResponseVo feeRuleResponseVo = feeRuleService.calculateOrderFee(calculateOrderFeeForm);
return Result.ok(feeRuleResponseVo);
}
}
FeeRuleServiceImpl
@Autowired
private KieContainer kieContainer;
//计算订单费用
@Override
public FeeRuleResponseVo calculateOrderFee(FeeRuleRequestForm calculateOrderFeeForm) {
//封装输入对象
FeeRuleRequest feeRuleRequest = new FeeRuleRequest();
feeRuleRequest.setDistance(calculateOrderFeeForm.getDistance());
Date startTime = calculateOrderFeeForm.getStartTime();
feeRuleRequest.setStartTime(new DateTime(startTime).toString("HH:mm:ss"));
feeRuleRequest.setWaitMinute(calculateOrderFeeForm.getWaitMinute());
//Drools使用
KieSession kieSession = kieContainer.newKieSession();
//封装返回对象
FeeRuleResponse feeRuleResponse = new FeeRuleResponse();
kieSession.setGlobal("feeRuleResponse",feeRuleResponse);
kieSession.insert(feeRuleRequest);
kieSession.fireAllRules();
kieSession.dispose();
//封装数据到FeeRuleResponseVo返回
FeeRuleResponseVo feeRuleResponseVo = new FeeRuleResponseVo();
// feeRuleResponse -- feeRuleResponseVo
BeanUtils.copyProperties(feeRuleResponse,feeRuleResponseVo);
return feeRuleResponseVo;
}
远程调用接口FeeRuleFeignClient
/**
* 计算订单费用
* @param calculateOrderFeeForm
* @return
*/
@PostMapping("/rules/fee/calculateOrderFee")
Result<FeeRuleResponseVo> calculateOrderFee(@RequestBody FeeRuleRequestForm calculateOrderFeeForm);
web-customer中
OrderController
@Autowired
private OrderService orderService;
@Operation(summary = "预估订单数据")
@GuiGuLogin
@PostMapping("/expectOrder")
public Result<ExpectOrderVo> expectOrder(@RequestBody ExpectOrderForm expectOrderForm) {
return Result.ok(orderService.expectOrder(expectOrderForm));
}
实现类
@Autowired
private MapFeignClient mapFeignClient;
@Autowired
private FeeRuleFeignClient feeRuleFeignClient;
//预估订单数据
@Override
public ExpectOrderVo expectOrder(ExpectOrderForm expectOrderForm) {
//获取驾驶线路
CalculateDrivingLineForm calculateDrivingLineForm = new CalculateDrivingLineForm();
BeanUtils.copyProperties(expectOrderForm,calculateDrivingLineForm);
Result<DrivingLineVo> drivingLineVoResult = mapFeignClient.calculateDrivingLine(calculateDrivingLineForm);
DrivingLineVo drivingLineVo = drivingLineVoResult.getData();
//获取订单费用
FeeRuleRequestForm calculateOrderFeeForm = new FeeRuleRequestForm();
calculateOrderFeeForm.setDistance(drivingLineVo.getDistance());
calculateOrderFeeForm.setStartTime(new Date());
calculateOrderFeeForm.setWaitMinute(0);
Result<FeeRuleResponseVo> feeRuleResponseVoResult = feeRuleFeignClient.calculateOrderFee(calculateOrderFeeForm);
FeeRuleResponseVo feeRuleResponseVo = feeRuleResponseVoResult.getData();
//封装ExpectOrderVo
ExpectOrderVo expectOrderVo = new ExpectOrderVo();
expectOrderVo.setDrivingLineVo(drivingLineVo);
expectOrderVo.setFeeRuleResponseVo(feeRuleResponseVo);
return expectOrderVo;
}
测试的时候在service-map中添加依赖
<!--mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
启动项目的时候显示Web server failed to start. Port 8504 was already in use.
查看端口占用后发现8504端口没有被占用,暂时先去改配置文件中的端口号,可以正常启动。
提示此key每日调用量以达到上线,需要去腾讯位置服务官网,给周边推荐(explore)和关键词输入提示两个接口分配额度。
规则引擎
对于不经常变化的业务,我们通常是硬编码到程序中。但是经常变化的业务,我们就得把业务流程从代码中剥离出来,我们怎么从程序中剥离出去?这里就需要用到规则引擎了。
规则引擎可以做到把算法剥离出程序,你可以保存到TXT文件或者数据库表里面,用的时候再加载回程序。虽然加载回来的算法是字符串,但是规则引擎有办法运行这些字符串。例如遇到雨雪天气,代驾费用就得上调一些。如果是业务淡季,代驾费用可以下调一点。既然代驾费的算法经常要变动,我们肯定不能把算法写死到程序里面。我们要把算法从程序中抽离,保存到MySQL里面。将来我们要改动计费算法,直接添加一个新纪录就行了,原有记录不需要删改,程序默认使用最新的计费方式。
什么是规则引擎
规则引擎,全称为业务规则管理系统,英文名为BRMS(即Business Rule Management System)。规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。
需要注意的是规则引擎并不是一个具体的技术框架,而是指的一类系统,即业务规则管理系统。目前市面上具体的规则引擎产品有:drools、VisualRules、iLog等。
规则引擎实现了将业务决策从应用程序代码中分离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策。规则引擎其实就是一个输入输出平台。
系统中引入规则引擎后,业务规则不再以程序代码的形式驻留在系统中,取而代之的是处理规则的规则引擎,业务规则存储在规则库中,完全独立于程序。业务人员可以像管理数据一样对业务规则进行管理,比如查询、添加、更新、统计、提交业务规则等。业务规则被加载到规则引擎中供应用系统调用。
规则引擎的优势
使用规则引擎的优势如下:
- 1、业务规则与系统代码分离,实现业务规则的集中管理
- 2、在不重启服务的情况下可随时对业务规则进行扩展和维护
- 3、可以动态修改业务规则,从而快速响应需求变更
- 4、规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则
- 5、减少了硬编码业务规则的成本和风险
- 6、使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单
- 规则引擎应用场景
- 对于一些存在比较复杂的业务规则并且业务规则会频繁变动的系统比较适合使用规则引擎,如下:
- 1、风险控制系统----风险贷款、风险评估
- 2、反欺诈项目----银行贷款、征信验证
- 3、决策平台系统----财务计算
- 4、促销平台系统----满减、打折、加价购
Drools介绍
drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。
drools官网地址:https://drools.org/
drools源码下载地址:https://github.com/kiegroup/drools
嗯0.....0先到这里!谢谢大家的观看