SpringAop切面实践-返回数据过滤填充

1339 21~28 min

SpringAop切面实践-返回数据过滤填充

需求: 公司业务中引入工作流框架,涉及到代办任务的操作,但产品设计的是主要是业务单据为准,没有一个单独的待审核列表。所以无法用到Activiti自带的查询代办任务的方法,鉴于各个模块中都存在工作流的运用,所以最好的办法只能是做到最小的侵入,和其他模块只关心自身业务,添加一个注解完事!前端的话就只要需要根据切面返回的数据中的某个字段,动态的展示审核按钮~

自定义注解

/**
 * @author <a href="mailto:[email protected]">文刀草乙</a>
 * @date 2021/11/3 16:49
 * @project
 * @Title: ProcDataFilter.java
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ProcData {

    /**
     * 流程定义的Key ActTemplate中定义的Key 流程设计定义key
     *
     * @return
     */
    public String procKey() default "";

    /**
     * 业务数据ID 启动流程时放的数据ID 需要反射到工作流服务中查询是否具有审核权限
     *
     * @return
     */
    public String businessKey() default "";
}

切面逻辑

/**
 * 带有流程的数据列表的审核权限的过滤
 *
 * @author <a href="mailto:[email protected]">刘艺</a>
 * @date 2021/11/3 17:04
 * @project
 * @Title: ProcFilterAspect.java
 **/
@Aspect
@Component
@Slf4j
@Order(9) // 定义切面的执行顺序
public class ProcFilterAspect {

    @Resource  
    private RemoteActTaskApiService apiService; // 远程调用工作流服务进行数据是否具有审核权限的判断

    /**
     * 列表方法执行完成之后针对返回的数据做处理判断
     *
     * @param point
     * @return java.lang.Object
     * @author <a href="mailto:[email protected]">文刀草乙</a>
     * @date 2021/11/3 17:10
     */
    //  采用AfterReturning 通知在正常的业务数据处理完成之后做增强处理 其中returning = "returnData" 就是业务处理完成之后返回的列表数据
    @AfterReturning(pointcut = "@annotation(com.qdd.act.api.annotation.ProcData)", returning = "returnData")
    public void afterReturnData(JoinPoint point, Object returnData)
    throws Throwable
    {
        //目的:获取切入点方法上自定义ProcData注解中procKey属性值
        //1.1获取目标对象对应的字节码对象
        Class<?> targetCls = point.getTarget().getClass();
        //1.2获取目标方法对象
        //1.2.1 获取方法签名信息从而获取方法名和参数类型
        Signature signature = point.getSignature();
        //1.2.1.1将方法签名强转成MethodSignature类型,方便调用
        MethodSignature ms = (MethodSignature)signature;
        //1.2.2通过字节码对象以及方法签名获取目标方法对象
        Method targetMethod = targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        //1.3获取目标方法对象上注解中的属性值
        //1.2.3 获取方法上的自定义ProcData注解
        ProcData procData = targetMethod.getAnnotation(ProcData.class);
        //1.2.4 获取自定义注解中procKey属性的值
        String procKey = procData.procKey();
        String businessKey = procData.businessKey();
        // 如果返回的是TableInfo 判断业务数据返回的是TableInfo 对象
        if (returnData instanceof TableDataInfo) {
            TableDataInfo dataInfo = (TableDataInfo)returnData;
            List<?> rows = dataInfo.getRows();
            if (dataInfo.getCode() == HttpStatus.SUCCESS && CollectionUtils.isNotEmpty(rows)) {
// 开始处理返回的业务数据
                List<?> returnDatas = handleData(dataInfo.getRows(), procKey, businessKey);
                dataInfo.setRows(CollectionUtils.isNotEmpty(returnDatas) ? returnDatas : rows);
            }
        }
    }

