8
n8n 中文网amn8n.com

我的工作流11

高级

这是一个Engineering领域的自动化工作流,包含 26 个节点。主要使用 If, Set, Code, Merge, ManualTrigger 等节点。 根据Excel标准验证CAD-BIM文件(Revit/IFC/DWG/DGN)

前置要求
  • 无特殊前置要求,导入即可使用

分类

工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "t92A5nG34BQ1ETsq",
  "meta": {
    "instanceId": "faa70e11b7175129a74fd834d3451fdc1862589b16d68ded03f91ca7b1ecca12"
  },
  "name": "我的工作流 11",
  "tags": [],
  "nodes": [
    {
      "id": "92efb3dc-aaf3-42c9-ac5e-c1cd237335dc",
      "name": "开始 - 点击开始",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -2300,
        80
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "95816564-1418-4634-8d43-b501929cb49a",
      "name": "设置 - 定义文件路径",
      "type": "n8n-nodes-base.set",
      "position": [
        -2060,
        80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9cbd4ec9-df24-41e8-b47a-720a4cdb733b",
              "name": "path_to_converter",
              "type": "string",
              "value": "C:\\Users\\Artem Boiko\\Desktop\\n8n pipelines\\DDC_Converter_Revit\\datadrivenlibs\\RvtExporter.exe"
            },
            {
              "id": "aa834467-80fb-476a-bac1-6728478834f0",
              "name": "project_file",
              "type": "string",
              "value": "C:\\Users\\Artem Boiko\\Desktop\\n8n pipelines\\Validation\\2023 racbasicsampleproject.rvt"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "71fb07b2-0664-4799-8a4e-64abbf952261",
      "name": "便签 - 开始",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2340,
        -180
      ],
      "parameters": {
        "color": 4,
        "width": 190,
        "height": 540,
        "content": "## 🚀 启动工作流"
      },
      "typeVersion": 1
    },
    {
      "id": "f86c21fe-e5db-4c06-b2bb-681a20041443",
      "name": "便签 - 设置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2120,
        -300
      ],
      "parameters": {
        "color": 4,
        "width": 220,
        "height": 660,
        "content": "## ⚙️ 设置"
      },
      "typeVersion": 1
    },
    {
      "id": "21fa1ff2-e1c1-4aa6-9891-936edbeee737",
      "name": "读取项目 Excel 文件",
      "type": "n8n-nodes-base.readBinaryFile",
      "position": [
        -480,
        220
      ],
      "parameters": {
        "filePath": "={{ $json[\"xlsx_filename\"] }}"
      },
      "typeVersion": 1
    },
    {
      "id": "1ac7b052-ff92-4b7e-a8fd-8ef736a08199",
      "name": "设置验证规则路径",
      "type": "n8n-nodes-base.set",
      "position": [
        -480,
        20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "validation-rules-path-id",
              "name": "validation_rules_path",
              "type": "string",
              "value": "C:\\Users\\Artem Boiko\\Desktop\\n8n pipelines\\Validation\\n8n_3_DDC_BIM_Requirements_Table_for_Revit_IFC_DWG.xlsx"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "59dcf928-8e10-4747-beb0-de38b0b10426",
      "name": "便签 - 验证规则",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -540,
        -300
      ],
      "parameters": {
        "color": 4,
        "width": 220,
        "height": 680,
        "content": "## 📋 验证规则"
      },
      "typeVersion": 1
    },
    {
      "id": "c844345a-6634-4305-85e9-ee898eb11fad",
      "name": "读取验证规则文件",
      "type": "n8n-nodes-base.readBinaryFile",
      "position": [
        -240,
        100
      ],
      "parameters": {
        "filePath": "={{ $json[\"validation_rules_path\"] }}"
      },
      "typeVersion": 1
    },
    {
      "id": "33182b90-0463-4d4f-ae56-06b168413f74",
      "name": "合并 Excel 文件",
      "type": "n8n-nodes-base.merge",
      "position": [
        -20,
        240
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "50aac1fe-7aa0-450f-910a-922e41b3cfe1",
      "name": "验证 - 增强版",
      "type": "n8n-nodes-base.code",
      "position": [
        160,
        100
      ],
      "parameters": {
        "language": "python",
        "pythonCode": "import micropip\nawait micropip.install(\"openpyxl\")\nfrom io import BytesIO\nimport pandas as pd\nimport datetime\nimport base64\nimport os\nfrom openpyxl import load_workbook\nfrom openpyxl.styles import Alignment, PatternFill\n\n# Get the uploaded Excel files\ninput_data = _input.all()\n\n# Get project Excel data (from Read Project Excel File node)\nproject_binary = input_data[0][\"binary\"]\nproject_b64_excel = project_binary[\"data\"][\"data\"]\nproject_excel_bytes = BytesIO(base64.b64decode(project_b64_excel))\n\n# Get validation rules Excel data (from Read Validation Rules File node)\nvalidation_binary = input_data[1][\"binary\"]\nvalidation_b64_excel = validation_binary[\"data\"][\"data\"]\nvalidation_excel_bytes = BytesIO(base64.b64decode(validation_b64_excel))\n\n# Get the validation rules path from the previous node to determine output directory\nvalidation_rules_path = input_data[1][\"json\"][\"validation_rules_path\"]\noutput_directory = os.path.dirname(validation_rules_path)\nprint(f\"Output directory: {output_directory}\")\n\n# Load project data\ndf = pd.read_excel(project_excel_bytes)\nprint(f\"Loaded {len(df)} rows from project file\")\n\n# Handle duplicate columns\nduplicate_cols = df.columns[df.columns.duplicated(keep=False)].unique().tolist()\nif duplicate_cols:\n    print(f\"WARNING: Duplicate columns found: {duplicate_cols}\")\n\n# Clean column names\ncolumn_mapping = {col: col.split(' : ')[0] if ':' in col else col for col in df.columns}\ncleaned_columns = list(column_mapping.values())\n\n# Load and prepare workbook\nwb = load_workbook(validation_excel_bytes)\nws = wb.active\n\n# Unmerge cells in columns D-G\nfor mr in list(ws.merged_cells.ranges):\n    if any(col in range(mr.min_col, mr.max_col + 1) for col in [4, 5, 6, 7]):\n        print(f\"Unmerging: {mr}\")\n        ws.unmerge_cells(str(mr))\n\n# Get category column\ncategory_col = ws['B2'].value\nprint(f\"Category column: {category_col}\")\n\n# Find category column in data\nif category_col not in df.columns:\n    for orig, cleaned in column_mapping.items():\n        if cleaned == category_col:\n            category_col = orig\n            break\n    if category_col not in df.columns:\n        raise ValueError(f\"Category column '{category_col}' not found\")\n\nprint(f\"Data type: {df[category_col].dtype}, Unique values: {df[category_col].nunique()}\")\n\n# Read validation rules from the Excel\nvalidation_excel_bytes.seek(0)  # Reset the stream\ndf_excel = pd.read_excel(validation_excel_bytes)\nvalid_mask = df_excel.iloc[1:, 1].notna() & df_excel.iloc[1:, 2].notna()\nvalidation_data = pd.DataFrame({\n    'row': df_excel.index[1:][valid_mask] + 2,\n    'group': df_excel.iloc[1:, 1][valid_mask].values,\n    'param': df_excel.iloc[1:, 2][valid_mask].values\n})\n\n# Define fills\nred_fill = PatternFill(start_color='FFCCCC', end_color='FFCCCC', fill_type='solid')\ngreen_fill = PatternFill(start_color='CCFFCC', end_color='CCFFCC', fill_type='solid')\n\nprint(f\"Processing {len(validation_data)} rules...\")\n\n# Helper function\ndef get_best_column(df, param, group_df):\n    matches = [i for i, col in enumerate(df.columns) if cleaned_columns[i] == param]\n    \n    if not matches:\n        return param if param in df.columns else None\n    \n    if len(matches) == 1:\n        return df.columns[matches[0]]\n    \n    print(f\"  Found {len(matches)} columns named '{param}'\")\n    best_idx, best_count = matches[0], -1\n    \n    for idx in matches:\n        col = df.columns[idx]\n        if col in group_df.columns:\n            count = group_df[col].notna().sum()\n            print(f\"    Column {idx}: {count} values\")\n            if count > best_count:\n                best_idx, best_count = idx, count\n    \n    return df.columns[best_idx]\n\n# Process validation rules\nfor _, row in validation_data.iterrows():\n    r, grp, prm = row['row'], str(row['group']).strip(), str(row['param']).strip()\n    \n    try:\n        # Filter by category\n        cat_df = df[df[category_col] == grp]\n        \n        # Try alternative matching if no exact match\n        if len(cat_df) == 0 and ':' in grp:\n            cat_df = df[df[category_col].astype(str).str.contains(grp, na=False, regex=False)]\n        \n        total = len(cat_df)\n        \n        # Get column and calculate metrics\n        col = get_best_column(df, prm, cat_df)\n        \n        if col and col in cat_df.columns:\n            valid_df = cat_df[cat_df[col].notna() & ~cat_df[col].isin([0, 0.0, \"0\", \"0.0\", \"0,0\"])]\n            filled = len(valid_df)\n            fill_pct = filled / total if total > 0 else 0\n            unique_cnt = valid_df[col].nunique()\n            unique_vals = valid_df[col].unique()[:10]\n            unique_str = \", \".join(map(str, unique_vals))\n            if len(unique_vals) > 10:\n                unique_str += f\"... ({valid_df[col].nunique()} total)\"\n        else:\n            filled = fill_pct = unique_cnt = 0\n            unique_str = \"\"\n        \n        # Write to Excel\n        ws[f'D{r}'] = total\n        ws[f'D{r}'].alignment = Alignment(horizontal='center')\n        \n        ws[f'E{r}'].value = fill_pct\n        ws[f'E{r}'].number_format = '0.00%'\n        ws[f'E{r}'].alignment = Alignment(horizontal='center')\n        ws[f'E{r}'].fill = green_fill if fill_pct > 0 else red_fill\n        \n        ws[f'F{r}'] = unique_cnt\n        ws[f'F{r}'].alignment = Alignment(horizontal='center')\n        \n        ws[f'G{r}'] = unique_str\n        ws[f'G{r}'].alignment = Alignment(horizontal='left')\n        \n    except Exception as e:\n        print(f\"  ERROR: {type(e).__name__}: {str(e)}\")\n        ws[f'D{r}'] = 0\n        ws[f'E{r}'].value = 0\n        ws[f'E{r}'].fill = red_fill\n        ws[f'F{r}'] = 0\n        ws[f'G{r}'] = f\"ERROR: {str(e)}\"\n\n# Save to bytes & return to n8n\nwith BytesIO() as out_stream:\n    wb.save(out_stream)\n    out_stream.seek(0)\n    result_b64 = base64.b64encode(out_stream.read()).decode()\n\noutput_timestamped = f\"DDC_Revit_IFC_Validation__{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx\"\n\n# Use the same directory as the validation rules file\nfull_output_path = os.path.join(output_directory, output_timestamped)\n\nreturn [{\n    \"json\": {\n        \"filename\": output_timestamped,\n        \"full_path\": full_output_path\n    },\n    \"binary\": {\n        \"data\": {\n            \"data\":          result_b64,\n            \"fileName\":      output_timestamped,\n            \"fileExtension\": \"xlsx\",\n            \"mimeType\":      \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"\n        }\n    }\n}]\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4925aa8f-4590-4280-83fe-cd8188baa47d",
      "name": "便签 - 验证",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -300,
        -180
      ],
      "parameters": {
        "color": 2,
        "width": 620,
        "height": 660,
        "content": "## 🤖 自动化验证"
      },
      "typeVersion": 1
    },
    {
      "id": "6df2ce0a-6e99-4cc9-860c-73b5c6b6f73b",
      "name": "便签 - 转换",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1880,
        -180
      ],
      "parameters": {
        "width": 1320,
        "height": 660,
        "content": "## 🔄 转换"
      },
      "typeVersion": 1
    },
    {
      "id": "c0cd20fa-8e9b-4b13-b989-ab9f35cbf6f0",
      "name": "便签 - 保存和打开",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        340,
        -180
      ],
      "parameters": {
        "color": 6,
        "width": 420,
        "height": 660,
        "content": "## 📂 保存和打开"
      },
      "typeVersion": 1
    },
    {
      "id": "78b9973e-a1b8-45c6-87c6-987e8a520533",
      "name": "保存验证报告1",
      "type": "n8n-nodes-base.writeBinaryFile",
      "position": [
        420,
        100
      ],
      "parameters": {
        "options": {},
        "fileName": "={{ $json.full_path }}"
      },
      "typeVersion": 1
    },
    {
      "id": "52e44f10-633e-4f2b-9b02-7f279bc56efa",
      "name": "打开 Excel 报告1",
      "type": "n8n-nodes-base.executeCommand",
      "position": [
        600,
        100
      ],
      "parameters": {
        "command": "=start \"\" \"{{ $json.full_path }}\""
      },
      "typeVersion": 1
    },
    {
      "id": "f10e9818-6d49-43f0-8174-227cb50714e4",
      "name": "创建 - Excel 文件名",
      "type": "n8n-nodes-base.set",
      "position": [
        -1820,
        80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "xlsx-filename-id",
              "name": "xlsx_filename",
              "type": "string",
              "value": "={{ $json[\"project_file\"].slice(0, -4) + \"_rvt.xlsx\" }}"
            },
            {
              "id": "path-to-converter-pass",
              "name": "path_to_converter",
              "type": "string",
              "value": "={{ $json[\"path_to_converter\"] }}"
            },
            {
              "id": "project-file-pass",
              "name": "project_file",
              "type": "string",
              "value": "={{ $json[\"project_file\"] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8b0c05f8-8eee-4096-9160-9f5db87bfff6",
      "name": "检查 - Excel 文件是否存在?",
      "type": "n8n-nodes-base.readBinaryFile",
      "position": [
        -1640,
        80
      ],
      "parameters": {
        "filePath": "={{ $json[\"xlsx_filename\"] }}"
      },
      "typeVersion": 1,
      "continueOnFail": true,
      "alwaysOutputData": true
    },
    {
      "id": "b8593fdb-f7b7-4c75-9e6d-59c03ff45d3c",
      "name": "如果 - 文件存在?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1480,
        80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e7fb1577-e753-43f5-9f5a-4d5285aeb96e",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $binary.data ? true : false }}",
              "rightValue": "={{ true }}"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6c3d3da3-639a-4c5d-9c08-43f2054ef577",
      "name": "提取 - 运行转换器",
      "type": "n8n-nodes-base.executeCommand",
      "position": [
        -1280,
        200
      ],
      "parameters": {
        "command": "=\"{{$node[\"Setup - Define file paths\"].json[\"path_to_converter\"]}}\" \"{{$node[\"Setup - Define file paths\"].json[\"project_file\"]}}\""
      },
      "typeVersion": 1,
      "continueOnFail": true
    },
    {
      "id": "97156ad5-e987-401b-8bbb-360a0b5da7b6",
      "name": "信息 - 跳过转换",
      "type": "n8n-nodes-base.set",
      "position": [
        -1200,
        -20
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "status-id",
              "name": "status",
              "type": "string",
              "value": "File already exists - skipping conversion"
            },
            {
              "id": "xlsx-filename-id",
              "name": "xlsx_filename",
              "type": "string",
              "value": "={{ $node[\"Create - Excel filename\"].json[\"xlsx_filename\"] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d618645d-acf6-4dca-8ea6-ba17b573ffb2",
      "name": "检查 - 提取是否成功?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1120,
        200
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition1",
              "operator": {
                "type": "object",
                "operation": "exists",
                "rightType": "any"
              },
              "leftValue": "={{ $node[\"Extract - Run converter\"].json.error }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "8059bfbc-0fb0-4894-8595-8aa8ae1f3d22",
      "name": "错误 - 显示问题所在",
      "type": "n8n-nodes-base.set",
      "position": [
        -940,
        100
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "error-message-id",
              "name": "error_message",
              "type": "string",
              "value": "=Extraction failed: {{ $node[\"Extract - Run converter\"].json.error || \"Unknown error\" }}"
            },
            {
              "id": "error-code-id",
              "name": "error_code",
              "type": "number",
              "value": "={{ $node[\"Extract - Run converter\"].json.code || -1 }}"
            },
            {
              "id": "xlsx-filename-error",
              "name": "xlsx_filename",
              "type": "string",
              "value": "={{ $node[\"Create - Excel filename\"].json[\"xlsx_filename\"] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "12da9c51-2e54-405c-9abb-339acfe37c10",
      "name": "成功后设置 xlsx_filename",
      "type": "n8n-nodes-base.set",
      "position": [
        -940,
        280
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "xlsx-filename-success",
              "name": "xlsx_filename",
              "type": "string",
              "value": "={{ $node[\"Create - Excel filename\"].json[\"xlsx_filename\"] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0607ae8e-453d-420a-ace9-f7c949449806",
      "name": "合并 - 继续工作流",
      "type": "n8n-nodes-base.merge",
      "position": [
        -700,
        20
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "e1bb7a42-ef83-48f5-a687-9efe58c3ef4e",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1880,
        -500
      ],
      "parameters": {
        "width": 540,
        "height": 300,
        "content": "## ⚠️ 故障排除"
      },
      "typeVersion": 1
    },
    {
      "id": "ebb5cc53-2c09-48f8-bdf4-6fafe0ccf866",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -300,
        -660
      ],
      "parameters": {
        "width": 540,
        "height": 460,
        "content": "## ⚠️ 故障排除"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "cac8701f-a570-4992-b139-33f47ce6894c",
  "connections": {
    "If - File exists?": {
      "main": [
        [
          {
            "node": "Info - Skip conversion",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Extract - Run converter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Excel Files": {
      "main": [
        [
          {
            "node": "Validate - Enhanced",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate - Enhanced": {
      "main": [
        [
          {
            "node": "Save Validation Report1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Info - Skip conversion": {
      "main": [
        [
          {
            "node": "Merge - Continue workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start - Click to begin": {
      "main": [
        [
          {
            "node": "Setup - Define file paths",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create - Excel filename": {
      "main": [
        [
          {
            "node": "Check - Does Excel file exist?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract - Run converter": {
      "main": [
        [
          {
            "node": "Check - Did extraction succeed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Project Excel File": {
      "main": [
        [
          {
            "node": "Merge Excel Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Validation Report1": {
      "main": [
        [
          {
            "node": "Open Excel Report1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge - Continue workflow": {
      "main": [
        [
          {
            "node": "Read Project Excel File",
            "type": "main",
            "index": 0
          },
          {
            "node": "Set Validation Rules Path",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Validation Rules Path": {
      "main": [
        [
          {
            "node": "Read Validation Rules File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Setup - Define file paths": {
      "main": [
        [
          {
            "node": "Create - Excel filename",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Validation Rules File": {
      "main": [
        [
          {
            "node": "Merge Excel Files",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Error - Show what went wrong": {
      "main": [
        [
          {
            "node": "Merge - Continue workflow",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Check - Does Excel file exist?": {
      "main": [
        [
          {
            "node": "If - File exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check - Did extraction succeed?": {
      "main": [
        [
          {
            "node": "Error - Show what went wrong",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set xlsx_filename after success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set xlsx_filename after success": {
      "main": [
        [
          {
            "node": "Merge - Continue workflow",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 工程

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量26
分类1
节点类型9
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

外部链接
在 n8n.io 查看

分享此工作流