{ "name": "Excel数据自动清洗与校验", "nodes": [ { "parameters": {}, "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ -240, -260 ], "id": "b3b60d87-0501-4e9c-9633-37f0a169ba42", "name": "When clicking ‘Test workflow’" }, { "parameters": { "documentId": { "__rl": true, "value": "1eAwxlsPHp_wlMjyibUpm-Z6U-BIfF9lnvQQv4JaCefg", "mode": "list", "cachedResultName": "商品订单表", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eAwxlsPHp_wlMjyibUpm-Z6U-BIfF9lnvQQv4JaCefg/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1483902731, "mode": "list", "cachedResultName": "工作表1(副本) 2", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eAwxlsPHp_wlMjyibUpm-Z6U-BIfF9lnvQQv4JaCefg/edit#gid=1483902731" }, "combineFilters": "OR", "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.6, "position": [ -20, -260 ], "id": "8cfb6055-d841-4ad7-ad2e-3a4ad90470ee", "name": "Google Sheets", "credentials": { "googleSheetsOAuth2Api": { "id": "rJy4Ge7IsXYRcWiY", "name": "Google Sheets account" } } }, { "parameters": { "jsCode": "// 增强的格式化日期函数\nfunction formatDate(dateInput) {\n if (!dateInput) return dateInput;\n \n let dateStr = String(dateInput).trim();\n \n // 处理8位数字格式 (如 20240501)\n if (/^\\d{8}$/.test(dateStr)) {\n const year = dateStr.substring(0, 4);\n const month = dateStr.substring(4, 6);\n const day = dateStr.substring(6, 8);\n return `${year}-${month}-${day}`;\n }\n \n // 处理7位数字格式 (如 2025529 表示 2025-05-29)\n if (/^\\d{7}$/.test(dateStr)) {\n const year = dateStr.substring(0, 4);\n const month = dateStr.substring(4, 5).padStart(2, '0');\n const day = dateStr.substring(5, 7).padStart(2, '0');\n return `${year}-${month}-${day}`;\n }\n \n // 处理6位数字格式 (如 202453 表示 2024-05-03)\n if (/^\\d{6}$/.test(dateStr)) {\n const year = dateStr.substring(0, 4);\n const month = dateStr.substring(4, 5).padStart(2, '0');\n const day = dateStr.substring(5, 6).padStart(2, '0');\n return `${year}-${month}-${day}`;\n }\n \n // 处理5位数字格式 (如 24529 表示 2024-05-29)\n if (/^\\d{5}$/.test(dateStr)) {\n const year = '20' + dateStr.substring(0, 2);\n const month = dateStr.substring(2, 3).padStart(2, '0');\n const day = dateStr.substring(3, 5).padStart(2, '0');\n return `${year}-${month}-${day}`;\n }\n \n // 处理带斜杠的日期格式 (如 2025/05/29 或 2025/5/29)\n const slashRegex = /^(\\d{4})[\\/\\\\](\\d{1,2})[\\/\\\\](\\d{1,2})$/;\n if (slashRegex.test(dateStr)) {\n const [_, year, month, day] = dateStr.match(slashRegex);\n return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;\n }\n \n // 处理带点的日期格式 (如 2025.05.29 或 2025.5.29)\n const dotRegex = /^(\\d{4})\\.(\\d{1,2})\\.(\\d{1,2})$/;\n if (dotRegex.test(dateStr)) {\n const [_, year, month, day] = dateStr.match(dotRegex);\n return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;\n }\n \n // 处理 MM-DD-YYYY 格式\n const mmddyyyyRegex = /^(\\d{1,2})-(\\d{1,2})-(\\d{4})$/;\n if (mmddyyyyRegex.test(dateStr)) {\n const [_, month, day, year] = dateStr.match(mmddyyyyRegex);\n return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;\n }\n \n // 处理 DD/MM/YYYY 格式\n const ddmmyyyyRegex = /^(\\d{1,2})[\\/\\\\](\\d{1,2})[\\/\\\\](\\d{4})$/;\n if (ddmmyyyyRegex.test(dateStr)) {\n const [_, day, month, year] = dateStr.match(ddmmyyyyRegex);\n return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;\n }\n \n // 处理 YYYY-MM-DD 格式 (已经是目标格式,但确保月和日是两位数)\n const yyyymmddRegex = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/;\n if (yyyymmddRegex.test(dateStr)) {\n const [_, year, month, day] = dateStr.match(yyyymmddRegex);\n return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;\n }\n \n // 处理中文日期格式 (如 2025年05月29日 或 2025年5月29日)\n const chineseRegex = /^(\\d{4})年(\\d{1,2})月(\\d{1,2})日?$/;\n if (chineseRegex.test(dateStr)) {\n const [_, year, month, day] = dateStr.match(chineseRegex);\n return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;\n }\n \n // 处理月份名称的日期格式 (如 29 May 2025 或 May 29, 2025)\n const monthNames = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];\n const monthNamesShort = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];\n \n // 尝试匹配 \"29 May 2025\" 格式\n const dayMonthYearRegex = new RegExp(`^(\\\\d{1,2})\\\\s+(${monthNames.join('|')}|${monthNamesShort.join('|')})\\\\s+(\\\\d{4})$`, 'i');\n if (dayMonthYearRegex.test(dateStr)) {\n const [_, day, monthStr, year] = dateStr.match(dayMonthYearRegex);\n const monthLower = monthStr.toLowerCase();\n let month;\n if (monthNames.includes(monthLower)) {\n month = (monthNames.indexOf(monthLower) + 1).toString().padStart(2, '0');\n } else {\n month = (monthNamesShort.indexOf(monthLower) + 1).toString().padStart(2, '0');\n }\n return `${year}-${month}-${day.padStart(2, '0')}`;\n }\n \n // 尝试匹配 \"May 29, 2025\" 格式\n const monthDayYearRegex = new RegExp(`^(${monthNames.join('|')}|${monthNamesShort.join('|')})\\\\s+(\\\\d{1,2})(?:,|\\\\s)\\\\s*(\\\\d{4})$`, 'i');\n if (monthDayYearRegex.test(dateStr)) {\n const [_, monthStr, day, year] = dateStr.match(monthDayYearRegex);\n const monthLower = monthStr.toLowerCase();\n let month;\n if (monthNames.includes(monthLower)) {\n month = (monthNames.indexOf(monthLower) + 1).toString().padStart(2, '0');\n } else {\n month = (monthNamesShort.indexOf(monthLower) + 1).toString().padStart(2, '0');\n }\n return `${year}-${month}-${day.padStart(2, '0')}`;\n }\n \n // 如果以上格式都不匹配,尝试使用Date对象解析\n // 但要小心处理,避免意外的解析结果\n try {\n const date = new Date(dateStr);\n if (!isNaN(date.getTime())) {\n const year = date.getFullYear();\n const month = (date.getMonth() + 1).toString().padStart(2, '0');\n const day = date.getDate().toString().padStart(2, '0');\n // 验证解析结果是否合理\n if (year >= 1900 && year <= 2100) {\n return `${year}-${month}-${day}`;\n }\n }\n } catch (e) {\n // 解析失败,继续使用原始值\n }\n \n // 如果所有尝试都失败,返回原始值\n return dateStr;\n}\n\n// 主处理函数\nfunction processData(items) {\n return items.map(item => {\n const data = item.json;\n const anomalies = [];\n \n // 1. 处理数量、单价、总金额\n const quantity = data.数量;\n const unitPrice = data.单价;\n let totalAmount = data.总金额;\n \n // 如果总金额为空但有数量和单价\n if ((totalAmount === \"\" || totalAmount === null || totalAmount === undefined) && quantity && unitPrice) {\n totalAmount = quantity * unitPrice;\n data.总金额 = totalAmount;\n anomalies.push(`总金额补全`);\n }\n // 如果数量为空但有单价和总金额\n else if ((quantity === \"\" || quantity === null || quantity === undefined) && unitPrice && totalAmount) {\n const calculatedQuantity = totalAmount / unitPrice;\n data.数量 = calculatedQuantity;\n anomalies.push(`数量补全`);\n }\n // 如果单价为空但有数量和总金额\n else if ((unitPrice === \"\" || unitPrice === null || unitPrice === undefined) && quantity && totalAmount) {\n const calculatedUnitPrice = totalAmount / quantity;\n data.单价 = calculatedUnitPrice;\n anomalies.push(`单价补全`);\n }\n \n // 2. 格式化日期\n const originalDate = data.下单日期;\n const formattedDate = formatDate(data.下单日期);\n if (String(originalDate) !== formattedDate) {\n anomalies.push(`下单日期补全`);\n data.下单日期 = formattedDate;\n }\n \n // 3. 添加数据异常字段\n data.数据异常 = anomalies.length > 0 ? anomalies.join('; ') : '无';\n \n return {\n json: data,\n pairedItem: item.pairedItem\n };\n });\n}\n\n// 处理输入数据\nreturn processData($input.all());\n" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 800, -260 ], "id": "34e158d3-de28-4423-8262-00a19d02f246", "name": "Code" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1eAwxlsPHp_wlMjyibUpm-Z6U-BIfF9lnvQQv4JaCefg", "mode": "list", "cachedResultName": "商品订单表", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eAwxlsPHp_wlMjyibUpm-Z6U-BIfF9lnvQQv4JaCefg/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1483902731, "mode": "list", "cachedResultName": "工作表1(副本) 2", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1eAwxlsPHp_wlMjyibUpm-Z6U-BIfF9lnvQQv4JaCefg/edit#gid=1483902731" }, "columns": { "mappingMode": "defineBelow", "value": { "收货地址": "={{ $json.output.address }}", "订单ID": "={{ $json[\"订单ID\"] }}", "数量": "={{ $json[\"数量\"] }}", "单价": "={{ $json[\"单价\"] }}", "下单日期": "={{ $json[\"下单日期\"] }}", "总金额": "={{ $json[\"总金额\"] }}", "异常补全": "={{ $json[\"数据异常\"] }}" }, "matchingColumns": [ "订单ID" ], "schema": [ { "id": "订单ID", "displayName": "订单ID", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "客户姓名", "displayName": "客户姓名", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "联系电话", "displayName": "联系电话", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "收货地址", "displayName": "收货地址", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "商品ID", "displayName": "商品ID", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "数量", "displayName": "数量", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "单价", "displayName": "单价", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "总金额", "displayName": "总金额", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "下单日期", "displayName": "下单日期", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "销售员", "displayName": "销售员", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "联系方式", "displayName": "联系方式", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "异常补全", "displayName": "异常补全", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "readOnly": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.6, "position": [ 1016, -260 ], "id": "62211a47-e18c-455d-a1be-70d903fa06a5", "name": "Google Sheets1", "credentials": { "googleSheetsOAuth2Api": { "id": "rJy4Ge7IsXYRcWiY", "name": "Google Sheets account" } } }, { "parameters": { "promptType": "define", "text": "=订单ID:{{ $json[\"订单ID\"] }}\n收货地址:{{ $json[\"收货地址\"] }}", "hasOutputParser": true, "messages": { "messageValues": [ { "message": "你是一个数据清洗专家,帮助我优化收货地址,把用户的地址信息修改成标准化的省市区格式显示" } ] } }, "type": "@n8n/n8n-nodes-langchain.chainLlm", "typeVersion": 1.6, "position": [ 200, -140 ], "id": "c117237a-10fb-42d7-a908-1486a82be81a", "name": "Basic LLM Chain" }, { "parameters": { "options": {} }, "type": "@n8n/n8n-nodes-langchain.lmChatDeepSeek", "typeVersion": 1, "position": [ 180, 100 ], "id": "07a61a28-33ec-453f-be81-2f2614163da5", "name": "DeepSeek Chat Model", "credentials": { "deepSeekApi": { "id": "4u9TInsIfdWaWI2C", "name": "DeepSeek account" } } }, { "parameters": { "jsonSchemaExample": "{\n \"orderid\": \"订单ID\",\n\t\"address\": \"收货地址\"\n}" }, "type": "@n8n/n8n-nodes-langchain.outputParserStructured", "typeVersion": 1.2, "position": [ 380, 100 ], "id": "066926df-f473-4cf9-9aa2-7c3ce5c74eea", "name": "Structured Output Parser" }, { "parameters": { "mode": "combine", "advanced": true, "mergeByFields": { "values": [ { "field1": "订单ID", "field2": "output.orderid" } ] }, "joinMode": "enrichInput1", "options": {} }, "type": "n8n-nodes-base.merge", "typeVersion": 3.1, "position": [ 576, -260 ], "id": "354a20ab-d96d-4342-9612-39400fa7a59c", "name": "Merge" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "conditions": [ { "id": "f6f39899-7d3b-409b-88a1-894cfb76b955", "leftValue": "={{ $('Google Sheets').item.json[\"联系电话\"].toString() }}", "rightValue": "", "operator": { "type": "string", "operation": "notEmpty", "singleValue": true } }, { "id": "0b90fe67-8d84-4197-9ff3-3c959e8b627a", "leftValue": "={{ $('Google Sheets').item.json[\"联系电话\"].toString().match(/^1[3-9]\\d{9}$/) !== null }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 1236, -260 ], "id": "4f18b9c2-2da7-43b5-a68b-e306a3456e19", "name": "If" }, { "parameters": { "content": "## 触发条件\n- 可以设置为定时触发\n- 也可以设置为 表格数据变动触发。当新数据写入,自动触发数据格式化工作流程\n\n## 表格数据\n- 可以是云表格,或者本地的excel表格,或者notion表格都可以\n\n## 收获地址处理\n- 如果数据条数非常多,可以分批执行,使用拆分节点+loop循环节点+合并节点实现每次只处理一部分内容。\n\n", "height": 380, "width": 340, "color": 3 }, "type": "n8n-nodes-base.stickyNote", "position": [ -300, -20 ], "typeVersion": 1, "id": "ea5062a7-e6d5-45e7-a24d-8d2896260300", "name": "Sticky Note" }, { "parameters": { "chatId": "={{ $('Google Sheets').item.json[\"联系方式\"] }}", "text": "=***订单发货通知***\n订单编号:***{{ $('Google Sheets').item.json[\"订单ID\"] }}***\n客户姓名:***{{ $('Google Sheets').item.json[\"客户姓名\"] }}***\n订单数据核实无误,等待发货!\n", "additionalFields": { "appendAttribution": false, "parse_mode": "Markdown" } }, "type": "n8n-nodes-base.telegram", "typeVersion": 1.2, "position": [ 1460, -400 ], "id": "7d83cbf0-8577-4a78-b2a9-6728eb0f5a6f", "name": "订单无误通知", "webhookId": "259a3e36-a1b7-4da7-aeed-fb99b7fef40c", "credentials": { "telegramApi": { "id": "TTK8cWeb2Gr6vF1r", "name": "Telegram AI小林官方" } } }, { "parameters": { "chatId": "={{ $('Google Sheets').item.json[\"联系方式\"] }}", "text": "=***订单数据异常通知***\n\n你的订ID:***{{ $('Google Sheets').item.json[\"订单ID\"] }}***,客户名:***{{ $('Google Sheets').item.json[\"客户姓名\"] }}***;\n联系方式存在异常;请及时检查订单数据,以免发货失败!\n", "additionalFields": { "appendAttribution": false, "parse_mode": "Markdown" } }, "type": "n8n-nodes-base.telegram", "typeVersion": 1.2, "position": [ 1460, -160 ], "id": "a6b99a5e-90b0-484f-888b-104fcde578b3", "name": "订单异常通知", "webhookId": "6399b6ee-9151-4d95-a04c-e5a63f5d2da3", "credentials": { "telegramApi": { "id": "cWYaMRF2D0C7JSq3", "name": "Telegram account" } } }, { "parameters": { "content": "## 通知\n- 可以设定不同的通知方式,如:短信、AI电话、钉钉、telegram、slack、whatsapp等", "width": 340, "color": 4 }, "type": "n8n-nodes-base.stickyNote", "position": [ 1420, 80 ], "typeVersion": 1, "id": "f1d9de59-f6ab-4261-a67e-983589624a3a", "name": "Sticky Note1" } ], "pinData": {}, "connections": { "When clicking ‘Test workflow’": { "main": [ [ { "node": "Google Sheets", "type": "main", "index": 0 } ] ] }, "Google Sheets": { "main": [ [ { "node": "Basic LLM Chain", "type": "main", "index": 0 }, { "node": "Merge", "type": "main", "index": 0 } ] ] }, "Code": { "main": [ [ { "node": "Google Sheets1", "type": "main", "index": 0 } ] ] }, "Basic LLM Chain": { "main": [ [ { "node": "Merge", "type": "main", "index": 1 } ] ] }, "DeepSeek Chat Model": { "ai_languageModel": [ [ { "node": "Basic LLM Chain", "type": "ai_languageModel", "index": 0 } ] ] }, "Structured Output Parser": { "ai_outputParser": [ [ { "node": "Basic LLM Chain", "type": "ai_outputParser", "index": 0 } ] ] }, "Merge": { "main": [ [ { "node": "Code", "type": "main", "index": 0 } ] ] }, "Google Sheets1": { "main": [ [ { "node": "If", "type": "main", "index": 0 } ] ] }, "If": { "main": [ [ { "node": "订单无误通知", "type": "main", "index": 0 } ], [ { "node": "订单异常通知", "type": "main", "index": 0 } ] ] } }, "active": false, "settings": { "executionOrder": "v1" }, "versionId": "080a5ca7-140e-4a44-a285-24f18fba6a4a", "meta": { "templateCredsSetupCompleted": true, "instanceId": "b98b98135dab39d60742eab16a2a66a05dec7028bfdb9b31aea7dce5ef9e0c67" }, "id": "j79EIWruCtJxrvjE", "tags": [] }