乐尚代驾五司机接单及司机抢单

司机接单

需求

  • 乘客下单之后,新订单信息已经在司机临时队列
  • 下面司机可以开始进行接单了

首先,司机登录,认证(身份证、驾驶证、创建人脸模型)

第二,司机进行人脸识别(每天司机接单之前都需要进行人脸识别)

第三,司机开始接单了,更新司机接单状态

第四,当司机开始接单之后,删除司机之前存储到Redis里面位置信息

第五,当司机开始接单之后,清空司机临时队列新订单信息

在这里插入图片描述

查找司机端当前订单

后续完善,目前默认司机当前没有正在执行订单

@Operation(summary = "查找司机端当前订单")
@GuiguLogin
@GetMapping("/searchDriverCurrentOrder")
public Result<CurrentOrderInfoVo> searchDriverCurrentOrder() {
    CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
    currentOrderInfoVo.setIsHasCurrentOrder(false);
    return Result.ok(currentOrderInfoVo);
}

判断司机在当日是否人脸识别

在这里插入图片描述

@Operation(summary = "判断司机当日是否进行过人脸识别")
@GetMapping("/isFaceRecognition/{driverId}")
Result<Boolean> isFaceRecognition(@PathVariable("driverId") Long driverId) {
    return Result.ok(driverInfoService.isFaceRecognition(driverId));
}





判断司机当日是否进行过人脸识别
@Override
public Boolean isFaceRecognition(Long driverId) {
    //根据司机id + 当日日期进行查询
    LambdaQueryWrapper<DriverFaceRecognition> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(DriverFaceRecognition::getDriverId,driverId);
    // 年-月-日 格式
    wrapper.eq(DriverFaceRecognition::getFaceDate,new DateTime().toString("yyyy-MM-dd"));
    //调用mapper方法
    Long count = driverFaceRecognitionMapper.selectCount(wrapper);
    
    return count != 0;
}




/**
 * 判断司机当日是否进行过人脸识别
 * @param driverId
 * @return
 */
@GetMapping("/driver/info/isFaceRecognition/{driverId}")
Result<Boolean> isFaceRecognition(@PathVariable("driverId") Long driverId);






@Operation(summary = "判断司机当日是否进行过人脸识别")
@GuiguLogin
@GetMapping("/isFaceRecognition")
Result<Boolean> isFaceRecognition() {
    Long driverId = AuthContextHolder.getUserId();
    return Result.ok(driverService.isFaceRecognition(driverId));
}





@Override
public Boolean isFaceRecognition(Long driverId) {
    return driverInfoFeignClient.isFaceRecognition(driverId).getData();
}

人脸识别接口

  • 进行人脸识别,基于腾讯云实现

  • 之前创建人脸识别模型,基于之前创建人脸模型完成当前识别功能

  • 找到腾讯云文档1:照片比对
    https://console.cloud.tencent.com/api/explorer?Product=iai&Version=2020-03-03&Action=VerifyFace

  • 找到腾讯云文档2:人脸静态活体检测

https://console.cloud.tencent.com/api/explorer?Product=iai&Version=2020-03-03&Action=DetectLiveFace

@Operation(summary = "验证司机人脸")
@PostMapping("/verifyDriverFace")
public Result<Boolean> verifyDriverFace(@RequestBody DriverFaceModelForm driverFaceModelForm) {
    return Result.ok(driverInfoService.verifyDriverFace(driverFaceModelForm));
}






//人脸识别
@Override
public Boolean verifyDriverFace(DriverFaceModelForm driverFaceModelForm) {
    //1 照片比对
    try{
        // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
        // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
        // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
        Credential cred = new Credential(tencentCloudProperties.getSecretId(),
                tencentCloudProperties.getSecretKey());
        // 实例化一个http选项,可选的,没有特殊需求可以跳过
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint("iai.tencentcloudapi.com");
        // 实例化一个client选项,可选的,没有特殊需求可以跳过
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);

        // 实例化要请求产品的client对象,clientProfile是可选的
        IaiClient client = new IaiClient(cred,
                                     tencentCloudProperties.getRegion(),
                                     clientProfile);
        // 实例化一个请求对象,每个接口都会对应一个request对象
        VerifyFaceRequest req = new VerifyFaceRequest();
        //设置相关参数
        req.setImage(driverFaceModelForm.getImageBase64());
        req.setPersonId(String.valueOf(driverFaceModelForm.getDriverId()));

        // 返回的resp是一个VerifyFaceResponse的实例,与请求对象对应
        VerifyFaceResponse resp = client.VerifyFace(req);
        // 输出json格式的字符串回包
        System.out.println(AbstractModel.toJsonString(resp));
        if(resp.getIsMatch()) { //照片比对成功
            //2 如果照片比对成功,静态活体检测
            Boolean isSuccess = this.
                    detectLiveFace(driverFaceModelForm.getImageBase64());
            if(isSuccess) {//3 如果静态活体检测通过,添加数据到认证表里面
                DriverFaceRecognition driverFaceRecognition = new DriverFaceRecognition();
                driverFaceRecognition.setDriverId(driverFaceModelForm.getDriverId());
                driverFaceRecognition.setFaceDate(new Date());
                driverFaceRecognitionMapper.insert(driverFaceRecognition);
                return true;
            }
        }
    } catch (TencentCloudSDKException e) {
        System.out.println(e.toString());
    }

    throw new GuiguException(ResultCodeEnum.DATA_ERROR);
}

