当前位置: 欣欣网 > 码农

进阶玩法:策略+责任链+组合实现合同签章

2024-06-22码农

点击「 IT码徒 」, 关注,置顶 公众号

每日技术干货,第一时间送达!

签章的处理流程

  1. 合同文本初始化

  2. 合同文本生成

  3. 签章挡板是否开启

  4. 合同签章发送mq

  5. 合同签章流水更新

  6. 合同上传文件服务器

  7. 签章渠道选择

  8. 签章渠道的实际调用

执行的流程如下:

整个结构类似于递归调用。每个节点中依赖上一个节点的输入以及下一个节点的输出,在中间过程可以实现每个节点的自定义操作,比较灵活。

流程实现

https://github.com/xbhog/DesignPatternsStudy

项目结构

DesignPatterns
└── src
└── main
└── java
└── com.xbhog.chainresponsibility
├── annotations
│ └── ContractSign
├── channel
│ ├── ContractSignChannelImpl.java
│ └── ContractSignChannel
├── Config
│ └── SignConfig
├── Enum
│ └── ContractSignEnum
├── impl
│ ├── ContractSignCompactInitImpl.java
│ ├── ContractSignGenerateImpl.java
│ ├── ContractSignMockImpl.java
│ ├── ContractSignMqImpl.java
│ ├── ContractSignSaveUploadImpl.java
│ ├── ContractSignSerialImpl.java
│ └── ContractSignTradeImpl.java
├── inter
│ ├── Call
│ ├── Chain
│ ├── Interceptor
│ └── Processor
├── pojo
│ ├── ContractRequest.java
│ └── ContractResponse.java
├── ContractCall
├── ContractChain
└── ContractSignProcessor.java

项目类图

责任链+组合模式代码实现

工程结构

DesignPatterns
└── src
└── main
└── java
└── com.xbhog.chainresponsibility
├── channel
│ ├── ContractSignChannelImpl.java
│ └── ContractSignChannel
├── impl
│ ├── ContractSignCompactInitImpl.java
│ ├── ContractSignGenerateImpl.java
│ ├── ContractSignMockImpl.java
│ ├── ContractSignMqImpl.java
│ ├── ContractSignSaveUploadImpl.java
│ ├── ContractSignSerialImpl.java
│ └── ContractSignTradeImpl.java
├── inter
│ ├── Call
│ ├── Chain
│ ├── Interceptor
│ └── Processor
├── pojo
│ ├── ContractRequest.java
│ └── ContractResponse.java
├── ContractCall
├── ContractChain
└── ContractSignProcessor.java

责任链中的对象定义

//请求
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public classContractRequest{
private String name;
private String age;
private String status;
}
//响应
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public classContractResponse{
private String status;
private String mas;
}


定义流程中的请求及响应类,方便处理每个责任链的请求、返回信息。

责任链处理流程

/**
 * @author xbhog
 * @describe: 责任链+组合实现合同签章
 * @date 2023/7/11
 */

@Slf4j
@Component
public classContractSignProcessor <TextendsContractRequestimplementsProcessor<TContractResponse{
@Resource(name = "contractSignCompactInitImpl")
private Interceptor<T,ContractResponse> contractCompactInitImpl;
......

publicContractSignProcessor(){
}
@Override
public ContractResponse process(T paramter){
//获取所有的监听器
List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
interceptorList.add(contractCompactInitImpl);
......
//开始签章
log.info("签章开始");
returnnew ContractCall(paramter,interceptorList).exectue();
}
}

合同签章方法的主流程调用接口(入口) ,该类中注入所有的节点实现类(如 contractCompactInitImpl ),通过编排实现责任链流程。在初始化节点之前,进行节点的封装以及数据请求的处理。例: contractCompactInitImpl -合同数据初始化节点

/**
 * @author xbhog
 * @describe: 合同数据请求、节点的实例化及方法执行
 * @date 2023/7/11
 */

