@flowlab/all
Version:
A cool library focusing on handling various flows
336 lines (335 loc) • 16.8 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var src_1 = require("../main/src"); // 假设库代码在此
// ---------- 定义各个操作对应的节点 ----------
// 节点 1: 获取用户信息 (继承 BaseNode 以便封装重试逻辑,虽然也可以在步骤配置里加)
var FetchUserNode = /** @class */ (function (_super) {
__extends(FetchUserNode, _super);
function FetchUserNode() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.metadata = { id: 'fetch-user', description: '获取用户信息' };
return _this;
}
FetchUserNode.prototype.execute = function (context) {
return __awaiter(this, void 0, void 0, function () {
var userId;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
userId = context.input.userId;
context.log("\u83B7\u53D6\u7528\u6237 ".concat(userId, " ..."));
// 实际应调用 fetchUserService,这里直接模拟
return [4 /*yield*/, new Promise(function (res) { return setTimeout(res, 50); })];
case 1:
// 实际应调用 fetchUserService,这里直接模拟
_a.sent();
if (Math.random() < 0.1 && context.retries === 0) { // 模拟第一次可能失败
throw new Error('用户服务暂时不可用 (模拟)');
}
context.output = { userId: userId, email: "".concat(userId, "@test.com"), vipLevel: Math.random() > 0.5 ? 'VIP' : 'Normal' };
return [2 /*return*/];
}
});
});
};
return FetchUserNode;
}(src_1.BaseNode));
// 节点 2: 获取商品信息 (简单函数节点)
function fetchProductNodeFunc(context) {
return __awaiter(this, void 0, void 0, function () {
var productId, stock;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
productId = context.input.productId;
context.log("\u83B7\u53D6\u5546\u54C1 ".concat(productId, " ..."));
// 实际应调用 fetchProductService
return [4 /*yield*/, new Promise(function (res) { return setTimeout(res, 60); })];
case 1:
// 实际应调用 fetchProductService
_a.sent();
if (Math.random() < 0.1 && context.retries === 0) { // 模拟第一次可能失败
throw new Error('商品服务暂时不可用 (模拟)');
}
stock = Math.floor(Math.random() * 5);
context.output = { productId: productId, price: 100, stock: stock };
return [2 /*return*/];
}
});
});
}
// 节点 3: 计算价格 (简单函数节点)
function calculatePriceNodeFunc(context) {
return __awaiter(this, void 0, void 0, function () {
var price, vipLevel, finalPrice;
return __generator(this, function (_a) {
price = context.input.price;
vipLevel = context.input.vipLevel;
finalPrice = price;
if (vipLevel === 'VIP') {
finalPrice = price * 0.9;
context.log("VIP \u7528\u6237\uFF0C\u5E94\u7528\u6298\u6263\u3002\u6700\u7EC8\u4EF7\u683C: ".concat(finalPrice));
}
else {
context.log("\u666E\u901A\u7528\u6237\u3002\u6700\u7EC8\u4EF7\u683C: ".concat(finalPrice));
}
context.output = { finalPrice: finalPrice }; // 设置输出
return [2 /*return*/];
});
});
}
// 节点 4: 创建订单 (简单函数节点)
function createOrderNodeFunc(context) {
return __awaiter(this, void 0, void 0, function () {
var _a, userId, productId, price;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = context.input, userId = _a.userId, productId = _a.productId, price = _a.price;
context.log("\u521B\u5EFA\u8BA2\u5355: \u7528\u6237 ".concat(userId, ", \u5546\u54C1 ").concat(productId, ", \u4EF7\u683C ").concat(price));
// 实际应调用 createOrderService
return [4 /*yield*/, new Promise(function (res) { return setTimeout(res, 70); })];
case 1:
// 实际应调用 createOrderService
_b.sent();
if (Math.random() < 0.05) {
throw new Error('订单服务创建失败 (模拟)');
}
context.output = { orderId: "order-".concat(Date.now()) };
return [2 /*return*/];
}
});
});
}
// 节点 5: 发送成功通知 (简单函数节点)
function sendSuccessEmailNodeFunc(context) {
return __awaiter(this, void 0, void 0, function () {
var _a, email, orderId, productId, finalPrice, subject, body;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = context.input, email = _a.email, orderId = _a.orderId, productId = _a.productId, finalPrice = _a.finalPrice;
subject = '订单创建成功';
body = "\u60A8\u7684\u8BA2\u5355 ".concat(orderId, " \u5DF2\u6210\u529F\u521B\u5EFA\uFF0C\u5546\u54C1 ").concat(productId, "\uFF0C\u4EF7\u683C ").concat(finalPrice, "\u3002");
context.log("\u53D1\u9001\u6210\u529F\u90AE\u4EF6\u7ED9 ".concat(email));
// 实际应调用 sendEmailService
return [4 /*yield*/, new Promise(function (res) { return setTimeout(res, 40); })];
case 1:
// 实际应调用 sendEmailService
_b.sent();
return [2 /*return*/];
}
});
});
}
// 节点 6: 发送失败通知 (简单函数节点)
function sendFailureEmailNodeFunc(context) {
return __awaiter(this, void 0, void 0, function () {
var _a, email, productId, reason, subject, body;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = context.input, email = _a.email, productId = _a.productId, reason = _a.reason;
subject = '订单创建失败';
body = "\u62B1\u6B49\uFF0C\u60A8\u7684\u8BA2\u5355 (\u5546\u54C1 ".concat(productId, ") \u521B\u5EFA\u5931\u8D25\u3002\u539F\u56E0: ").concat(reason || '未知错误');
context.log("\u53D1\u9001\u5931\u8D25\u90AE\u4EF6\u7ED9 ".concat(email));
// 实际应调用 sendEmailService
return [4 /*yield*/, new Promise(function (res) { return setTimeout(res, 40); })];
case 1:
// 实际应调用 sendEmailService
_b.sent();
return [2 /*return*/];
}
});
});
}
// 节点 7: 记录库存不足 (简单函数节点)
function logStockIssueNodeFunc(context) {
return __awaiter(this, void 0, void 0, function () {
var productId, stock, message;
return __generator(this, function (_a) {
productId = context.input.productId;
stock = context.input.stock;
message = "\u5546\u54C1 ".concat(productId, " \u5E93\u5B58\u4E0D\u8DB3 (").concat(stock, ")\uFF0C\u8BA2\u5355\u65E0\u6CD5\u521B\u5EFA\u3002");
context.log(message);
// 可以将原因设置到变量,用于后续的失败通知
context.setVariable('failureReason', message);
return [2 /*return*/];
});
});
}
// ---------- 初始化 FlowLab 引擎并注册节点 ----------
var engine = new src_1.FlowLabEngine();
engine.registerNode(new FetchUserNode()); // 注册类节点
engine.registerNode('fetch-product', fetchProductNodeFunc, '获取商品信息'); // 注册函数节点
engine.registerNode('calculate-price', calculatePriceNodeFunc, '计算最终价格');
engine.registerNode('create-order', createOrderNodeFunc, '调用订单服务创建订单');
engine.registerNode('send-success-email', sendSuccessEmailNodeFunc, '发送成功通知邮件');
engine.registerNode('send-failure-email', sendFailureEmailNodeFunc, '发送失败通知邮件');
engine.registerNode('log-stock-issue', logStockIssueNodeFunc, '记录库存不足信息');
// ---------- 定义工作流 ----------
var orderWorkflow = engine.defineWorkflow('order-processing', '订单处理与通知流程')
.addParallel({
id: 'fetch-data',
parallelSteps: [
{
id: 'fetch-user-step', // 并行分支内的步骤也需要 ID (虽然通常不直接引用)
type: src_1.StepType.TASK, // 需要显式指定类型
nodeId: 'fetch-user',
inputMapping: { 'userId': 'input.userId' },
outputMapping: { 'variables.userInfo': 'output' }, // 将整个用户信息存入变量
retryOptions: { maxRetries: 2, delayMs: 100 } // 在步骤级别配置重试
},
{
id: 'fetch-product-step',
type: src_1.StepType.TASK,
nodeId: 'fetch-product',
inputMapping: { 'productId': 'input.productId' },
outputMapping: { 'variables.productInfo': 'output' }, // 将整个商品信息存入变量
retryOptions: { maxRetries: 2, delayMs: 100 } // 配置重试
}
],
nextStepId: 'check-stock' // 所有并行步骤成功后,进入库存检查
})
.addCondition({
id: 'check-stock',
condition: function (context) { var _a, _b; return ((_b = (_a = context.variables.productInfo) === null || _a === void 0 ? void 0 : _a.stock) !== null && _b !== void 0 ? _b : 0) > 0; }, // 检查变量中的库存
branches: {
'true': 'calculate-price', // 库存充足,去计算价格
'false': 'log-stock-issue' // 库存不足,记录问题
}
})
.addStep({
id: 'log-stock-issue',
nodeId: 'log-stock-issue',
inputMapping: {
'productId': 'variables.productInfo.productId',
'stock': 'variables.productInfo.stock',
},
nextStepId: 'notify-failure' // 记录后直接去发送失败通知
})
.addStep({
id: 'calculate-price',
nodeId: 'calculate-price',
inputMapping: {
'price': 'variables.productInfo.price',
'vipLevel': 'variables.userInfo.vipLevel',
},
outputMapping: {
'variables.finalPrice': 'output.finalPrice'
},
nextStepId: 'create-order' // 下一步创建订单
})
.addStep({
id: 'create-order',
nodeId: 'create-order',
inputMapping: {
'userId': 'variables.userInfo.userId',
'productId': 'variables.productInfo.productId',
'price': 'variables.finalPrice'
},
outputMapping: {
'variables.orderId': 'output.orderId'
},
retryOptions: { maxRetries: 1, delayMs: 200 }, // 配置重试
nextStepId: 'notify-success' // 成功则发送成功通知
// 注意:如果此步骤失败,默认会使整个工作流失败 (除非配置了错误处理分支或补偿)
})
.addStep({
id: 'notify-success',
nodeId: 'send-success-email',
inputMapping: {
'email': 'variables.userInfo.email',
'orderId': 'variables.orderId',
'productId': 'variables.productInfo.productId',
'finalPrice': 'variables.finalPrice'
}
// 流程结束
})
.addStep({
// 为了简化,我们假设从 log-stock-issue 直接跳过来
id: 'notify-failure',
nodeId: 'send-failure-email',
inputMapping: {
'email': 'variables.userInfo.email', // 假设 userInfo 总是能获取到
'productId': 'input.productId', // 从初始输入获取
'reason': 'variables.failureReason' // 从 log-stock-issue 设置的变量获取原因
}
// 流程结束
})
.setStartStep('fetch-data'); // 设置起始步骤
// (可选) 注册定义,以便按 ID 执行
engine.registerDefinition(orderWorkflow);
// ---------- 执行工作流 ----------
function runFlowLabExample() {
return __awaiter(this, void 0, void 0, function () {
var executor, initialData, resultContext;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
executor = engine.createExecutor();
initialData = { userId: 'user1', productId: 'productA' };
console.log("\n--- \u4F7F\u7528 FlowLab \u6267\u884C\u8BA2\u5355\u5904\u7406: \u7528\u6237 ".concat(initialData.userId, ", \u5546\u54C1 ").concat(initialData.productId, " ---"));
return [4 /*yield*/, executor.run(orderWorkflow, initialData)];
case 1:
resultContext = _a.sent();
console.log("\n--- FlowLab \u5DE5\u4F5C\u6D41\u6267\u884C\u5B8C\u6BD5 ---");
console.log("\u6700\u7EC8\u72B6\u6001: ".concat(resultContext.status));
console.log("\u6700\u7EC8\u53D8\u91CF:", resultContext.variables);
return [2 /*return*/];
}
});
});
}
runFlowLabExample();