我的工作流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)可能需要您自行付费。
相关工作流推荐
n8n_3_CAD-BIM-批量转换器-管道
批量转换 CAD/BIM 文件为 XLSX/DAE,带验证和报告
If
Set
Code
+14
82 节点Artem Boiko
文档提取
n8n_7_Revit和IFC碳足迹CO2估算器
使用AI分类计算Revit/IFC模型的隐含碳(CO2)
If
Set
Code
+13
55 节点Artem Boiko
AI 摘要总结
n8n_6_使用LLM为Revit和IFC进行施工造价估算
使用GPT-4和Claude基于Revit/IFC模型估算施工成本
If
Set
Code
+13
55 节点Artem Boiko
AI 摘要总结
CAD-BIM 工程量提取 HTML 报告生成器
从Revit模型生成交互式墙体工程量HTML报告
If
Set
Function
+6
18 节点Artem
工程
n8n_8_Revit_IFC_DWG转换_提取阶段_解析XLSX
提取并解析Revit模型数据至结构化Excel格式
If
Set
Manual Trigger
+4
13 节点Artem Boiko
工程
n8n_1_Revit_IFC_DWG_转换_简化版
使用DataDrivenConstruction将CAD/BIM文件转换为Excel和3D模型
Set
Manual Trigger
Execute Command
+1
5 节点Artem Boiko
工程