最近工作中有个对工单单据进行加锁/解锁处理,目的是为了防止多用户对同一条单据处理。以下是完整的前后端代码。
前端JS billLock.js
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 代码
@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方法简单介绍如下:
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,返回给前端的信息是“加锁失败...”,
前端获取字符串信息中没有加锁成功,会设置页面为浏览态,不能编辑。 到此,加锁完成!