public classContractCall<TextendsContractRequestimplementsCall<TContractResponse{
privatefinal T originalRequest;
privatefinal List<Interceptor<T,ContractRequest>> interceptorList;
publicContractCall(T originalRequest, List<Interceptor<T, ContractRequest>> interceptorList){
this.originalRequest = originalRequest;
this.interceptorList = interceptorList;
}
@Override
public T request(){
returnthis.originalRequest;
}
@Override
public ContractResponse exectue(){
//实例化流程节点
ContractChain<T> chain = new ContractChain(0,this.originalRequest,this.interceptorList);
return chain.proceed(this.originalRequest);
}
}

获取节点中的请求参数,实例化当前责任链节点( contractCompactInitImpl ),在执行节点中的proceed方法来获取当前节点的参数以及获取节点的信息。

/**
 * @author xbhog
 * @describe: 合同节点
 * @date 2023/7/11
 */

@Slf4j
public classContractChain<TextendsContractRequestimplementsChain<TContractResponse{
privatefinal Integer index;
privatefinal T request;
privatefinal List<Interceptor<T,ContractResponse>> interceptors;
publicContractChain(Integer index, T request, List<Interceptor<T, ContractResponse>> interceptors){
this.index = index;
this.request = request;
this.interceptors = interceptors;
}
@Override
public T request(){
returnthis.request;
}
@Override
public ContractResponse proceed(T request){
//控制节点流程
if(this.index >= this.interceptors.size()){
thrownew IllegalArgumentException("index越界");
}
//下一个节点参数设置
Chain<T,ContractResponse> nextChain = new ContractChain(this.index + 1, request, this.interceptors);
//获取节点信息
Interceptor<T, ContractResponse> interceptor = this.interceptors.get(this.index);
class<? extends Interceptor> a class = interceptor.get class();
log.info("当前节点:{}",a class.getSimpleName());
ContractResponse response = interceptor.process(nextChain);
if(Objects.isNull(response)){
thrownew NullPointerException("intercetor"+interceptor+"return null");
}
return response;
}
}



到此合同签章的架构流程已经确定,后续只要填充Interceptor具体的实现类即可。在代码中 ContractResponse response = interceptor.process(nextChain); 来执行合同初始化节点的具体操作。

/**
 * @author xbhog
 * @describe: 合同文本初始化
 * @date 2023/7/12
 */

@Slf4j
@Component
public classContractSignCompactInitImpl<TextendsContractRequestimplementsInterceptor<TContractResponse{
publicContractSignCompactInitImpl(){
}
@Override
public ContractResponse process(Chain<T,ContractResponse> chain){
log.info("=============执行合同文本初始化拦截器开始");
//获取处理的请求参数
T request = chain.request();
request.setStatus("1");
log.info("=============执行合同文本初始化拦截器结束");
//进入下一个责任链节点
ContractResponse response = chain.proceed(request);
if(Objects.isNull(response)){
log.error("返回值的为空");
response = ContractResponse.builder().status("fail").mas("处理失败").build();
}
//其他处理
return response;
}
}

测试验证

@SpringBootTest
classSPringBootTestApplicationTests{
@Autowired
@Qualifier("contractSignProcessor")
private Processor<ContractRequest,ContractResponse> contractSignProcessor;
@Test
voidcontextLoads(){
ContractRequest contractRequest = new ContractRequest();
contractRequest.setName("xbhog");
contractRequest.setAge("12");
ContractResponse process = contractSignProcessor.process(contractRequest);
System.out.println(process);
}
}

在这里只需要调用合同签章入口的方法即可进入合同签章的流程。

