管线执行方法实现

67 阅读4分钟

管线执行方法实现

在之前公司开发铺货中台的时候,有时候需要通过多种方式进行商品属性的匹配,为了避免过多的执行,就写了一个管线执行方法,它会在有任意一个匹配成功时进行返回,并且避免继续执行下去。

虽然好像也可以直接通过写一个线性的代码来进行执行,但是这样似乎更加优雅一点,哈哈哈。

类方法的实现

/**
 * @class 管线执行方法
 * @description 这个与验证数据管线执行不同,这里是在传入一堆后续方法,只要有一个方法成功了就进行返回,如果全部方法都没有成功,在返回失败
 */
export class ExecutionPipeline<T, P> {
  /** 传递数据内容 */
  data: T;
  /** 执行方法集合 */
  executionFunctions: ExecutionFuncion<T, P>[];
  /** 成功执行回调 */
  successCallback: (...args) => void;
  /** 失败执行回调 */
  errorCallback: (...args) => void;
  constructor(data: T) {
    this.data = data;
    this.executionFunctions = [];
    this.successCallback = null;
    this.errorCallback = null;
  }
  /**
   * @description 添加执行方法
   */
  addExecutionFunction(
    executionFunction: ExecutionFuncion<T, P>
  ): ExecutionPipeline<T, P> {
    this.executionFunctions.push(executionFunction);
    return this;
  }
  /**
   * @description 成功时的回调
   */
  onSuccess(callback: (...args) => void): ExecutionPipeline<T, P> {
    this.successCallback = callback;
    return this;
  }

  /**
   * @description 失败时的回调
   */
  onFailure(callback: (...args) => void): ExecutionPipeline<T, P> {
    this.errorCallback = callback;
    return this;
  }

  /**
   * @description 执行函数
   */
  async execute(): Promise<void> {
    // NOTE: 执行管道函数,成功就返回
    for (const executionFunction of this.executionFunctions) {
      const result: ExecutionResult<P> = await executionFunction(this.data);
      if (result.status === "success") {
        this.successCallback(result.callBackData);
        return;
      }
    }
    // NOTE: 没有成功,返回失败回调
    this.errorCallback();
  }
}

export type ExecutionFuncion<T, P> = (
  data: T
) => Promise<ExecutionResult<P>> | ExecutionResult<P>;

export type ExecutionResult<P> = {
  status: "success" | "error";
  callBackData?: P;
};

这里直接把代码贴出来,方便大家观看,同时进行一些解释。

作为管线,肯定可以知道,放进来的可执行函数是要收入到一个集合中的,然后再有执行函数统一调度执行,然后在一条一条执行,直到有一个成功了,就调用成功的回调,如果没有成功的内容就调用失败的回调。

这里似乎在进行 Promise 某些函数实现时是一致的。

但是这里做了一点改变就是只要有一个成功就会进行返回,并且避免接下来的内容执行,减少资源的消耗。

这种管线处理的方法,适合的场景:

  1. 当有需要进行多种匹配关系的时候可以进行使用,比如商品属性匹配、商品类目匹配,一开始会在上游商品详情中进行查找,查找不到时,则要进入数据库中找已经设置过自定义属性的内容。
  2. 进行降级处理的时候,就非常适合使用这种一成功就返回的管线操作。

管线方法的使用

/**
 * @description 匹配上游商品属性与商品属性规则
 */
protected async matchProductForRule (attributeRuleItem: DyPropertyInfo, isRequired: boolean) {
    let returnData = { property_id: 0, modifyReturn: null };
    // NOTE: 创建处理链路
    const executionPipeline = new ExecutionPipeline<{ attributeRuleItem: DyPropertyInfo, productAttribute: ProductAttribute[], isRequired: boolean }, ExecutionResult<any>>({ attributeRuleItem, productAttribute: this.upstreamProductAttributeList, isRequired });
    const { property_id } = attributeRuleItem;
    // NOTE: 开始处理链路
    executionPipeline.addExecutionFunction(async (matchData): Promise<any> => {
        const { attributeRuleItem, productAttribute, isRequired } = matchData;
        // this.logger.info('抖店铺货----商品属性处理----商品属性参数----第一步处理: accurateTextMatching');
        const matchResult = await this.accurateTextMatching(attributeRuleItem, productAttribute, isRequired);
        // this.logger.info(`抖店铺货----商品属性处理----商品属性参数----查看第一步的返回: ${JSON.stringify(matchResult)}`);
        return matchResult;
    }).addExecutionFunction(async (matchData): Promise<any> => {
        const { attributeRuleItem, productAttribute, isRequired } = matchData;
        // this.logger.info('抖店铺货----商品属性处理----商品属性参数----第二步处理: databaseMatching');
        const matchResult = await this.databaseMatching(attributeRuleItem, productAttribute, isRequired);
        // this.logger.info(`抖店铺货----商品属性处理----商品属性参数----第二步处理返回: ${JSON.stringify(matchResult)}`);
        return matchResult;
    })
        .addExecutionFunction(async (matchData): Promise<any> => {
            const { attributeRuleItem, productAttribute, isRequired } = matchData;
            const matchResult = await this.specialAttributeHandle(attributeRuleItem, productAttribute, isRequired);
            return matchResult;
        })
        .onSuccess((data) => {
            this.logger.info(`抖店铺货----商品属性处理----商品属性参数----成功的回调: ${JSON.stringify(data)}`);
            returnData = { property_id, modifyReturn: data };
        })
        .onFailure(() => {
            if (attributeRuleItem.diy_type === IS_DIY_TYPE && attributeRuleItem.required) {
                returnData = { property_id, modifyReturn: { diy_type: IS_DIY_TYPE, name: '默认', value: DIY_ATTRIBUTE_VALUE } };
                return;
            }
            this.logger.error('抖店铺货----商品属性处理----商品属性参数----匹配失败');
            returnData = { property_id, modifyReturn: null };
        });
    // NOTE: 管道执行
    await executionPipeline.execute();
    // NOTE: 获取数据
    return returnData;
}

上面就是一个商品属性匹配的一个例子。

我们再把1688商品上架到抖店时,需要将抖店商品在该类目下需要填写的商品属性进行完善。我们就可以通过管线处理的方式进行处理,如上面这段代码:

  1. 首先在1688商品属性中,查找是否具有匹配的属性
  2. 在数据库中查找当前抖店属性是否存在默认的属性值
  3. 在1688商品标题中进行查找,查看是否有匹配抖店要求的商品属性值

通过上述的三种处理,来匹配商品属性,如果没有找到的话,就是商品属性匹配失败;如果成功的话,就填入抖店要求的商品属性即可

总结

一个简单的管线处理的封装,可以让项目在开发时拥有更好的开发体验,可以更加快速的解决对应的场景下的问题,同时让代码更加的优雅,希望大家在开发时可以多多封装相关的工具类,来提升自己的代码水平。

千万不能因为天天在干活而放弃提升自己的编码水平,加油加油