使用 RFM 分析对 WooCommerce 客户进行细分以实现精准营销

中级

这是一个Marketing领域的自动化工作流,包含 7 个节点。主要使用 Code、Html、Start、WooCommerce、ScheduleTrigger 等节点。 使用 RFM 分析对 WooCommerce 客户进行细分以实现精准营销

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

分类

工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "370f0437fbf8d71caf7e9230e115239afcf5012c55ac368aa9dee29869df4a6b",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "5db1ea37-aa49-4081-8fd0-fa9416cbcff4",
      "name": "手动开始",
      "type": "n8n-nodes-base.start",
      "position": [
        0,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "c6c0b7d2-e1d4-442d-aff5-b206426411bc",
      "name": "每周触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        200
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "e00baa15-fd2c-444b-9c31-189d6b9986c7",
      "name": "从 WooCommerce 获取订单",
      "type": "n8n-nodes-base.wooCommerce",
      "position": [
        200,
        0
      ],
      "parameters": {
        "options": {
          "after": "={{ $now.minus(1, 'year').toISO() }}",
          "status": "completed"
        },
        "resource": "order",
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {
        "wooCommerceApi": {
          "id": "7FzoXyPl8TAUChEg",
          "name": "WooCommerce account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ef5905d0-4db8-4e57-bc4e-44b72d3bf4a6",
      "name": "计算 RFM 细分",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        0
      ],
      "parameters": {
        "jsCode": "// Group orders by customer and calculate RFM\nconst orders = $input.all();\nconst customers = {};\nconst today = new Date();\n\n// Process orders\norders.forEach(order => {\n  const customerId = order.json.customer_id || order.json.billing.email;\n  if (!customerId) return;\n  \n  if (!customers[customerId]) {\n    customers[customerId] = {\n      customerId: customerId,\n      customerName: `${order.json.billing.first_name} ${order.json.billing.last_name}`,\n      email: order.json.billing.email,\n      orders: [],\n      totalSpent: 0,\n      orderCount: 0\n    };\n  }\n  \n  const orderDate = new Date(order.json.date_created);\n  customers[customerId].orders.push({\n    date: orderDate,\n    total: parseFloat(order.json.total)\n  });\n  customers[customerId].totalSpent += parseFloat(order.json.total);\n  customers[customerId].orderCount++;\n});\n\n// Calculate RFM values\nconst customerData = Object.values(customers).map(customer => {\n  // Recency: Number of days since last purchase\n  const lastOrderDate = Math.max(...customer.orders.map(o => o.date));\n  const recency = Math.floor((today - new Date(lastOrderDate)) / (1000 * 60 * 60 * 24));\n  \n  // Frequency: Number of orders\n  const frequency = customer.orderCount;\n  \n  // Monetary: Total spent\n  const monetary = customer.totalSpent;\n  \n  return {\n    customerId: customer.customerId,\n    customerName: customer.customerName,\n    email: customer.email,\n    recency: recency,\n    frequency: frequency,\n    monetary: monetary,\n    lastOrderDate: new Date(lastOrderDate).toISOString().split('T')[0]\n  };\n});\n\n// Sort for quartile calculations\nconst sortedByRecency = [...customerData].sort((a, b) => a.recency - b.recency);\nconst sortedByFrequency = [...customerData].sort((a, b) => b.frequency - a.frequency);\nconst sortedByMonetary = [...customerData].sort((a, b) => b.monetary - a.monetary);\n\n// Quartile calculation helper\nfunction getQuartile(sortedArray, value, field, ascending = false) {\n  const index = sortedArray.findIndex(item => item[field] === value);\n  const quartile = Math.ceil((index + 1) / sortedArray.length * 4);\n  return ascending ? quartile : 5 - quartile;\n}\n\n// Assign RFM scores\nconst rfmScores = customerData.map(customer => {\n  const rScore = getQuartile(sortedByRecency, customer.recency, 'recency', true);\n  const fScore = getQuartile(sortedByFrequency, customer.frequency, 'frequency');\n  const mScore = getQuartile(sortedByMonetary, customer.monetary, 'monetary');\n  \n  const rfmScore = `${rScore}${fScore}${mScore}`;\n  \n  // Determine segment\n  let segment = '';\n  if (rScore >= 4 && fScore >= 4 && mScore >= 4) {\n    segment = 'Champions';\n  } else if (rScore >= 3 && fScore >= 3 && mScore >= 4) {\n    segment = 'Loyal Customers';\n  } else if (rScore >= 3 && fScore >= 1 && mScore >= 3) {\n    segment = 'Potential Loyalists';\n  } else if (rScore >= 4 && fScore <= 2) {\n    segment = 'New Customers';\n  } else if (rScore >= 3 && fScore >= 3 && mScore <= 2) {\n    segment = 'Promising';\n  } else if (rScore <= 2 && fScore >= 3) {\n    segment = 'Need Attention';\n  } else if (rScore <= 2 && fScore <= 2 && mScore >= 3) {\n    segment = 'About to Sleep';\n  } else if (rScore <= 2 && fScore >= 3 && mScore >= 3) {\n    segment = 'At Risk';\n  } else if (rScore <= 2 && fScore <= 2 && mScore <= 2) {\n    segment = 'Lost';\n  } else {\n    segment = 'Others';\n  }\n  \n  return {\n    ...customer,\n    rScore,\n    fScore,\n    mScore,\n    rfmScore,\n    segment,\n    avgOrderValue: customer.monetary / customer.frequency\n  };\n});\n\nreturn rfmScores;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5702ae22-50a8-4a53-9fef-593a9e4c8001",
      "name": "生成细分摘要",
      "type": "n8n-nodes-base.code",
      "position": [
        840,
        0
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\n// Segment translation and priority definitions\nconst segmentDetails = {\n  'Champions': { fa: 'Champions', priority: 1, action: 'Send VIP offer and exclusive discount code' },\n  'Loyal Customers': { fa: 'Loyal Customers', priority: 2, action: 'Invite to loyalty club and introduce new products' },\n  'Potential Loyalists': { fa: 'Potential Loyalists', priority: 3, action: 'Encourage more interaction and suggest complementary products' },\n  'New Customers': { fa: 'New Customers', priority: 4, action: 'Send welcome message and encourage second purchase' },\n  'Promising': { fa: 'Promising', priority: 5, action: 'Send product reminders and additional education' },\n  'Need Attention': { fa: 'Need Attention', priority: 6, action: 'Investigate reasons for inactivity and send comeback campaign' },\n  'About to Sleep': { fa: 'About to Sleep', priority: 7, action: 'Send new products and return incentives' },\n  'At Risk': { fa: 'At Risk', priority: 8, action: 'Send personalized message with heavy discount' },\n  'Lost': { fa: 'Lost', priority: 9, action: 'Offer return deal and \"We Miss You\" campaign' },\n  'Others': { fa: 'Others', priority: 10, action: 'Manual review for behavior analysis' },\n};\n\n// Count customers per segment\nconst segmentCounts = {};\n\nfor (const item of items) {\n  const seg = item.json.segment || 'Others';\n  if (!segmentCounts[seg]) segmentCounts[seg] = 0;\n  segmentCounts[seg]++;\n}\n\n// Build HTML table\nlet html = `\n<h2 style=\"text-align: center;\">RFM Summary Report - ${new Date().toISOString().split('T')[0]}</h2>\n<table border=\"1\" cellpadding=\"8\" cellspacing=\"0\" style=\"width:90%; margin:auto; border-collapse:collapse; font-family:sans-serif;\">\n  <thead style=\"background:#e6f2ff;\">\n    <tr>\n      <th>Segment</th>\n      <th>Customer Count</th>\n      <th>Marketing Suggestion</th>\n    </tr>\n  </thead>\n  <tbody>\n`;\n\nObject.entries(segmentCounts)\n  .map(([key, count]) => ({\n    key,\n    count,\n    priority: segmentDetails[key]?.priority || 999,\n    label: segmentDetails[key]?.fa || key,\n    action: segmentDetails[key]?.action || '-'\n  }))\n  .sort((a, b) => a.priority - b.priority)\n  .forEach(({ label, count, action }) => {\n    html += `\n      <tr>\n        <td>${label}</td>\n        <td>${count}</td>\n        <td>${action}</td>\n      </tr>\n    `;\n  });\n\nhtml += `\n  </tbody>\n</table>\n`;\n\nreturn [{ json: { html } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4b4993f8-25d0-4109-bec7-41902c065eb3",
      "name": "构建报告页面",
      "type": "n8n-nodes-base.html",
      "position": [
        1120,
        0
      ],
      "parameters": {
        "html": "<head>\n  <meta charset=\"UTF-8\">\n  <style>\n    body {\n      font-family: sans-serif;\n      background-color: #f7f7f7;\n      padding: 20px;\n    }\n    h2 {\n      text-align: center;\n      color: #333;\n    }\n    table {\n      width: 100%;\n      border-collapse: collapse;\n      margin-top: 20px;\n      background-color: #fff;\n    }\n    th, td {\n      border: 1px solid #ccc;\n      padding: 8px 12px;\n      font-size: 14px;\n      text-align: center;\n    }\n    th {\n      background-color: #e6f2ff;\n    }\n    tr:nth-child(even) {\n      background-color: #f9f9f9;\n    }\n    .segment {\n      font-weight: bold;\n      color: #2e7d32;\n    }\n  </style>\n</head>\n{{ $json.html }}\n"
      },
      "typeVersion": 1.2
    },
    {
      "id": "76665a4f-a27d-42a2-81dd-4e62411a7129",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -460
      ],
      "parameters": {
        "width": 740,
        "height": 440,
        "content": "## 🛠️ 本工作流设置说明"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "Manual Start": {
      "main": [
        [
          {
            "node": "Fetch Orders From WooCommerce",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Trigger": {
      "main": [
        [
          {
            "node": "Fetch Orders From WooCommerce",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute RFM Segmentation": {
      "main": [
        [
          {
            "node": "Generate Segment Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Segment Summary": {
      "main": [
        [
          {
            "node": "Build Report Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Orders From WooCommerce": {
      "main": [
        [
          {
            "node": "Compute RFM Segmentation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

这是一个中级难度的工作流,适用于Marketing等场景。适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

需要付费吗?

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

工作流信息
难度等级
中级
节点数量7
分类1
节点类型6
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

作者
外部链接
在 n8n.io 上查看 →

分享此工作流