//人脸静态活体检测
private Boolean detectLiveFace(String imageBase64) {
    try{
        // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
        // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
        // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
        Credential cred = new Credential(tencentCloudProperties.getSecretId(), 
                tencentCloudProperties.getSecretKey());
        // 实例化一个http选项,可选的,没有特殊需求可以跳过
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint("iai.tencentcloudapi.com");
        // 实例化一个client选项,可选的,没有特殊需求可以跳过
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        // 实例化要请求产品的client对象,clientProfile是可选的
        IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion(),
                clientProfile);
        // 实例化一个请求对象,每个接口都会对应一个request对象
        DetectLiveFaceRequest req = new DetectLiveFaceRequest();
        req.setImage(imageBase64);
        // 返回的resp是一个DetectLiveFaceResponse的实例,与请求对象对应
        DetectLiveFaceResponse resp = client.DetectLiveFace(req);
        // 输出json格式的字符串回包
        System.out.println(DetectLiveFaceResponse.toJsonString(resp));
        if(resp.getIsLiveness()) {
            return true;
        }
    } catch (TencentCloudSDKException e) {
        System.out.println(e.toString());
    }
    return false;
}
  • 静态活体检测是一种技术,‌用于判别人脸是否为真实活体,‌而不是通过照片、‌视频或其他非真实的方式呈现的。‌

更新司机接单状态

@Operation(summary = "更新接单状态")
@GetMapping("/updateServiceStatus/{driverId}/{status}")
public Result<Boolean> updateServiceStatus(@PathVariable Long driverId, @PathVariable Integer status) {
   return Result.ok(driverInfoService.updateServiceStatus(driverId, status));
}



Boolean updateServiceStatus(Long driverId, Integer status);




@Transactional
@Override
public Boolean updateServiceStatus(Long driverId, Integer status) {
    LambdaQueryWrapper<DriverSet> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(DriverSet::getDriverId, driverId);
    DriverSet driverSet = new DriverSet();
    driverSet.setServiceStatus(status);
    driverSetMapper.update(driverSet, queryWrapper);
    return true;
}





/**
 * 更新接单状态
 * @param driverId
 * @param status
 * @return
 */
@GetMapping("/driver/info/updateServiceStatus/{driverId}/{status}")
Result<Boolean> updateServiceStatus(@PathVariable("driverId") Long driverId, @PathVariable("status") Integer status);

开启和停止接单的web接口

司机要开启接单后,上传位置信息到redis的geo,这样才能被任务调度搜索到司机信息,才能抢单。

@Operation(summary = "开始接单服务")
@GuiguLogin
@GetMapping("/startService")
public Result<Boolean> startService() {
   Long driverId = AuthContextHolder.getUserId();
   return Result.ok(driverService.startService(driverId));
}




Boolean startService(Long driverId);




@Autowired
private LocationFeignClient locationFeignClient;

@Autowired
private NewOrderFeignClient newOrderDispatchFeignClient;

@Override
public Boolean startService(Long driverId) {
    //判断认证状态
    DriverLoginVo driverLoginVo = driverInfoFeignClient.getDriverLoginInfo(driverId).getData();
    if(driverLoginVo.getAuthStatus().intValue() != 2) {
        throw new GuiguException(ResultCodeEnum.AUTH_ERROR);
    }

    //判断当日是否人脸识别
    Boolean isFaceRecognition = driverInfoFeignClient.isFaceRecognition(driverId).getData();
    if(!isFaceRecognition) {
        throw new GuiguException(ResultCodeEnum.FACE_ERROR);
    }

    //更新司机接单状态
    driverInfoFeignClient.updateServiceStatus(driverId, 1);

    //删除司机位置信息
    locationFeignClient.removeDriverLocation(driverId);

    //清空司机新订单队列
    newOrderDispatchFeignClient.clearNewOrderQueueData(driverId);
    return true;
}

