最近工作中有个对工单单据进行加锁/解锁处理,目的是为了防止多用户对同一条单据处理。以下是完整的前后端代码。
前端JS billLock.js
        
          
            
            
              javascript
              复制代码
              
            
          
          let orderLock = {
    orderLock:(viewmodel, id, billType)=>{
        var url = '/orderLock/lock?domainKey='+viewmodel.getDomainKey();
        var proxy = cb.rest.DynamicProxy.create({
            ensure: {url: url, method: 'POST'}
        });
        let data = {"id":id, "billType": billType, "ttl": 60*60*12}
        proxy.ensure(data, function (err, result) {
            if (err) {
                cb.utils.alert(err.message, 'error');
            } else {
                cb.utils.alert(result, 'info');
                if(viewmodel.originalViewMeta.cBillNo && viewmodel.originalViewMeta.cBillNo=="02924ed1"){
                    //维修领料页面进入
                    if(result.indexOf('成功') <= 0){
                          viewmodel.biz.action().common.browse(viewmodel)
                          viewmodel.biz.action().common.setMode(viewmodel,"browse");  
                          
                    }
                }else{
                    if(result.indexOf('成功') <= 0){
                        viewmodel.biz.action().common.browse(viewmodel)
                    }
                }
                
            }
        })
    },
    unOrderLock:(viewmodel, id, billType)=>{
        var url = '/orderLock/unLock?domainKey='+viewmodel.getDomainKey();
        var proxy = cb.rest.DynamicProxy.create({
            ensure: {url: url, method: 'POST'}
        });
        var data = {"id":id, "billType": billType}
        proxy.ensure(data, function (err, result) {
            if (err) {
                cb.utils.alert(err.message, 'error');
            } else {
                cb.utils.alert(result, 'info');
            }
        })
    }
}
export default orderLock
  
         
      后端Java 代码
        
          
            
            
              java
              复制代码
              
            
          
          @RestController
@RequestMapping("/orderLock")
@Slf4j
public class OrderLockController extends BaseController {
    @Autowired
    private GmsRedisUtil gmsRRedisUtil;
    @Autowired
    private UserInfoService userInfoService;
    private String ENGINE_NO = "afterservice";
    /**
     * 加锁
     */
    @PostMapping(value = "/lock")
    public void orderLock(@RequestBody OrderLockDTO orderLockDTO, HttpServletResponse response) {
        try{
            String result;
            UserExtendDto userExtendDto = userInfoService.getUserExtend();
            String nowDate = DateUtil.dateFormat(PartCommonConstants.dateTimeFormatter, LocalDateTime.now());
            String key = ENGINE_NO + "_" + orderLockDTO.getBillType() + "_" + orderLockDTO.getId();
            String message = "锁定人["+userExtendDto.getStaffName()+"]锁定时间["+nowDate+"]";
            Boolean flag = gmsRRedisUtil.tryGetLock(key, message, orderLockDTO.getTtl(), 2);
            if(flag){
                result = "加锁成功!";
            }else{
                String lockValue = gmsRRedisUtil.getObject(key);
                if(lockValue.contains(userExtendDto.getStaffName())){
                    result = "加锁成功!原锁定用户["+userExtendDto.getStaffName()+"]进入单据!";
                }else{
                    result = "加锁失败!单据已被锁定,"+lockValue;
                }
            }
            renderJson(response, ResultMessage.data(result,false));
        }catch(Exception e){
            renderJson(response, ResultMessage.error(e.getMessage()));
            log.error("加锁失败:{}",e);
        }
    }
         
      代码解析(以上代码大致步骤如下:)
 1. 前端代码 参数是data,带有id,billtype,字符串ttl,时间变量 4个参数穿入后端。
 2.后端接收参数,调用gmsRRedisUtil.tryGetLock,这个方法是项目框架自己封装,原理是实现了redisTemplate.opsForValue().setIfAbsent方法
 
 
 3.setIfAbsent方法简单介绍如下:
        
          
            
            
              java
              复制代码
              
            
          
          1.setIfAbsent(K var1, V var2);
如果key不存在则新增,key存在不做任何操作
redisTemplate.opsForValue().setIfAbsent("BBB", "好的");
2.setIfAbsent(K var1, V var2, long var3, TimeUnit var5)
如果key不存在则新增,同时设置过期时间,key存在不做任何操作。
redisTemplate.opsForValue().setIfAbsent("AAA", "好的", 1, TimeUnit.MINUTES);
3.setIfAbsent(K key, V value, Duration timeout)
如果key不存在则新增,同时设置过期时间,key存在不做任何操作。
redisTemplate.opsForValue().setIfAbsent("BBB", "好的", Duration.ofMinutes(1));
         
      4.如果单据第一次进入,key不存在,flag为true,直接返回给前端"加锁成功",前端解析字符串中含有"成功,不设置页面为浏览态,仍为编辑态
        
          
            
            
              
              复制代码
              
            
          
          本次加锁我调用的是第2个方法,这里的第一个参数key可见是单据id加一些特定字符。所以单据id就会被加锁,并设置加锁时间\
         
      5 如果单据在已经加锁的情况下,仍有其他用户编辑此单据,则key重复,gmsRRedisUtil.tryGetLock不做操作。
        
          
            
            
              
              复制代码
              
            
          
          	gmsRRedisUtil.getObject(key)实际是调用redisTemplate.opsForValue().get(key) 方法,
	其中 get(Object var1)  是获取指定的key对应的值。
	第一次加锁的message是用户信息,这里如果是不同用户登录的话,
    if(lockValue.contains(userExtendDto.getStaffName())) 就为false,返回给前端的信息是"加锁失败...",
    前端获取字符串信息中没有加锁成功,会设置页面为浏览态,不能编辑。 到此,加锁完成!
         
      