改状态,你真的会改吗?,qq下面的状态怎么改
改状态,你真的会改吗?,qq下面的状态怎么改
企业应用中,涉及到修改状态的场景太多了。比如,企业入网后,要审核资质。个人领取任务后,企业管理员要审核领取人。
应用管理系统中,通常是下图这样,在列表后有操作按钮来修改数据记录的状态。
点击“通过”/“拒绝”操作,要修改数据记录的status字段。服务端程序逻辑怎么实现呢?
先定义服务端api接口:
系统采用前后端分离,系统架构是分布式的。SpringMVC的RestController通过远程调用Dubbo接口,来实现数据的CRUD。
项目使用jeecg-boot逆向工程生成代码,然后在此基础上进行调整。
程序逻辑v1
@RequestMapping(value = "/enterprise/taskApply/audit",method = RequestMethod.POST) public Result audit(@RequestBody TaskApplyVO taskApplyVO, HttpServletRequest req){ //当前登录企业 EnterpriseVO enterpriseVo = EnterpriseContext.getEnterpriseVo(); if(enterpriseVo == null){ return Result.error("未登录"); } if(StringUtils.isEmpty(taskApplyVO.getApplyIds())){ return Result.error("未选择"); } String[] applyIds = taskApplyVO.getApplyIds().split(","); for (String applyId : applyIds) { TaskApplyVO taskApply = new TaskApplyVO(); taskApply.setApplyId(Long.parseLong(applyId)); taskApply.setStatus(taskApplyVO.getStatus()); taskApply.setAuditTime(DateUtils.getDate()); taskApplyService.updateById(taskApply); } return Result.ok("成功"); }
其中,taskApplyService是远程RPC接口实例。
系统使用了一段时间后,bug出现了————已经审核完了的记录还能再审核。什么情况下会出现这种情况呢?我们且不说。不过看代码逻辑,我们可以发现,在修改一条记录的状态字段之前并没有判断状态字段的初始值(在 待审核 状态下 才能修改为 审核通过or审核拒绝),所以会出现这种情况。
程序逻辑v2
修复上面的bug。加上前置状态判断。
@RequestMapping(value = "/enterprise/taskApply/audit",method = RequestMethod.POST) public Result audit(@RequestBody TaskApplyVO taskApplyVO, HttpServletRequest req){ //当前登录企业 EnterpriseVO enterpriseVo = EnterpriseContext.getEnterpriseVo(); if(enterpriseVo == null){ return Result.error("未登录"); } if(StringUtils.isEmpty(taskApplyVO.getApplyIds())){ return Result.error("未选择"); } String[] applyIds = taskApplyVO.getApplyIds().split(","); for (String applyId : applyIds) { TaskApplyVO taskApply = taskApplyService.getById(applyId); if(!TaskApplyStatusEnum.TO_AUDIT.name().equals(taskApply.getStatus())){ continue; } taskApply.setStatus(taskApplyVO.getStatus()); taskApply.setAuditTime(DateUtils.getDate()); taskApplyService.updateById(taskApply); } return Result.ok("成功"); }
系统使用了一段时间,bug出现了————用户对数据记录的修改莫名其妙的丢失了。哈哈,看上面的代码,并发较多下,对这张数据表的同一条记录的多个不同操作请求都出现时,比如这里是审核,同时还有信息修改,是不是会出现覆盖的情况?是的,因为在rpc调用getById与rpc调用updateById之间是有时间间隔的。两个线程都通过getById取到了数据记录,然后都修改了不同的字段后执行updateById,数据库操作本身是有先后的,所以,可能就会出现其中一次update覆盖另一次update。那怎么改呢?用分布式事务来控制?那可就比较费事了。为了减少这种冲突发生的可能性,还是先重构一下我们这个方法的逻辑吧。
程序逻辑v3
修改上面逻辑,new一个实体对象,只update所需字段。
@RequestMapping(value = "/enterprise/taskApply/audit",method = RequestMethod.POST) public Result audit(@RequestBody TaskApplyVO taskApplyVO, HttpServletRequest req){ //当前登录企业 EnterpriseVO enterpriseVo = EnterpriseContext.getEnterpriseVo(); if(enterpriseVo == null){ return Result.error("未登录"); } if(StringUtils.isEmpty(taskApplyVO.getApplyIds())){ return Result.error("未选择"); } String[] applyIds = taskApplyVO.getApplyIds().split(","); for (String applyId : applyIds) { TaskApplyVO byId = taskApplyService.getById(applyId); if(!TaskApplyStatusEnum.TO_AUDIT.name().equals(byId.getStatus())){ continue; } TaskApplyVO taskApply = new TaskApplyVO(); taskApply.setApplyId(Long.parseLong(applyId)); taskApply.setStatus(taskApplyVO.getStatus()); taskApply.setAuditTime(DateUtils.getDate()); taskApplyService.updateById(taskApply); } return Result.ok("保存成功"); }
系统使用了一段时间,bug出现了。什么bug?还是最开始的bug————已经审核完了的记录还能再审核。 !!!奔溃了~~
程序逻辑v4
使用状态锁。
同时,变更程序实现。将审核的逻辑后置封装到RPC服务里。
@RequestMapping(value = "/enterprise/taskApply/audit",method = RequestMethod.POST) public Result audit(@RequestBody TaskApplyVO taskApplyVO, HttpServletRequest req){ //当前登录企业 EnterpriseVO enterpriseVo = EnterpriseContext.getEnterpriseVo(); if(enterpriseVo == null){ return Result.error("未登录"); } if(StringUtils.isEmpty(taskApplyVO.getApplyIds())){ return Result.error("未选择"); } String[] applyIds = taskApplyVO.getApplyIds().split(","); return taskApplyService.audit(applyIds, taskApplyVO.getStatus()); }
taskApplyService实现类中的audit方法:
@Override public Result audit(String[] taskIdArr, String auditStatus) { for (String id : taskIdArr) { TaskApply taskApply = new TaskApply(); taskApply.setStatus(auditStatus); taskApply.setAuditTime(DateUtils.getDate()); taskApplyManager.update(taskApply, new LambdaQueryWrapper<TaskApply>() .eq(TaskApply::getApplyId, Long.parseLong(id)) .eq(TaskApply::getStatus, TaskApplyStatusEnum.TO_AUDIT.name())); } return Result.ok(); }
我们修改mybatis-plus配置,把程序执行的sql打印出来
mybatis-plus:
configuration:
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
如下是程序执行的sql。where条件里有status字段。update执行成功返回1,否则返回0。
==> Preparing: UPDATE emax_task_apply SET apply_status=?, audit_time=? WHERE apply_id = ? AND status = ? ==> Parameters: TASKAPPLY_PASS(String), 2020-03-23 16:52:44.186(Timestamp), 1(Long), TO_AUDIT(String) <== Updates: 1
结束
感谢阅读,本文整理用时2:51:00(18:00~20:51)。不足之处,欢迎交流!
相关文章
- 暂无相关文章
用户点评