流水线模式应用设计

流水线模式介绍

通过流水线模式使代码具有职责单一、移植性高、扩展性强、代码复用性强、代码清晰。

将一个业务逻辑拆分为若干个小的逻辑单元,之后将这些小的逻辑单元按顺序添加到管道中,这样就组成一个有顺序的管道。

职责单一:将一个大的业务逻辑拆分为若干个小的逻辑单元。

移植性高:每个小的逻辑单元可以通过可拔插方式添加移除到管道中。

扩展性强:增强业务逻辑,只需向管道添加小的逻辑单元即可。

代码复用性强:将业务逻辑封装在若干个小的逻辑单元中,使得每一个单元块只聚合某一点功能,复用性大大增加。

代码清晰:将业务逻辑封装在若干个小的逻辑单元,使得代码聚合,如果代码有改动只需要改动某一个小的单元,而避免影响这个业务逻辑。

工作使用场景

功能简介

列表查询/查询详情/业务系统用户菜单权限/业务系统查询全部菜单 ,他们都共用到MenuEntityToMenuDtoList这个单元块,这里体现到代码复用性强,又因为MenuEntityToMenuDtoList中职责单一和聚合才体现代码复用性强。

列表查询

列表查询功能:首先从库中查询出菜单集合 ,之后将数据库实体对象MenuEntity转换为MenuDto,之后是对MenuDto的属性设置(SetResourceToMenuList/AddRecouceButtonToMenuList),接下来是校验用户是否对此采用有操作权限,最后是将MenuDto转换为菜单树。

QueryMenuList(从库中查询出菜单列表) -> MenuEntityToMenuDtoList(将MenuEntity实体转换为
MenuDto) -> SetResourceToMenuList(设置MenuDto关联表Resource额外属性,增强MenuDto) -> 
AddRecouceButtonToMenuList(添加资源按钮,增强MenuDto) -> CheckMenuResoucePerimissionList
(校验用户是否对此菜单有操作权限) -> MenuListToTree(MenuDto转换菜单树)

查询详情

MenuById(从库中通过id查询出菜单) -> MenuEntityToMenuDtoList(将MenuEntity实体转换为
MenuDto) -> SetResourceToMenuList(设置MenuDto关联表Resource额外属性,增强MenuDto)

保存

业务系统用户菜单权限

业务系统查询全部菜单 

 代码设计

流水线代码

public class Pipeline<I,O> {
    private final Handler<I,O> currentHandler;
    public Pipeline(Handler<I, O> currentHandler) {
        this.currentHandler = currentHandler;
    }

    public <K> Pipeline<I, K> addHandler(Handler<O, K> newHandler) {
        return new Pipeline<>(input -> newHandler.process(currentHandler.process(input)));
    }

    public boolean before(Object o){
        return true;
    }

    public void after(O o){
    }

    public O execute(I input) {
        return currentHandler.process(input);
    }

}
public interface Handler<I,O> {
    O process(I input);
}

菜单列表查询代码 

public class QueryMenuList implements Handler<String, List<SyMenu>> {

    @Override
    public List<SyMenu> process(String input) {

        SyMenuMapper syMenuMapper = SpringApplicationUtils.getBean(SyMenuMapper.class);
        QueryWrapper qw = Wrappers.query().eq("service_id", CurrentUserContext.getServiceId());
        qw.eq("menu_source",input);
        List<SyMenu> menus = syMenuMapper.selectList(qw);
        return menus;

    }
}
public class MenuEntityToMenuDtoList implements Handler<List<SyMenu>, List<SyMenuDto>> {
    @Override
    public List<SyMenuDto> process(List<SyMenu> input) {
        if (CollectionUtils.isEmpty(input)){
            return Collections.emptyList();
        }
        List<SyMenuDto> dtos = input.stream().map(v ->{
            SyMenuDto dto = new SyMenuDto();
            BeanUtils.copyProperties(v,dto);
            dto.setResourceId(v.getResourceId());
            dto.setId(v.getId());
            dto.setParentId(v.getParentId());
            return dto;
        }).collect(Collectors.toList());
        return dtos;
    }
}
public class SetResourceToMenuList implements Handler<List<SyMenuDto>, List<SyMenuDto>> {
    @Override
    public List<SyMenuDto> process(List<SyMenuDto> input) {
        if (CollectionUtils.isEmpty(input)){
            return Collections.emptyList();
        }
        ResourceMapper resourceMapper = SpringApplicationUtils.getBean(ResourceMapper.class);
        for (SyMenuDto dto : input){
            if (dto.getResourceId() != null && dto.getResourceId() != -1){
                SyResource resource = resourceMapper.selectById(dto.getResourceId());
                if (resource != null){
                    dto.setResourceName(resource.getResourceName());
                }
            }
        }
        return input;
    }
}
/**
 *添加资源按钮,查找资源下按钮,生成一条按钮menu记录
 * Created by Jiyang.Zheng on 2022/4/19 13:29.
 */