司机抢成功单,就要关闭接单服务。

@Operation(summary = "停止接单服务")
@GuiguLogin
@GetMapping("/stopService")
public Result<Boolean> stopService() {
    Long driverId = AuthContextHolder.getUserId();
    return Result.ok(driverService.stopService(driverId));
}





 Boolean stopService(Long driverId);






@Override
public Boolean stopService(Long driverId) {
    //更新司机接单状态
    driverInfoFeignClient.updateServiceStatus(driverId, 0);

    //删除司机位置信息
    locationFeignClient.removeDriverLocation(driverId);

    //清空司机新订单队列
    newOrderDispatchFeignClient.clearNewOrderQueueData(driverId);
    return true;
}

在这里插入图片描述

司机抢单基本功能

当前司机已经开启接单服务了,实时轮流司机服务器端临时队列,只要有合适的新订单产生,那么就会轮回获取新订单数据,进行语音播放,如果司机对这个订单感兴趣就可以抢单,大家注意,同一个新订单会放入满足条件的所有司机的临时队列,谁先抢到就是谁的。

@Operation(summary = "司机抢单")
@GetMapping("/robNewOrder/{driverId}/{orderId}")
public Result<Boolean> robNewOrder(@PathVariable Long driverId, @PathVariable Long orderId) {
    return Result.ok(orderInfoService.robNewOrder(driverId, orderId));
}





// 在OrderInfoServiceImpl之前保存订单 方法修改
//乘客下单
@Override
public Long saveOrderInfo(OrderInfoForm orderInfoForm) {
    //order_info添加订单数据
    OrderInfo orderInfo = new OrderInfo();
    BeanUtils.copyProperties(orderInfoForm,orderInfo);
    //订单号
    String orderNo = UUID.randomUUID().toString().replaceAll("-","");
    orderInfo.setOrderNo(orderNo);
    //订单状态
    orderInfo.setStatus(OrderStatus.WAITING_ACCEPT.getStatus());
    orderInfoMapper.insert(orderInfo);

    //记录日志
    this.log(orderInfo.getId(),orderInfo.getStatus());

    //向redis添加标识
    //接单标识,标识不存在了说明不在等待接单状态了
    // todo 司机抢单在redis中添加标识的时候是不是也需要把orderId加上啊,要不然怎么知道是哪个订单是否存在?不可能所有订单共用一个标识吧?
    redisTemplate.opsForValue().set(RedisConstant.ORDER_ACCEPT_MARK, 
            "0", RedisConstant.ORDER_ACCEPT_MARK_EXPIRES_TIME, TimeUnit.MINUTES);

    return orderInfo.getId();
}








    //司机抢单
    @Override
    public Boolean robNewOrder(Long driverId, Long orderId) {
        //判断订单是否存在,通过Redis,减少数据库压力
        if(!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)) {
            //抢单失败
            throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
        }

        //司机抢单
        //修改order_info表订单状态值2:已经接单 + 司机id + 司机接单时间
        //修改条件:根据订单id
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getId,orderId);
        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        //设置
        orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus());
        orderInfo.setDriverId(driverId);
        orderInfo.setAcceptTime(new Date());
        //调用方法修改
        int rows = orderInfoMapper.updateById(orderInfo);
        if(rows != 1) {
            //抢单失败
            throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
        }

        //删除抢单标识
        redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK);
        return true;
    }








/**
 * 司机抢单
 * @param driverId
 * @param orderId
 * @return
 */
@GetMapping("/order/info/robNewOrder/{driverId}/{orderId}")
Result<Boolean> robNewOrder(@PathVariable("driverId") Long driverId, @PathVariable("orderId") Long orderId);








@Operation(summary = "司机抢单")
@GuiguLogin
@GetMapping("/robNewOrder/{orderId}")
public Result<Boolean> robNewOrder(@PathVariable Long orderId) {
    Long driverId = AuthContextHolder.getUserId();
    return Result.ok(orderService.robNewOrder(driverId, orderId));
}






@Override
public Boolean robNewOrder(Long driverId, Long orderId) {
    return orderInfoFeignClient.robNewOrder(driverId,orderId).getData();
}






/**
 * 司机抢单
 * @param driverId
 * @param orderId
 * @return
 */
@GetMapping("/order/info/robNewOrder/{driverId}/{orderId}")
Result<Boolean> robNewOrder(@PathVariable("driverId") Long driverId, @PathVariable("orderId") Long orderId);








