管线执行方法实现
在之前公司开发铺货中台的时候,有时候需要通过多种方式进行商品属性的匹配,为了避免过多的执行,就写了一个管线执行方法,它会在有任意一个匹配成功时进行返回,并且避免继续执行下去。
虽然好像也可以直接通过写一个线性的代码来进行执行,但是这样似乎更加优雅一点,哈哈哈。
类方法的实现
/**
* @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 某些函数实现时是一致的。
但是这里做了一点改变就是只要有一个成功就会进行返回,并且避免接下来的内容执行,减少资源的消耗。
这种管线处理的方法,适合的场景:
- 当有需要进行多种匹配关系的时候可以进行使用,比如商品属性匹配、商品类目匹配,一开始会在上游商品详情中进行查找,查找不到时,则要进入数据库中找已经设置过自定义属性的内容。
- 进行降级处理的时候,就非常适合使用这种一成功就返回的管线操作。
管线方法的使用
/**
* @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商品上架到抖店时,需要将抖店商品在该类目下需要填写的商品属性进行完善。我们就可以通过管线处理的方式进行处理,如上面这段代码:
- 首先在1688商品属性中,查找是否具有匹配的属性
- 在数据库中查找当前抖店属性是否存在默认的属性值
- 在1688商品标题中进行查找,查看是否有匹配抖店要求的商品属性值
通过上述的三种处理,来匹配商品属性,如果没有找到的话,就是商品属性匹配失败;如果成功的话,就填入抖店要求的商品属性即可
总结
一个简单的管线处理的封装,可以让项目在开发时拥有更好的开发体验,可以更加快速的解决对应的场景下的问题,同时让代码更加的优雅,希望大家在开发时可以多多封装相关的工具类,来提升自己的代码水平。
千万不能因为天天在干活而放弃提升自己的编码水平,加油加油