@Slf4j
public class AddRecouceButtonToMenuList implements Handler<List<SyMenuDto>, List<SyMenuDto>> {
    @Override
    public List<SyMenuDto> process(List<SyMenuDto> input) {
        ResourceMapper resourceMapper = SpringApplicationUtils.getBean(ResourceMapper.class);
        if (CollectionUtils.isEmpty(input)){
            return Collections.emptyList();
        }
        List<Integer> resourceIds = input.stream().filter(v -> v.getResourceId() != null).map(v -> v.getResourceId()).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(resourceIds)){
            return Collections.emptyList();
        }
        List<SyMenuDto> newMenuDto = Lists.newArrayList();
        newMenuDto.addAll(input);
        Map<Integer,List<Integer>> resouceUseMenuIds = getResouceUseMenuIds(input);
        List<SyResource> resouces = resourceMapper.selectBatchIds(resourceIds);
        for (SyResource resource : resouces){
            QueryWrapper qw = Wrappers.query().eq("parent_id",resource.getId()).eq("resource_type","button");
            List<SyResource> buttonResouces = resourceMapper.selectList(qw);
            if (CollectionUtils.isEmpty(buttonResouces)){
                continue;
            }
            List<Integer> menuIds = resouceUseMenuIds.get(resource.getId());
            if (CollectionUtils.isEmpty(menuIds)){
                continue;
            }
            for (Integer parentId : menuIds){
                double i = 0;
                for (SyResource br : buttonResouces){
                    SyMenuDto bn = new SyMenuDto();
                    BeanUtils.copyProperties(br,bn);
                    bn.setParentId(parentId);
                    bn.setMenuName(br.getResourceName());
                    bn.setMenuType("button");
                    bn.setSortNum(i);
                    bn.setWatermark(0);
                    bn.setResourceId(br.getId());
                    newMenuDto.add(bn);
                    i++;
                }
            }
        }
        return newMenuDto;
    }
    private Map<Integer,List<Integer>> getResouceUseMenuIds(List<SyMenuDto> input){
        //标识资源拥有菜单
        Map<Integer,List<Integer>> resouceUseMenuIds = Maps.newHashMap();
        for (SyMenuDto md : input){
            List<Integer> l = resouceUseMenuIds.get(md.getResourceId());
            if (l == null){
                l = new ArrayList<>();
                resouceUseMenuIds.put(md.getResourceId(),l);
            }
            l.add(md.getId());
        }
        return resouceUseMenuIds;
    }
}

public class CheckMenuResoucePerimissionList implements Handler<List<SyMenuDto> , List<SyMenuDto> > {
    @Override
    public List<SyMenuDto> process(List<SyMenuDto> input) {
        if(CollectionUtils.isEmpty(input)){
            return Collections.emptyList();
        }
        Handler<Void, List<SyResourceDto>> handler = new MenuResouceQueryList(MenuResoucePerimissionDataList.QUERY_PERIMISSION_BACK);
        Pipeline pipeline = new Pipeline<>(handler);
        List<SyResourceDto> list = (List<SyResourceDto>) pipeline.execute(null);
        if (CollectionUtils.isNotEmpty(list)){
            Map<Integer,SyResourceDto> map = list.stream().distinct().collect(Collectors.toMap(v -> v.getId(), v ->v));
            input.stream().forEach(v ->{
                if (!StringUtils.equals(v.getMenuType(),"page")){
                    v.setIsUsePerimission(true);
                }else {
                    SyResourceDto syResourceDto = map.get(v.getResourceId());
                    if (syResourceDto != null){
                        v.setIsUsePerimission(true);
                    }
                }

            });
        }
        return input;
    }
}
public class MenuListToTree implements Handler<List<SyMenuDto>, List<TreeModel>> {
    @Override
    public List<TreeModel> process(List<SyMenuDto> input) {
        List<TreeModel> treeModels = AbstarctGenerateTree.buidTree(input);
        return treeModels;
    }
}
public static void main(String[] args) {
        String menuSource = "";
        Handler<String, List<SyMenu>> handler = new QueryMenuList();
        Pipeline pipeline = new Pipeline<>(handler)
//                .addHandler(new RecursionParentMenuList())
                .addHandler(new MenuEntityToMenuDtoList())
                .addHandler(new SetResourceToMenuList())
                .addHandler(new AddRecouceButtonToMenuList())
                .addHandler(new CheckMenuResoucePerimissionList())
                .addHandler(new MenuListToTree());
        List<TreeModel> treeModels = (List<TreeModel>) pipeline.execute(menuSource);
    }

特殊需求扩展代码

继承Pipeline重新Pipeline内部方法before