2023-07-1613:25:13.063 INFO 26892 --- [ main] c.e.s.c.ContractSignProcessor : 签章开始
2023-07-1613:25:13.067 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignCompactInitImpl
2023-07-1613:25:13.068 INFO 26892 --- [ main] c.e.s.c.i.ContractSignCompactInitImpl : =============执行合同文本初始化拦截器开始
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.i.ContractSignCompactInitImpl : =============执行合同文本初始化拦截器结束
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignGenerateImpl
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignGenerateImpl : =============执行合同文本生成拦截器开始
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignGenerateImpl : =============执行合同文本生成拦截器结束
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignMockImpl
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMockImpl : =============执行签章挡板拦截器开始
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMockImpl : =============执行签章挡板拦截器结束
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignMqImpl
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMqImpl : =============执行合同签章完成发送mq拦截器开始
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignSerialImpl
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSerialImpl : =============执行合同签章流水处理拦截器开始
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignSaveUploadImpl
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : =============执行合同签章完成上传服务器拦截器开始
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.chainresponsibility.ContractChain : 当前节点:ContractSignTradeImpl
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignTradeImpl : =============执行签章渠道实际调用拦截器开始
2023-07-1613:25:13.069 INFO 26892 --- [ main] c.e.s.c.channel.ContractSignChannelImpl : 签章处理开始
2023-07-1613:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : 开始上传服务器
2023-07-1613:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : .............
2023-07-1613:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : 上传服务器完成
2023-07-1613:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSaveUploadImpl : =============执行合同签章完成上传服务器拦截器结束
2023-07-1613:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignSerialImpl : =============执行合同签章流水处理拦截器结束
2023-07-1613:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMqImpl : 发送MQ给下游处理数据
2023-07-1613:25:13.070 INFO 26892 --- [ main] c.e.s.c.impl.ContractSignMqImpl : =============执行合同签章完成发送mq拦截器结束
ContractResponse(status=success, mas=处理成功)

策略+责任链+组合代码实现

以下是完整的合同签章入口实现类:

/**
 * @author xbhog
 * @describe: 责任链+组合实现合同签章
 * @date 2023/7/11
 */

@Slf4j
@Component
public classContractSignProcessor <TextendsContractRequestimplementsProcessor<TContractResponse{
@Resource(name = "contractSignCompactInitImpl")
private Interceptor<T,ContractResponse> contractCompactInitImpl;
@Resource(name = "contractSignGenerateImpl")
private Interceptor<T,ContractResponse> contractGenerateImpl;
@Resource(name = "contractSignMockImpl")
private Interceptor<T,ContractResponse> contractSignMockImpl;
@Resource(name = "contractSignMqImpl")
private Interceptor<T,ContractResponse> contractSignMqImpl;
@Resource(name = "contractSignSaveUploadImpl")
private Interceptor<T,ContractResponse> contractSignSaveUploadImpl;
@Resource(name = "contractSignSerialImpl")
private Interceptor<T,ContractResponse> contractSignSerialImpl;
@Resource(name = "contractSignTradeImpl")
private Interceptor<T,ContractResponse> ContractSignTradeImpl;

publicContractSignProcessor(){
}
@Override
public ContractResponse process(T paramter){
//获取所有的监听器
List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
interceptorList.add(contractCompactInitImpl);
interceptorList.add(contractGenerateImpl);
interceptorList.add(contractSignMockImpl);
interceptorList.add(contractSignMqImpl);
interceptorList.add(contractSignSerialImpl);
interceptorList.add(contractSignSaveUploadImpl);
interceptorList.add(ContractSignTradeImpl);
//开始签章
log.info("签章开始");
returnnew ContractCall(paramter,interceptorList).exectue();
}
}