    /**
     * 处理返回的List数据
     *
     * @param rows
     * @param key
     * @param businessKey
     * @return void
     * @author <a href="mailto:[email protected]">文刀草乙</a>
     * @date 2021/11/3 18:01
     */
    public List<?> handleData(List<?> rows, String key, String businessKey)
    {
        String currentUserId = String.valueOf(SecurityUtils.getUserId());
        ArrayList<ProcListHandleData> lists = new ArrayList<>();
        for (int i = 0; i < rows.size(); i++) {
            Object o = rows.get(i);
            try {
                String id = Objects.requireNonNull(getGetMethod(o, businessKey)).toString();
                if (StringUtils.isNotBlank(id)) {
                    ProcListHandleData procListHandleData = new ProcListHandleData();
                    procListHandleData.setBusinessKey(Long.valueOf(id));
                    procListHandleData.setProcDefKey(key);
                    procListHandleData.setCurrentUserId(currentUserId);
                    lists.add(procListHandleData);
                }
            }
            catch (NoSuchMethodException | IllegalAccessException e) {
                e.printStackTrace();
            }
            catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (CollectionUtils.isNotEmpty(lists)) {
            // 远程调用处理是否具有审核权限的逻辑
            R<List<ProcListHandleData>> handleProList = apiService.handleProcList(lists);
            if (handleProList.getCode() == HttpStatus.SUCCESS) {
                return handleButtonShow(rows, handleProList.getData(), businessKey);
            }
        }

        return new ArrayList<>();
    }

    /**
     * 根据远程调用的结果来处理按钮到时显示不显示
     *
     * @param rows
     * @param lists
     * @param businessKey
     * @return void
     * @author <a href="mailto:[email protected]">文刀草乙</a>
     * @date 2021/11/4 9:53
     */
    public List<Object> handleButtonShow(List<?> rows, List<ProcListHandleData> lists, String businessKey) {
        List<Object> objects = new ArrayList<>();
        for (Object row : rows) {
            try {
                String id = Objects.requireNonNull(getGetMethod(row, businessKey)).toString();
                if (StringUtils.isNotBlank(id)) {
                    ProcListHandleData handleData = (ProcListHandleData)lists.stream().filter(e -> Objects.equals(id, String.valueOf(e.getBusinessKey()))).distinct().toArray()[0];
                    Map<String, Object> propertiesMap = new HashMap<>(1);
                    propertiesMap.put("showAuditButton", handleData.getShowAuditButton());
                    Object obj = PropertyAppender.generate(row, propertiesMap);
                    objects.add(obj);
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        return objects;
    }

    /**
     * 根据属性,获取get方法
     *
     * @param ob
     * @param name
     * @return java.lang.Object
     * @author <a href="mailto:[email protected]">刘艺</a>
     * @date 2021/11/4 14:12
     */
    public static Object getGetMethod(Object ob, String name)
    throws Exception
    {
        Method[] m = ob.getClass().getMethods();
        for (int i = 0; i < m.length; i++) {
            if (("get" + name).equalsIgnoreCase(m[i].getName())) {
                return m[i].invoke(ob);
            }
        }
        return null;
    }

校验是否具有审核权限

 /**
     * 根据传过来数据筛选是否展示审核按钮
     *
     * @param dataList
     * @return com.qdd.common.core.domain.R<java.util.List < com.qdd.act.api.domain.ProcListHandleData>>
     * @author <a href="mailto:[email protected]">文刀草乙</a>
     * @date 2021/11/4 9:19
     */
    @Override
    public R<List<ProcListHandleData>> handleProcList(List<ProcListHandleData> dataList) {
        RequestContextHolder.getRequestAttributes();
        // 获取当前登陆人
        Long userId = SecurityUtils.getUserId();
        List<BizNode> bizNodes = (List<BizNode>)bizNodeService.selectBizNodeByProcInfo(new BizNode().setProcKey(dataList.get(0).getProcDefKey()));
        for (ProcListHandleData procListHandleData : dataList) {
            BizBusiness business = businessService.selectBizBusinessByBusinessKey(new BizAudit().setBusinessKey(procListHandleData.getBusinessKey()).setProcDefKey(procListHandleData.getProcDefKey()));
            if (Objects.nonNull(business)) {
                TaskEntity currentTask = getCurrentTask(business.getProcInstId());
                if (Objects.nonNull(currentTask)) {
                    boolean result = checkCurrenTaskAndHisReapt(business, currentTask, userId);
                    if (!result) {
                        Object[] objects = bizNodes.stream().filter(e -> Objects.equals(currentTask.getTaskDefinitionKey(), e.getNodeId())).distinct().toArray();
                        BizNode node = (objects.length >= 1) ? (BizNode)objects[0] : new BizNode();
                        // checkAuditUse() 主要检验当前登录人 角色、岗位、指定人是否是在审核人权限列表中 ,校验结果放入到setShowAuditButton结果中
                        procListHandleData.setShowAuditButton((HttpStatus.SUCCESS == checkAuditUser(node, Objects.isNull(userId) ?
                            Long.parseLong(procListHandleData.getCurrentUserId()) :
                            userId).getCode()) ? "1" : "0");
                    }
                }
            }
        }
   // 将没有审核权限的或者是为null的放到最后 友好展示给前端
        return R.ok(dataList.stream().sorted(Comparator.comparing(ProcListHandleData::getShowAuditButton, Comparator.<String>nullsLast(String::compareTo))).collect(Collectors.toList()));
    }

食用说明

后端在请求审核列表的Controller方法上添加注解 @ProcData(procKey = "流程定义的Key", businessKey = "启动流程时放入的业务数据ID 的字段名")如下

    @ProcData(procKey = "GOODS_CHANGE_AUDIT", businessKey = "id") @RequestMapping("goodsAuditList") 
    public TableDataInfo goodsAuditList(@RequestBody GoodsChangeAudit goodsChangeAudit)
    {
          startPage();
          List<GoodsChangeAudit> goodsChangeAuditList=qddGoodsService.goodsAuditList(goodsChangeAudit);
          return getDataTable(goodsChangeAuditList);
    }

前端根据返回rowsList数据中的 showAuditButton 字段展示审核按钮 1-展示 0-不展示

showAuditButton 字段的填充