public class EditResouceMenuPipeline<I, O> extends Pipeline<I, O> {
    private final Handler<I, O> currentHandler;
    public EditResouceMenuPipeline(Handler<I, O> currentHandler) {
        super(currentHandler);
        this.currentHandler = currentHandler;
    }

    @Override
    public <K> EditResouceMenuPipeline<I, K> addHandler(Handler<O, K> newHandler) {
        return new EditResouceMenuPipeline<>(input -> newHandler.process(currentHandler.process(input)));
    }

    @Override
    public boolean before(Object o) {
        SyResource resource = (SyResource) o;
        boolean b =  checkEdit(resource);
        if (b) {
            throw BusinessException.withErrorMessage(resource.getResourceName() + ":已存在");
        }
        return false;
    }

    private boolean checkEdit(SyResource syResource) {
        ResourceMapper resourceMapper = SpringApplicationUtils.getBean(ResourceMapper.class);
        QueryWrapper qw = Wrappers.query()
                .eq("resource_name", syResource.getResourceName())
                .eq("resource_type", ResourceServiceImpl.RESOURCE_TYPE_PAGE)
                .eq("service_id", CurrentUserContext.getServiceId());
        SyResource selectOne = resourceMapper.selectOne(qw);
        if (selectOne != null) {
            boolean flag = true;
            if (syResource.getId() != null && ObjectUtils.equals(selectOne.getId(), syResource.getId())) {
                flag = false;
            }
            return flag;
        }
        return false;
    }
}

public Integer save(SyResource resource) {
        Handler<SyResource, SyResource> handler = new EditAppIcon();
        Pipeline pipeline = new EditResouceMenuPipeline<>(handler)
                .addHandler(new EditMenuName())
                .addHandler(new EditWebIcon())
                .addHandler(new EditMenuResouce());
        if (pipeline.before(resource)) {
            throw BusinessException.withErrorMessage(resource.getResourceName() + ":已存在");
        }
        Integer id = (Integer) pipeline.execute(resource);
        return id ;
    }

pipeline日志扩展

记录每个流水的入参和出参做日志打印

package com.geely.grms.rms.service.service.pipeline;


import cn.evun.sweet.framework.core.mvc.BusinessException;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

/**
 *
 * Created by Jiyang.Zheng on 2022/4/19 11:09.
 */
@Slf4j
public class Pipeline<I,O> {
    private final Handler<I,O> currentHandler;
    public Pipeline(Handler<I, O> currentHandler) {
        this.currentHandler = currentHandler;
    }


    public static ThreadLocal<StringBuilder> CurrentLog = new ThreadLocal<>();
    public <K> Pipeline<I, K> addHandler(Handler<O, K> newHandler) {
        return new Pipeline<>(input -> {
            O o = currentHandler.process(input);
            StringBuilder sb = CurrentLog.get();
            K k;
            try {
                k = newHandler.process(o);
                if (sb == null){
                    sb = new StringBuilder();
                    CurrentLog.set(sb);
                }
                log(sb,currentHandler,input,o);
                sb.append("  -> \n");
                log(sb,newHandler,o,k);
                return k;
            }catch (Exception e){
                String name = newHandler.getClass().getName();
                PipelineHandleName nextat = newHandler.getClass().getAnnotation(PipelineHandleName.class);
                if (nextat != null) {
                    name = nextat.name();
                }
                if (sb != null){
                    sb.append("  -> "+e.getMessage());
                }
                String errorMsg =  "执行【"+name+"】异常,param:"+JSONObject.toJSONString(o)+",ErrorMsg:"+e.getMessage();
//                log.error(errorMsg,e);
                throw new BusinessException(errorMsg,e);
            }
        });
    }

    public boolean before(Object o){
        return true;
    }

    public void after(O o){
        if (CurrentLog.get() != null){
            log.info("pipeline execute log {}",CurrentLog.get().toString());

        }
        CurrentLog.remove();
    }



    public O execute(I input) {
        O o = currentHandler.process(input);
        after(null);
        return o;
    }

    private  void  log(StringBuilder sb,Object newHandler,Object o,Object k){
        PipelineHandleName nextat = newHandler.getClass().getAnnotation(PipelineHandleName.class);
        if (nextat != null){
            sb.append(nextat.order()+":"+nextat.name());
            if (nextat.isParamLog()){
                String inStr = JSONObject.toJSONString(o);
                String ouStr = JSONObject.toJSONString(k);
                sb.append(","+"input【"+inStr+"】,output【"+ouStr+"】");

            }
        }
    }

}
package com.geely.grms.rms.service.service.pipeline;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PipelineHandleName {

    /**
     * 顺序
     * @return
     */
    int order() default  1;

    /**
     * 名称
     * @return
     */
    String name();

    /**
     * 是否记录请求参数和返回参数日志
     * @return
     */
    boolean isParamLog() default true;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值