可以看到,目前的合同签章的处理流程需要的节点数已经7个了,后续如果新增节点或者减少节点都需要对该类进行手动的处理;比如:减少一个节点的流程。

  • 删除节点实现的注入

  • 删除list中的bean实现类

  • 为方便后续的拓展(懒是社会进步的加速器,不是),在责任链,组合的基础上通过策略模式来修改bean的注入方式。完整的项目结构和项目类图就是作者文章开始放的,可返回查看。在第一部分的基础上增加的功能点如下

  • 新增签章注解

  • 新增签章节点枚举

  • 新增签章配置类

  • 签章注解实现

    package com.example.springboottest.chainresponsibility.annotations;
    import com.example.springboottest.chainresponsibility.Enum.ContractSignEnum;
    import java.lang.annotation.*;
    /**
     * @author xbhog
     * @describe:
     * @date 2023/7/15
     */

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public@interface ContractSign {
    ContractSignEnum.SignChannel SIGN_CHANNEL();
    }


    设置注解修饰对象的范围,主要是对bean的一个注入,所以类型选择type,

  • TYPE : 用于描述类、接口(包括注解类型) 或enum声明

  • 设置注解的运行周期(有效范围),一般是运行时有效,

  • RUNTIME :在运行时有效 (大部分注解的选择)

  • 设置该注解的数据类型,

  • ENUM :枚举类型,方便统一处理

  • 枚举实现

    package com.xbhog.chainresponsibility.Enum;
    /**
     * @author xbhog
     * @describe:
     * @date 2023/7/15
     */

    public classContractSignEnum{
    publicenum SignChannel {
    SIGN_INIT(1"合同文本初始化"),
    SIGN_GENERATE(2"合同文本生成"),
    SIGN_MOCK(3"签章挡板"),
    SIGN_MQ(4"合同签章完成发送MQ"),
    SIGN_TABLE(5"合同签章表处理"),
    SIGN_UPLOAD(6"合同签章完成上传服务器"),
    SIGN_TRADE(7"签章渠道实际调用");
    private Integer code;
    private String info;
    SignChannel(Integer code, String info) {
    this.code = code;
    this.info = info;
    }
    ......
    }
    }


    对合同签章中的流程节点进行统一的配置。

    签章配置类

    在项目启动的时候,通过注解工具类 AnnotationUtils 扫描所有被 ContractSign 注解修饰的类,将这些类通过Map进行存储,方便后续的调用。

    public classSignConfig{
    @Resource
    protected List<Interceptor> contractSignList;
    protectedstaticfinal Map<Integer,Interceptor> CONTRACT_SIGN_MAP = new ConcurrentHashMap<>();
    @PostConstruct
    publicvoidinit(){
    contractSignList.forEach(interceptor -> {
    //查找这个接口的实现类上有没有ContractSign注解
    ContractSign sign = AnnotationUtils.findAnnotation(interceptor.get class(), ContractSign. class);
    if(!Objects.isNull(sign)){
    CONTRACT_SIGN_MAP.put(sign.SIGN_CHANNEL().getCode(),interceptor);
    }
    });
    }
    }

    到此,简化了Bean的注入方式。

    签章注解使用

    以合同文本初始化 ContractSignCompactInitImpl 来说。

    /**
     * @author xbhog
     * @describe: 合同文本初始化
     * @date 2023/7/12
     */

    @Slf4j
    @ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT)
    @Component
    public classContractSignCompactInitImpl<TextendsContractRequestimplementsInterceptor<TContractResponse{
    publicContractSignCompactInitImpl(){
    }
    @Override
    public ContractResponse process(Chain<T,ContractResponse> chain){
    log.info("=============执行合同文本初始化拦截器开始");
    //获取处理的请求参数
    T request = chain.request();
    request.setStatus("1");
    log.info("=============执行合同文本初始化拦截器结束");
    //进入下一个责任链节点
    ContractResponse response = chain.proceed(request);
    if(Objects.isNull(response)){
    log.error("返回值的为空");
    response = ContractResponse.builder().status("fail").mas("处理失败").build();
    }
    //其他处理
    return response;
    }
    }

    在该实现类上绑定了枚举 @ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT)

    在合同签章入口类( ContractSignProcessor )中的变更如下:

    @Slf4j
    @Component
    public classContractSignProcessor <TextendsContractRequestextendsSignConfigimplementsProcessor<TContractResponse{
    publicContractSignProcessor(){
    }
    @Override
    public ContractResponse process(T paramter){
    //获取所有的监听器
    List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
    //获取排序后的结果,保证责任链的顺序,hashmap中key如果是数字的话,通过hashcode编码后是有序的
    for(Integer key : CONTRACT_SIGN_MAP.keySet()){
    interceptorList.add(CONTRACT_SIGN_MAP.get(key));
    }
    //开始签章
    log.info("签章开始");
    returnnew ContractCall(paramter,interceptorList).exectue();
    }
    }

    通过继承合同签章配置类(SignConfig),来获取Map,遍历Map添加到list后进入责任链流程。到此,整个策略+责任链+组合的优化方式结束了。

    END

    PS:防止找不到本篇文章,可以收藏点赞,方便翻阅查找哦。

    往期推荐