@Operation(summary = "司机抢单")
@GuiguLogin
@GetMapping("/robNewOrder/{orderId}")
public Result<Boolean> robNewOrder(@PathVariable Long orderId) {
    Long driverId = AuthContextHolder.getUserId();
    return Result.ok(orderService.robNewOrder(driverId, orderId));
}








@Override
public Boolean robNewOrder(Long driverId, Long orderId) {
    return orderInfoFeignClient.robNewOrder(driverId,orderId).getData();
}

司机抢单优化方案

  • 刚才抢单接口里面,并没有考虑并发的问题,所以,产生问题,类似于电商里面超卖问题

  • 超卖问题

  • 解决方案

第一种 设置数据库事务的隔离级别,设置为Serializable,效率低下

第二种 使用乐观锁解决,通过版本号进行控制

在这里插入图片描述

  • 两个事务同时开启,第一个事务先提交之后,修改了版本,第二个事务在判断的时候版本号不一致,就没法修改数据。

  • 两个事务都可以读到数据,但是最终只有一个事务可以写成功。

第三种 加锁解决,学习过synchronized 及lock锁,本地锁,目前微服务架构,分布式部署方式。

我们使用分布式锁方式解决相关问题

1、基于 Redis 做分布式锁

基于 REDIS 的 SETNX()、EXPIRE() 方法做分布式锁

(1)、setnx(lockkey, 1) 如果返回 0,则说明占位失败;如果返回 1,则说明占位成功

(2)、expire() 命令对 lockkey 设置超时时间,为的是避免死锁问题。

(3)、执行完业务代码后,可以通过 delete 命令删除 key

2、基于 REDISSON 做分布式锁

redisson 是 redis 官方的分布式锁组件。

基于乐观锁解决司机抢单

# 基础sql语句,实现抢单
update order_info set status =2 ,driver_id = ?,accept_time = ? where id=?


# 如果使用上面语句,产生问题:只要订单接单标识没有删除,
# 如果有很多线程请求过来,都会去更新sql语句,造成,最后提交的把之前提交数据覆盖
# 使用乐观锁解决,添加版本号
# 版本号
update order_info set status =2 ,driver_id = ?,accept_time = ? where id=? and status = 1
# 在where后面添加条件,status=1,相当于添加版本号
//司机抢单:乐观锁方案解决并发问题
public Boolean robNewOrder1(Long driverId, Long orderId) {
    //判断订单是否存在,通过Redis,减少数据库压力
    if(!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)) {
        //抢单失败
        throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
    }

    //司机抢单
    //update order_info set status =2 ,driver_id = ?,accept_time = ?
    // where id=? and status = 1
    LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(OrderInfo::getId,orderId);
    wrapper.eq(OrderInfo::getStatus,OrderStatus.WAITING_ACCEPT.getStatus());

    //修改值
    OrderInfo orderInfo = new OrderInfo();
    orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus());
    orderInfo.setDriverId(driverId);
    orderInfo.setAcceptTime(new Date());

    //调用方法修改
    int rows = orderInfoMapper.update(orderInfo,wrapper);
    if(rows != 1) {
        //抢单失败
        throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
    }

    //删除抢单标识
    redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK);
    return true;
}

本地锁的局限性

  • 之前学习过锁机制,synchronized 及lock锁,都是本地锁,只在当前jvm生效
  • 举例演示
@Tag(name = "测试接口")
@RestController
@RequestMapping("/order/test")
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("testLock")
    public Result testLock() {
        testService.testLock();
        return Result.ok();
    }
}







@Service
public class TestServiceImpl implements TestService{
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Override
    public synchronized void testLock() {
        //从redis里面获取数据
        String value = redisTemplate.opsForValue().get("num");

        if(StringUtils.isBlank(value)) {
            return;
        }
        
        //把从redis获取数据+1
        int num = Integer.parseInt(value);
        
        //数据+1之后放回到redis里面
        redisTemplate.opsForValue().set("num",String.valueOf(++num));
    }
}

在redis添加初始值,num=0

  • 测试,模拟100请求并发过程

使用测试工具jmeter实现功能测试

  • 上面测试方式,在一个服务器里面进行的测试,单机版服务。比如部署集群效果,锁还会生效?

使用idea部署集群效果

第一步 修改nacos配置中心端口配置,端口部分注释掉

在这里插入图片描述

第二步 项目配置文件中添加端口号 bootstrap.properties

在这里插入图片描述
第三步 操作idea添加多个端口号
在这里插入图片描述

第四步 启动集群服务,还需要启动网关服务

  • 网关模块配置文件添加路由规则

在这里插入图片描述

  • 修改测试工具端口号,是网关端口号
    在这里插入图片描述

实现分布式锁-Redisson

