在现代的出行服务中,代驾服务越来越普及。为了确保用户能够快速找到附近的代驾司机,自动化调度系统显得尤为重要。今天,我将分享一个Java实现的调度任务,旨在根据用户的位置自动搜索附近符合条件的代驾司机,并推送订单信息。
任务需求
我们需要完成以下几个任务:
- 根据任务ID从数据库中查询任务的创建情况。
- 根据订单状态判断是否继续执行任务调度。
- 远程调用服务来搜索附近满足条件的司机。
- 为满足条件的司机创建临时队列,并将新订单信息推送给他们。
代码实现
下面是完整的代码实现,逐步解析各个步骤。
// 执行任务:搜索附近代驾司机
@Override
public void executeTask(long jobId) {
// 1. 根据jobid查询数据库,当前任务是否已经创建
// 如果没有创建,不往下执行了
LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderJob::getJobId, jobId);
OrderJob orderJob = orderJobMapper.selectOne(wrapper);
if (orderJob == null) {
return;
}
// 2. 查询订单状态,如果当前订单接单状态,继续执行。如果当前订单不是接单状态,停止任务调度
String jsonString = orderJob.getParameter();
NewOrderTaskVo newOrderTaskVo = JSON.parseObject(jsonString, NewOrderTaskVo.class);
Long orderId = newOrderTaskVo.getOrderId();
Integer status = orderInfoFeignClient.getOrderStatus(orderId).getData();
if (status.intValue() != OrderStatus.WAITING_ACCEPT.getStatus().intValue()) {
// 停止任务调度
xxlJobClient.stopJob(jobId);
return;
}
// 3. 远程调用:搜索附近满足条件的司机
SearchNearByDriverForm searchNearByDriverForm = new SearchNearByDriverForm();
searchNearByDriverForm.setLongitude(newOrderTaskVo.getStartPointLongitude());
searchNearByDriverForm.setLatitude(newOrderTaskVo.getStartPointLatitude());
searchNearByDriverForm.setMileageDistance(newOrderTaskVo.getExpectDistance());
List<NearByDriverVo> nearByDriverVoList = locationFeignClient.searchNearByDriver(searchNearByDriverForm)
.getData();
// 4. 遍历集合,得到每个司机,为每个司机创建临时队列,存储新订单信息
nearByDriverVoList.forEach(driver -> {
// 使用Redis的set类型,保证不重复
String repeatKey = RedisConstant.DRIVER_ORDER_REPEAT_LIST + newOrderTaskVo.getOrderId();
// 记录司机id,防止重复推送
Boolean isMember = redisTemplate.opsForSet().isMember(repeatKey, driver.getDriverId());
if (!isMember) {
// 把订单消息推送给满足条件的多个司机
redisTemplate.opsForSet().add(repeatKey, driver.getDriverId());
// 过期时间:15分钟,超过15分钟没有接单自动取消
redisTemplate.expire(repeatKey, RedisConstant.DRIVER_ORDER_REPEAT_LIST_EXPIRES_TIME, TimeUnit.MINUTES);
NewOrderDataVo newOrderDataVo = new NewOrderDataVo();
newOrderDataVo.setOrderId(newOrderTaskVo.getOrderId());
newOrderDataVo.setStartLocation(newOrderTaskVo.getStartLocation());
newOrderDataVo.setEndLocation(newOrderTaskVo.getEndLocation());
newOrderDataVo.setExpectAmount(newOrderTaskVo.getExpectAmount());
newOrderDataVo.setExpectDistance(newOrderTaskVo.getExpectDistance());
newOrderDataVo.setExpectTime(newOrderTaskVo.getExpectTime());
newOrderDataVo.setFavourFee(newOrderTaskVo.getFavourFee());
newOrderDataVo.setDistance(driver.getDistance());
newOrderDataVo.setCreateTime(newOrderTaskVo.getCreateTime());
// 新订单保存司机的临时队列,Redis里面List集合
String key = RedisConstant.DRIVER_ORDER_TEMP_LIST+ driver.getDriverId();
redisTemplate.opsForList().leftPush(key, JSONObject.toJSONString(newOrderDataVo));
// 过期时间:1分钟
redisTemplate.expire(key, RedisConstant.DRIVER_ORDER_TEMP_LIST_EXPIRES_TIME, TimeUnit.MINUTES);
}
});
}
代码解读
1. 任务查询和校验
首先,我们根据任务ID (jobId
) 查询数据库,判断该任务是否已经创建。如果没有找到相应的任务记录,直接返回,结束任务执行。
LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderJob::getJobId, jobId);
OrderJob orderJob = orderJobMapper.selectOne(wrapper);
if (orderJob == null) {
return;
}
2. 订单状态检查
接下来,通过远程调用获取订单的当前状态。只有当订单状态为“等待接单”时,任务才会继续执行。如果订单状态不符,则停止任务调度。
Long orderId = newOrderTaskVo.getOrderId();
Integer status = orderInfoFeignClient.getOrderStatus(orderId).getData();
if (status.intValue() != OrderStatus.WAITING_ACCEPT.getStatus().intValue()) {
xxlJobClient.stopJob(jobId);
return;
}
3. 搜索附近的司机
使用locationFeignClient
远程调用服务,搜索在用户附近的符合条件的司机。我们通过用户的经纬度和预期距离来筛选司机。
SearchNearByDriverForm searchNearByDriverForm = new SearchNearByDriverForm();
searchNearByDriverForm.setLongitude(newOrderTaskVo.getStartPointLongitude());
searchNearByDriverForm.setLatitude(newOrderTaskVo.getStartPointLatitude());
searchNearByDriverForm.setMileageDistance(newOrderTaskVo.getExpectDistance());
List<NearByDriverVo> nearByDriverVoList = locationFeignClient.searchNearByDriver(searchNearByDriverForm)
.getData();
4. 推送订单信息
最后,我们遍历满足条件的司机列表,为每个司机创建一个临时队列,并将订单信息推送给他们。我们使用Redis来保证推送的订单不会重复,并为每个订单设置了过期时间。
nearByDriverVoList.forEach(driver -> {
String repeatKey = RedisConstant.DRIVER_ORDER_REPEAT_LIST + newOrderTaskVo.getOrderId();
Boolean isMember = redisTemplate.opsForSet().isMember(repeatKey, driver.getDriverId());
if (!isMember) {
redisTemplate.opsForSet().add(repeatKey, driver.getDriverId());
redisTemplate.expire(repeatKey, RedisConstant.DRIVER_ORDER_REPEAT_LIST_EXPIRES_TIME, TimeUnit.MINUTES);
NewOrderDataVo newOrderDataVo = new NewOrderDataVo();
newOrderDataVo.setOrderId(newOrderTaskVo.getOrderId());
newOrderDataVo.setStartLocation(newOrderTaskVo.getStartLocation());
newOrderDataVo.setEndLocation(newOrderTaskVo.getEndLocation());
newOrderDataVo.setExpectAmount(newOrderTaskVo.getExpectAmount());
newOrderDataVo.setExpectDistance(newOrderTaskVo.getExpectDistance());
newOrderDataVo.setExpectTime(newOrderTaskVo.getExpectTime());
newOrderDataVo.setFavourFee(newOrderTaskVo.getFavourFee());
newOrderDataVo.setDistance(driver.getDistance());
newOrderDataVo.setCreateTime(newOrderTaskVo.getCreateTime());
String key = RedisConstant.DRIVER_ORDER_TEMP_LIST+ driver.getDriverId();
redisTemplate.opsForList().leftPush(key, JSONObject.toJSONString(newOrderDataVo));
redisTemplate.expire(key, RedisConstant.DRIVER_ORDER_TEMP_LIST_EXPIRES_TIME, TimeUnit.MINUTES);
}
});
结论
这个调度任务的实现有效地解决了通过位置搜索司机并推送订单的问题。通过整合数据库查询、远程调用、Redis缓存等多种技术,我们实现了一个高效的代驾司机调度系统。希望这个例子能够对你的项目开发有所帮助!