UNPKG

@flowlab/all

Version:

A cool library focusing on handling various flows

336 lines (335 loc) 16.8 kB
"use strict"; 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();