redis实现分布式锁,我感觉谷粒商城的更清晰一些,谷粒商城十三缓存与分布式锁

概述

  • Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。
  • Redisson的宗旨是:促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

在common里面service-util引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
</dependency>






@Data
@Configuration
@ConfigurationProperties(prefix = "spring.data.redis")
public class RedissonConfig {

    private String host;

    private String password;

    private String port;

    private int timeout = 3000;
    private static String ADDRESS_PREFIX = "redis://";

    /**
     * 自动装配
     *
     */
    @Bean
    RedissonClient redissonSingle() {
        Config config = new Config();

        if(!StringUtils.hasText(host)){
            throw new RuntimeException("host is  empty");
        }
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(ADDRESS_PREFIX + this.host + ":" + port)
                .setTimeout(this.timeout);
        if(StringUtils.hasText(this.password)) {
            serverConfig.setPassword(this.password);
        }
        return Redisson.create(config);
    }
}










@Autowired
private RedissonClient redissonClient;

//Redisson实现
@Override
public void testLock()  {
    
    //1 通过redisson创建锁对象
    RLock lock = redissonClient.getLock("lock1");

    //2 尝试获取锁
    //(1) 阻塞一直等待直到获取到,获取锁之后,默认过期时间30s
    lock.lock();
    
    //(2) 获取到锁,锁过期时间10s
   // lock.lock(10,TimeUnit.SECONDS);
    
    //(3) 第一个参数获取锁等待时间
    //    第二个参数获取到锁,锁过期时间
    //        try {
    //            // true
    //            boolean tryLock = lock.tryLock(30, 10, TimeUnit.SECONDS);
    //        } catch (InterruptedException e) {
    //            throw new RuntimeException(e);
    //        }

    //3 编写业务代码
    //1.先从redis中通过key num获取值  key提前手动设置 num 初始值:0
    String value = redisTemplate.opsForValue().get("num");
    //2.如果值为空则非法直接返回即可
    if (StringUtils.isBlank(value)) {
        return;
    }
    //3.对num值进行自增加一
    int num = Integer.parseInt(value);
    redisTemplate.opsForValue().set("num", String.valueOf(++num));
        
    //4 释放锁
    lock.unlock();
}

基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口。

Redisson内部提供了一个监控锁的看门狗。

看门狗原理:

只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程一还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完的问题(例如业务需要40s,锁只有30s)。

1、如果我们指定了锁的超时时间,就发送给Redis执行脚本,进行占锁,默认超时就是我们制定的时间,不会自动续期;
2、如果我们未指定锁的超时时间,就使用 lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】

添加Redisson分布式锁到司机抢单

//Redisson分布式锁
//司机抢单
@Override
public Boolean robNewOrder(Long driverId, Long orderId) {
    //判断订单是否存在,通过Redis,减少数据库压力
    if(!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)) {
        //抢单失败
        throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
    }

    //创建锁
    RLock lock = redissonClient.getLock(RedisConstant.ROB_NEW_ORDER_LOCK + orderId);

    try {
        //获取锁
        boolean flag = lock.tryLock(RedisConstant.ROB_NEW_ORDER_LOCK_WAIT_TIME,RedisConstant.ROB_NEW_ORDER_LOCK_LEASE_TIME, TimeUnit.SECONDS);
        if(flag) {
            if(!redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK)) {
                //抢单失败
                throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
            }
            //司机抢单
            //修改order_info表订单状态值2:已经接单 + 司机id + 司机接单时间
            //修改条件:根据订单id
            LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(OrderInfo::getId,orderId);
            OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
            //设置
            orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus());
            orderInfo.setDriverId(driverId);
            orderInfo.setAcceptTime(new Date());
            //调用方法修改
            int rows = orderInfoMapper.updateById(orderInfo);
            if(rows != 1) {
                //抢单失败
                throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
            }

            //删除抢单标识
            redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK);
        }
    }catch (Exception e) {
        //抢单失败
        throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
    }finally {
        //释放
        if(lock.isLocked()) {
            lock.unlock();
        }
    }
    return true;
}

我们公司对员工是选拔制,不是培养制,学生在选择来不来华为的时候,要考虑工作适宜不适宜你的兴趣爱好。不适宜就别来,浪费了你的青春。我们不能为学生设计命运。你进华为时就要考虑适应性,不能只看到工资高,要看给你的岗位是否符合你的兴趣爱好,你愿不愿意在这里贡献。华为不能容纳天下人,也不可能让天下人适应。

https://baijiahao.baidu.com/s?id=1760664270073856317&wfr=spider&for=pc
擦亮花火、共创未来——任正非在“难题揭榜”花火奖座谈会上的讲话
任正非

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值