业务场景

针对一个赔付工单(由底下小二发起),当金额数量大于一定值以后,针对这笔工单就会有层层审批(风控),先YY一个审批流「TL审批」--->「主管审批」--->「财务审批」.这里就会存在3种权限「一审权限」「二审权限」「终审权限」,当这笔工单被小二提交以后就会给小二对应的TL创建一个审批任务,在主管的界面就可以看到相应的审批任务,主管可以点击通过或者拒绝

实现

    1.查询任务,判断当前角色是否有权限操作该笔任务,任务没有完结等一系列校验
    2.驱动状态机更新工单状态
    3.完结任务
    

异常场景

场景一
  1. 操作:主管疯狂点击通过按钮3次(前提是按钮点击一次不会灰显,就算灰显也可以通过模拟请求来实现)
  2. 异常情况:三个点击线程都运行完1,然后线程1驱动状态机(通过 + 当前状态:待TL审批)得到的结果是待主管审批,紧接着线程2驱动状态机(通过 + 当前状态:待主管审批)得到的结果是待财务审批,然后线程3再运行驱动状态机(通过 + 当前状态:待财务审批)得到的结果是出账成功(不考虑多次完结任务会出现异常)
  3. 异常分析:在该场景中,只要角色拥有「一审权限」就可以通过漏洞直接把该订单审核出账
  4. 解决方案:

    • 细化状态机中的每个审核操作
      1.查询任务,查询该笔任务对应当前操作类型(TL审批OR主管审批OR财务审批)
      ---这里不仅仅是「通过」操作,判断当前角色是否有对应操作权限
      2.根据当前操作类型+当前状态驱动状态机
      3.完结任务
    • 在查询任务之前加一个全局锁,针对这笔工单进行全局锁定(更加优雅)

场景二

  1. 操作:一个角色同时拥有一审权限和二审权限,当他打开一个工单要进行一审,但是其它主管已经执行了一审的动作,并且更新了一些信息.因为该角色页面没有刷新获取不到更新的信息,然后点击了通过按钮
  2. 异常情况:点击通过按钮就相当于进行了二审,会造成该角色获取不到最新信息而产生误判
  3. 解决方案:在场景一增加一个全局锁的前提下,可以给页面传递一个工单modifyTime,在全局锁里面判断modifyTime是否一致,如果不一致说明该笔工单已经被更新了,可以给用户相应的提醒

其它

  1. 在业务中整个状态机的流转都是确定的,所以为了保证状态流转正确,所以在更新数据库状态的时候,需要带上关于状态的乐观锁UPDATE XXX SET status = XXX WHERE id = XXX AND from_status = XXX
  2. 如果使用的是全局锁,那么每个操作工单的地方都需要加上相应的全局锁

iMouseWu
1.6k 声望53 粉丝

Conding with Java