Logon8n中文教程

数据转换

掌握N8N中的数据转换技巧,灵活处理各种数据格式和结构

数据转换

数据转换是工作流处理的核心技能。在实际应用中,我们经常需要将来自不同系统的数据转换为目标格式,或者对数据进行清洗、聚合、分析等操作。本章将详细介绍 N8N 中的各种数据转换技巧。

🔄 数据转换概述

转换场景分类

转换类型目的常用节点适用场景
格式转换改变数据格式Set, FunctionJSON ↔ CSV ↔ XML
结构转换重新组织数据Set, Code扁平化、嵌套化
内容转换修改数据内容Function, Code计算、验证、清洗
聚合转换数据汇总分析Aggregate, Function统计、分组、排序

🛠️ Set 节点数据转换

Set 节点是最常用的数据转换工具,适合简单到中等复杂度的转换。

基础字段转换

// 基本字段映射和计算
{
  // 字符串操作
  "fullName": "{{ $json.firstName }} {{ $json.lastName }}",
  "email": "{{ $json.email.toLowerCase() }}",
  "displayName": "{{ $json.nickname || $json.firstName }}",
  
  // 数值计算
  "totalPrice": "{{ $json.price * $json.quantity }}",
  "discountedPrice": "{{ $json.price * (1 - $json.discountRate) }}",
  "tax": "{{ Math.round($json.price * 0.13 * 100) / 100 }}",
  
  // 日期处理
  "formattedDate": "{{ new Date($json.date).toLocaleDateString('zh-CN') }}",
  "dayOfWeek": "{{ ['日', '一', '二', '三', '四', '五', '六'][new Date($json.date).getDay()] }}",
  "isWeekend": "{{ [0, 6].includes(new Date($json.date).getDay()) }}",
  
  // 布尔逻辑
  "isVIP": "{{ $json.memberLevel === 'gold' || $json.points > 10000 }}",
  "needsReview": "{{ $json.amount > 1000 || $json.isFirstTime }}",
  "isValid": "{{ $json.email && $json.phone && $json.name }}"
}

条件转换

// 复杂条件逻辑
{
  "customerLevel": "{{ 
    $json.totalSpent > 50000 ? 'VIP' : 
    $json.totalSpent > 10000 ? 'Gold' : 
    $json.totalSpent > 1000 ? 'Silver' : 'Bronze' 
  }}",
  
  "shippingFee": "{{
    $json.amount > 99 ? 0 :
    $json.location === '北京' || $json.location === '上海' ? 10 : 15
  }}",
  
  "urgencyLevel": "{{
    $json.keywords.includes('紧急') ? 'high' :
    $json.amount > 10000 ? 'medium' : 'normal'
  }}",
  
  "statusColor": "{{
    $json.status === 'success' ? 'green' :
    $json.status === 'pending' ? 'orange' :
    $json.status === 'failed' ? 'red' : 'gray'
  }}"
}

数组和对象处理

// 数组操作
{
  "productNames": "{{ $json.products.map(p => p.name) }}",
  "totalQuantity": "{{ $json.items.reduce((sum, item) => sum + item.quantity, 0) }}",
  "hasElectronics": "{{ $json.categories.includes('electronics') }}",
  "topProduct": "{{ $json.products.sort((a, b) => b.sales - a.sales)[0] }}",
  
  // 对象重构
  "orderSummary": "{{
    {
      id: $json.orderId,
      customer: $json.customerName,
      total: $json.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
      itemCount: $json.items.length,
      categories: [...new Set($json.items.map(item => item.category))]
    }
  }}",
  
  // 嵌套数据提取
  "billingInfo": "{{
    {
      name: $json.customer.billing.name,
      address: `${$json.customer.billing.street}, ${$json.customer.billing.city}`,
      country: $json.customer.billing.country || 'CN'
    }
  }}"
}

💻 Function 节点高级转换

Function 节点提供完整的 JavaScript 环境,适合复杂的数据转换逻辑。

数据清洗和验证

// Function 节点:用户数据清洗
const items = $input.all();
const cleanedData = [];
const errors = [];

for (const item of items) {
  const user = item.json;
  
  try {
    // 数据清洗
    const cleanedUser = {
      id: user.id?.toString().trim(),
      name: user.name?.trim(),
      email: user.email?.toLowerCase().trim(),
      phone: user.phone?.replace(/\D/g, ''), // 只保留数字
      age: parseInt(user.age) || null,
      
      // 地址标准化
      address: {
        province: normalizeProvince(user.province),
        city: normalizeCity(user.city),
        district: user.district?.trim(),
        street: user.street?.trim()
      },
      
      // 标签处理
      tags: Array.isArray(user.tags) 
        ? user.tags.filter(tag => tag && tag.trim()).map(tag => tag.trim())
        : [],
      
      // 计算字段
      fullAddress: `${user.province}${user.city}${user.district}${user.street}`,
      isAdult: parseInt(user.age) >= 18,
      emailDomain: user.email?.split('@')[1],
      
      // 元数据
      processedAt: new Date().toISOString(),
      dataSource: user.source || 'unknown'
    };
    
    // 数据验证
    const validationErrors = validateUser(cleanedUser);
    if (validationErrors.length > 0) {
      errors.push({
        id: user.id,
        errors: validationErrors,
        originalData: user
      });
      continue;
    }
    
    cleanedData.push({ json: cleanedUser });
    
  } catch (error) {
    errors.push({
      id: user.id || 'unknown',
      error: error.message,
      originalData: user
    });
  }
}

// 辅助函数
function normalizeProvince(province) {
  const provinceMap = {
    '北京': '北京市',
    '上海': '上海市',
    '广东': '广东省',
    '江苏': '江苏省'
    // 更多省份映射...
  };
  return provinceMap[province] || province;
}

function normalizeCity(city) {
  return city?.replace(/$/, '') + '市';
}

function validateUser(user) {
  const errors = [];
  
  if (!user.id) errors.push('缺少用户ID');
  if (!user.name) errors.push('缺少用户姓名');
  if (!user.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(user.email)) {
    errors.push('邮箱格式无效');
  }
  if (user.phone && !/^1[3-9]\d{9}$/.test(user.phone)) {
    errors.push('手机号格式无效');
  }
  if (user.age !== null && (user.age < 0 || user.age > 150)) {
    errors.push('年龄数据异常');
  }
  
  return errors;
}

// 输出清洗后的数据
console.log(`数据清洗完成: 成功 ${cleanedData.length} 条, 错误 ${errors.length} 条`);

if (errors.length > 0) {
  console.error('数据错误详情:', errors);
}

return cleanedData;

数据重构和聚合

// Function 节点:订单数据聚合分析
const orders = $input.all();
const analysis = {
  summary: {
    totalOrders: orders.length,
    totalRevenue: 0,
    averageOrderValue: 0,
    processedAt: new Date().toISOString()
  },
  byCustomer: new Map(),
  byProduct: new Map(),
  byDate: new Map(),
  byRegion: new Map()
};

// 处理每个订单
for (const item of orders) {
  const order = item.json;
  const orderDate = new Date(order.createdAt).toDateString();
  const customerId = order.customerId;
  const region = order.customer.region;
  
  // 总收入计算
  analysis.summary.totalRevenue += order.totalAmount;
  
  // 按客户聚合
  if (!analysis.byCustomer.has(customerId)) {
    analysis.byCustomer.set(customerId, {
      customerName: order.customer.name,
      orderCount: 0,
      totalSpent: 0,
      averageOrderValue: 0,
      firstOrderDate: order.createdAt,
      lastOrderDate: order.createdAt,
      favoriteCategories: new Set()
    });
  }
  
  const customerData = analysis.byCustomer.get(customerId);
  customerData.orderCount += 1;
  customerData.totalSpent += order.totalAmount;
  customerData.averageOrderValue = customerData.totalSpent / customerData.orderCount;
  customerData.lastOrderDate = order.createdAt;
  
  // 按产品聚合
  for (const item of order.items) {
    if (!analysis.byProduct.has(item.productId)) {
      analysis.byProduct.set(item.productId, {
        productName: item.productName,
        category: item.category,
        totalSold: 0,
        totalRevenue: 0,
        orderCount: 0,
        customers: new Set()
      });
    }
    
    const productData = analysis.byProduct.get(item.productId);
    productData.totalSold += item.quantity;
    productData.totalRevenue += item.price * item.quantity;
    productData.orderCount += 1;
    productData.customers.add(customerId);
    
    customerData.favoriteCategories.add(item.category);
  }
  
  // 按日期聚合
  if (!analysis.byDate.has(orderDate)) {
    analysis.byDate.set(orderDate, {
      date: orderDate,
      orderCount: 0,
      revenue: 0,
      uniqueCustomers: new Set()
    });
  }
  
  const dateData = analysis.byDate.get(orderDate);
  dateData.orderCount += 1;
  dateData.revenue += order.totalAmount;
  dateData.uniqueCustomers.add(customerId);
  
  // 按地区聚合
  if (!analysis.byRegion.has(region)) {
    analysis.byRegion.set(region, {
      region: region,
      orderCount: 0,
      revenue: 0,
      customerCount: new Set()
    });
  }
  
  const regionData = analysis.byRegion.get(region);
  regionData.orderCount += 1;
  regionData.revenue += order.totalAmount;
  regionData.customerCount.add(customerId);
}

// 计算平均订单金额
analysis.summary.averageOrderValue = analysis.summary.totalRevenue / analysis.summary.totalOrders;

// 转换 Map 为 Array 并排序
const customerRanking = Array.from(analysis.byCustomer.entries())
  .map(([id, data]) => ({ customerId: id, ...data, favoriteCategories: Array.from(data.favoriteCategories) }))
  .sort((a, b) => b.totalSpent - a.totalSpent)
  .slice(0, 10); // 前10名客户

const productRanking = Array.from(analysis.byProduct.entries())
  .map(([id, data]) => ({ productId: id, ...data, uniqueCustomers: data.customers.size }))
  .sort((a, b) => b.totalRevenue - a.totalRevenue)
  .slice(0, 20); // 前20名产品

const dailyStats = Array.from(analysis.byDate.entries())
  .map(([date, data]) => ({ ...data, uniqueCustomers: data.uniqueCustomers.size }))
  .sort((a, b) => new Date(a.date) - new Date(b.date));

const regionStats = Array.from(analysis.byRegion.entries())
  .map(([region, data]) => ({ ...data, customerCount: data.customerCount.size }))
  .sort((a, b) => b.revenue - a.revenue);

// 生成分析报告
const report = {
  summary: analysis.summary,
  topCustomers: customerRanking,
  topProducts: productRanking,
  dailyTrends: dailyStats,
  regionPerformance: regionStats,
  insights: generateInsights(analysis)
};

function generateInsights(analysis) {
  const insights = [];
  
  // 客户洞察
  const avgCustomerValue = analysis.summary.totalRevenue / analysis.byCustomer.size;
  insights.push(`平均客户价值: ¥${avgCustomerValue.toFixed(2)}`);
  
  // 产品洞察
  const topProduct = Array.from(analysis.byProduct.values())
    .sort((a, b) => b.totalRevenue - a.totalRevenue)[0];
  insights.push(`最受欢迎产品: ${topProduct.productName} (收入: ¥${topProduct.totalRevenue})`);
  
  // 时间洞察
  const peakDate = Array.from(analysis.byDate.values())
    .sort((a, b) => b.revenue - a.revenue)[0];
  insights.push(`最高销售日: ${peakDate.date} (收入: ¥${peakDate.revenue})`);
  
  return insights;
}

console.log('订单分析完成:', report.summary);
return [{ json: report }];

🔧 Code 节点复杂转换

Code 节点提供更强大的处理能力,适合复杂的数据处理场景。

数据结构重组

// Code 节点:电商数据标准化
// 输入:多个电商平台的不同格式数据
// 输出:统一的标准格式

for (const item of $input.all()) {
  const rawData = item.json;
  const platform = rawData.platform;
  
  let standardizedData;
  
  switch (platform) {
    case 'taobao':
      standardizedData = transformTaobaoData(rawData);
      break;
    case 'jd':
      standardizedData = transformJDData(rawData);
      break;
    case 'tmall':
      standardizedData = transformTmallData(rawData);
      break;
    default:
      throw new Error(`Unsupported platform: ${platform}`);
  }
  
  $return.push({ json: standardizedData });
}

function transformTaobaoData(data) {
  return {
    // 基本信息
    id: data.item_id,
    title: data.title,
    description: data.desc,
    
    // 价格信息
    pricing: {
      originalPrice: parseFloat(data.price),
      currentPrice: parseFloat(data.sale_price || data.price),
      currency: 'CNY',
      discount: data.discount_rate ? parseFloat(data.discount_rate) : 0
    },
    
    // 商品属性
    attributes: {
      brand: data.brand,
      category: data.cat_name,
      specifications: parseSpecifications(data.props),
      weight: data.weight ? parseFloat(data.weight) : null,
      dimensions: parseDimensions(data.size)
    },
    
    // 库存和销量
    inventory: {
      stock: parseInt(data.quantity),
      sold: parseInt(data.sales),
      availability: data.quantity > 0 ? 'in_stock' : 'out_of_stock'
    },
    
    // 图片和媒体
    media: {
      mainImage: data.pic_url,
      thumbnails: data.small_images || [],
      gallery: data.images || []
    },
    
    // 商家信息
    seller: {
      id: data.seller_id,
      name: data.seller_name,
      rating: data.seller_credit ? parseFloat(data.seller_credit) : null,
      location: data.location
    },
    
    // 元数据
    metadata: {
      platform: 'taobao',
      originalUrl: data.detail_url,
      lastUpdated: new Date().toISOString(),
      syncSource: 'api'
    }
  };
}

function transformJDData(data) {
  return {
    id: data.wareId,
    title: data.name,
    description: data.introduction,
    
    pricing: {
      originalPrice: parseFloat(data.jdPrice),
      currentPrice: parseFloat(data.wlPrice || data.jdPrice),
      currency: 'CNY',
      discount: calculateDiscount(data.jdPrice, data.wlPrice)
    },
    
    attributes: {
      brand: data.brandName,
      category: data.category,
      specifications: parseJDSpecs(data.param),
      weight: data.weight ? parseFloat(data.weight) : null
    },
    
    inventory: {
      stock: parseInt(data.stockState),
      sold: parseInt(data.commentCount),
      availability: data.stockState === 33 ? 'in_stock' : 'out_of_stock'
    },
    
    media: {
      mainImage: `https:${data.imageInfo.imageList[0]}`,
      thumbnails: data.imageInfo.imageList.slice(1, 5).map(img => `https:${img}`),
      gallery: data.imageInfo.imageList.map(img => `https:${img}`)
    },
    
    seller: {
      id: data.shopId,
      name: data.shopName,
      rating: null,
      location: null
    },
    
    metadata: {
      platform: 'jd',
      originalUrl: `https://item.jd.com/${data.wareId}.html`,
      lastUpdated: new Date().toISOString(),
      syncSource: 'api'
    }
  };
}

function transformTmallData(data) {
  // 天猫数据转换逻辑...
  return {
    // 标准化结构
  };
}

// 辅助函数
function parseSpecifications(propsString) {
  if (!propsString) return {};
  
  const specs = {};
  const pairs = propsString.split(';');
  
  for (const pair of pairs) {
    const [key, value] = pair.split(':');
    if (key && value) {
      specs[key.trim()] = value.trim();
    }
  }
  
  return specs;
}

function parseDimensions(sizeString) {
  if (!sizeString) return null;
  
  const match = sizeString.match(/(\d+\.?\d*)\s*[x×]\s*(\d+\.?\d*)\s*[x×]\s*(\d+\.?\d*)/);
  if (match) {
    return {
      length: parseFloat(match[1]),
      width: parseFloat(match[2]),
      height: parseFloat(match[3]),
      unit: 'cm'
    };
  }
  
  return null;
}

function calculateDiscount(originalPrice, salePrice) {
  if (!salePrice || salePrice >= originalPrice) return 0;
  return Math.round((1 - salePrice / originalPrice) * 100);
}

function parseJDSpecs(paramArray) {
  const specs = {};
  
  if (Array.isArray(paramArray)) {
    for (const group of paramArray) {
      if (group.groupName && Array.isArray(group.params)) {
        for (const param of group.params) {
          specs[param.name] = param.value;
        }
      }
    }
  }
  
  return specs;
}

📊 Aggregate 节点数据聚合

Aggregate 节点专门用于数据汇总和分组操作。

基础聚合操作

// Aggregate 节点配置
{
  "keys": ["category", "region"],  // 分组字段
  "values": {
    // 数值聚合
    "totalSales": {
      "field": "amount",
      "operation": "sum"
    },
    "averageOrderValue": {
      "field": "amount", 
      "operation": "average"
    },
    "maxSingleOrder": {
      "field": "amount",
      "operation": "max"
    },
    "minSingleOrder": {
      "field": "amount",
      "operation": "min"
    },
    
    // 计数聚合
    "orderCount": {
      "field": "orderId",
      "operation": "count"
    },
    "uniqueCustomers": {
      "field": "customerId",
      "operation": "countUnique"
    },
    
    // 数组聚合
    "allProducts": {
      "field": "productName",
      "operation": "append"
    },
    "firstOrderDate": {
      "field": "orderDate",
      "operation": "first"
    },
    "lastOrderDate": {
      "field": "orderDate",
      "operation": "last"
    }
  }
}

复合聚合示例

// Function 节点:多维度数据聚合
const salesData = $input.all();

// 创建多维度聚合结构
const aggregation = {
  overall: {
    totalRevenue: 0,
    totalOrders: 0,
    uniqueCustomers: new Set(),
    uniqueProducts: new Set()
  },
  byTimeFrame: {
    daily: {},
    weekly: {},
    monthly: {}
  },
  byDimension: {
    category: {},
    region: {},
    customerSegment: {},
    salesChannel: {}
  },
  crossAnalysis: {
    categoryByRegion: {},
    channelByTime: {},
    customerByCategory: {}
  }
};

// 处理每条销售记录
for (const item of salesData) {
  const sale = item.json;
  const date = new Date(sale.orderDate);
  const dateStr = date.toISOString().split('T')[0];
  const weekStr = getWeekString(date);
  const monthStr = date.toISOString().substring(0, 7);
  
  // 总体统计
  aggregation.overall.totalRevenue += sale.amount;
  aggregation.overall.totalOrders += 1;
  aggregation.overall.uniqueCustomers.add(sale.customerId);
  aggregation.overall.uniqueProducts.add(sale.productId);
  
  // 时间维度聚合
  updateTimeAggregation(aggregation.byTimeFrame.daily, dateStr, sale);
  updateTimeAggregation(aggregation.byTimeFrame.weekly, weekStr, sale);
  updateTimeAggregation(aggregation.byTimeFrame.monthly, monthStr, sale);
  
  // 分类维度聚合
  updateDimensionAggregation(aggregation.byDimension.category, sale.category, sale);
  updateDimensionAggregation(aggregation.byDimension.region, sale.region, sale);
  updateDimensionAggregation(aggregation.byDimension.customerSegment, sale.customerSegment, sale);
  updateDimensionAggregation(aggregation.byDimension.salesChannel, sale.channel, sale);
  
  // 交叉分析
  const categoryRegionKey = `${sale.category}-${sale.region}`;
  updateCrossAggregation(aggregation.crossAnalysis.categoryByRegion, categoryRegionKey, sale);
  
  const channelTimeKey = `${sale.channel}-${monthStr}`;
  updateCrossAggregation(aggregation.crossAnalysis.channelByTime, channelTimeKey, sale);
  
  const customerCategoryKey = `${sale.customerSegment}-${sale.category}`;
  updateCrossAggregation(aggregation.crossAnalysis.customerByCategory, customerCategoryKey, sale);
}

// 辅助函数
function updateTimeAggregation(timeGroup, key, sale) {
  if (!timeGroup[key]) {
    timeGroup[key] = {
      period: key,
      revenue: 0,
      orders: 0,
      customers: new Set(),
      averageOrderValue: 0
    };
  }
  
  timeGroup[key].revenue += sale.amount;
  timeGroup[key].orders += 1;
  timeGroup[key].customers.add(sale.customerId);
  timeGroup[key].averageOrderValue = timeGroup[key].revenue / timeGroup[key].orders;
}

function updateDimensionAggregation(dimensionGroup, key, sale) {
  if (!dimensionGroup[key]) {
    dimensionGroup[key] = {
      dimension: key,
      revenue: 0,
      orders: 0,
      customers: new Set(),
      products: new Set(),
      averageOrderValue: 0,
      marketShare: 0
    };
  }
  
  dimensionGroup[key].revenue += sale.amount;
  dimensionGroup[key].orders += 1;
  dimensionGroup[key].customers.add(sale.customerId);
  dimensionGroup[key].products.add(sale.productId);
  dimensionGroup[key].averageOrderValue = dimensionGroup[key].revenue / dimensionGroup[key].orders;
}

function updateCrossAggregation(crossGroup, key, sale) {
  if (!crossGroup[key]) {
    crossGroup[key] = {
      combination: key,
      revenue: 0,
      orders: 0,
      customers: new Set()
    };
  }
  
  crossGroup[key].revenue += sale.amount;
  crossGroup[key].orders += 1;
  crossGroup[key].customers.add(sale.customerId);
}

function getWeekString(date) {
  const year = date.getFullYear();
  const week = getWeekNumber(date);
  return `${year}-W${week.toString().padStart(2, '0')}`;
}

function getWeekNumber(date) {
  const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
  const pastDaysOfYear = (date - firstDayOfYear) / 86400000;
  return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
}

// 计算市场份额等相对指标
const totalRevenue = aggregation.overall.totalRevenue;
for (const [category, data] of Object.entries(aggregation.byDimension.category)) {
  data.marketShare = (data.revenue / totalRevenue * 100).toFixed(2) + '%';
  data.customers = data.customers.size;
  data.products = data.products.size;
}

// 转换 Set 为数量
aggregation.overall.uniqueCustomers = aggregation.overall.uniqueCustomers.size;
aggregation.overall.uniqueProducts = aggregation.overall.uniqueProducts.size;

// 生成最终报告
const report = {
  summary: aggregation.overall,
  trends: {
    daily: Object.values(aggregation.byTimeFrame.daily).map(item => ({
      ...item,
      customers: item.customers.size
    })),
    monthly: Object.values(aggregation.byTimeFrame.monthly).map(item => ({
      ...item,
      customers: item.customers.size
    }))
  },
  analysis: {
    topCategories: Object.values(aggregation.byDimension.category)
      .sort((a, b) => b.revenue - a.revenue)
      .slice(0, 10),
    topRegions: Object.values(aggregation.byDimension.region)
      .sort((a, b) => b.revenue - a.revenue)
      .slice(0, 10)
  },
  insights: generateBusinessInsights(aggregation)
};

function generateBusinessInsights(agg) {
  const insights = [];
  
  // 增长趋势分析
  const monthlyData = Object.values(agg.byTimeFrame.monthly);
  if (monthlyData.length >= 2) {
    const latest = monthlyData[monthlyData.length - 1];
    const previous = monthlyData[monthlyData.length - 2];
    const growth = ((latest.revenue - previous.revenue) / previous.revenue * 100).toFixed(1);
    insights.push(`月度收入${growth > 0 ? '增长' : '下降'} ${Math.abs(growth)}%`);
  }
  
  // 客户分析
  const avgOrderValue = agg.overall.totalRevenue / agg.overall.totalOrders;
  insights.push(`平均订单金额: ¥${avgOrderValue.toFixed(2)}`);
  
  // 地区分析
  const topRegion = Object.values(agg.byDimension.region)
    .sort((a, b) => b.revenue - a.revenue)[0];
  insights.push(`最佳销售地区: ${topRegion.dimension} (占比 ${topRegion.marketShare})`);
  
  return insights;
}

console.log('数据聚合完成:', report.summary);
return [{ json: report }];

🎯 实战案例:CRM数据整合

业务场景

从多个系统(销售系统、客服系统、营销系统)收集客户数据,整合为360度客户画像。

数据整合流程

// Function 节点:客户数据360度整合
const customerProfiles = new Map();

// 处理来自不同系统的数据
for (const item of $input.all()) {
  const data = item.json;
  const customerId = data.customerId;
  const dataSource = data.source;
  
  // 获取或创建客户档案
  if (!customerProfiles.has(customerId)) {
    customerProfiles.set(customerId, {
      id: customerId,
      basicInfo: {},
      salesData: {
        totalOrders: 0,
        totalSpent: 0,
        averageOrderValue: 0,
        firstOrderDate: null,
        lastOrderDate: null,
        favoriteCategories: [],
        preferredChannel: null
      },
      serviceData: {
        totalTickets: 0,
        resolvedTickets: 0,
        avgResolutionTime: 0,
        satisfactionScore: 0,
        commonIssues: []
      },
      marketingData: {
        emailOpens: 0,
        emailClicks: 0,
        campaignResponses: 0,
        unsubscribed: false,
        preferences: []
      },
      behaviorAnalysis: {
        activityLevel: 'unknown',
        riskLevel: 'low',
        lifetimeValue: 0,
        churnProbability: 0,
        nextBestAction: null
      },
      lastUpdated: new Date().toISOString()
    });
  }
  
  const profile = customerProfiles.get(customerId);
  
  // 根据数据源整合不同类型的数据
  switch (dataSource) {
    case 'sales':
      integrateSalesData(profile, data);
      break;
    case 'service':
      integrateServiceData(profile, data);
      break;
    case 'marketing':
      integrateMarketingData(profile, data);
      break;
    case 'crm':
      integrateBasicInfo(profile, data);
      break;
  }
}

// 生成分析和洞察
for (const [customerId, profile] of customerProfiles) {
  analyzeCustomerBehavior(profile);
  calculateLifetimeValue(profile);
  predictChurnRisk(profile);
  recommendNextAction(profile);
}

function integrateSalesData(profile, data) {
  const sales = profile.salesData;
  
  // 基础销售指标
  sales.totalOrders = data.orderCount || 0;
  sales.totalSpent = data.totalAmount || 0;
  sales.averageOrderValue = sales.totalOrders > 0 ? sales.totalSpent / sales.totalOrders : 0;
  sales.firstOrderDate = data.firstOrderDate;
  sales.lastOrderDate = data.lastOrderDate;
  
  // 偏好分析
  if (data.categoryStats) {
    sales.favoriteCategories = Object.entries(data.categoryStats)
      .sort(([,a], [,b]) => b - a)
      .slice(0, 3)
      .map(([category]) => category);
  }
  
  if (data.channelStats) {
    sales.preferredChannel = Object.entries(data.channelStats)
      .sort(([,a], [,b]) => b - a)[0][0];
  }
}

function integrateServiceData(profile, data) {
  const service = profile.serviceData;
  
  service.totalTickets = data.ticketCount || 0;
  service.resolvedTickets = data.resolvedCount || 0;
  service.avgResolutionTime = data.avgResolutionHours || 0;
  service.satisfactionScore = data.avgSatisfactionScore || 0;
  
  if (data.issueCategories) {
    service.commonIssues = Object.entries(data.issueCategories)
      .sort(([,a], [,b]) => b - a)
      .slice(0, 5)
      .map(([issue]) => issue);
  }
}

function integrateMarketingData(profile, data) {
  const marketing = profile.marketingData;
  
  marketing.emailOpens = data.emailOpens || 0;
  marketing.emailClicks = data.emailClicks || 0;
  marketing.campaignResponses = data.campaignResponses || 0;
  marketing.unsubscribed = data.unsubscribed || false;
  marketing.preferences = data.preferences || [];
}

function integrateBasicInfo(profile, data) {
  profile.basicInfo = {
    name: data.name,
    email: data.email,
    phone: data.phone,
    company: data.company,
    title: data.title,
    industry: data.industry,
    region: data.region,
    registrationDate: data.registrationDate,
    tags: data.tags || []
  };
}

function analyzeCustomerBehavior(profile) {
  const sales = profile.salesData;
  const service = profile.serviceData;
  const marketing = profile.marketingData;
  
  // 活跃度分析
  const daysSinceLastOrder = sales.lastOrderDate 
    ? Math.floor((new Date() - new Date(sales.lastOrderDate)) / (1000 * 60 * 60 * 24))
    : Infinity;
  
  if (daysSinceLastOrder <= 30) {
    profile.behaviorAnalysis.activityLevel = 'high';
  } else if (daysSinceLastOrder <= 90) {
    profile.behaviorAnalysis.activityLevel = 'medium';
  } else {
    profile.behaviorAnalysis.activityLevel = 'low';
  }
  
  // 风险等级评估
  let riskScore = 0;
  
  if (daysSinceLastOrder > 180) riskScore += 3;
  else if (daysSinceLastOrder > 90) riskScore += 2;
  else if (daysSinceLastOrder > 30) riskScore += 1;
  
  if (service.satisfactionScore < 3) riskScore += 2;
  if (marketing.unsubscribed) riskScore += 2;
  if (sales.totalOrders === 1) riskScore += 1;
  
  if (riskScore >= 5) profile.behaviorAnalysis.riskLevel = 'high';
  else if (riskScore >= 3) profile.behaviorAnalysis.riskLevel = 'medium';
  else profile.behaviorAnalysis.riskLevel = 'low';
}

function calculateLifetimeValue(profile) {
  const sales = profile.salesData;
  const monthsActive = sales.firstOrderDate 
    ? Math.max(1, Math.floor((new Date() - new Date(sales.firstOrderDate)) / (1000 * 60 * 60 * 24 * 30)))
    : 1;
  
  const monthlyValue = sales.totalSpent / monthsActive;
  const projectedLifetime = 24; // 假设24个月生命周期
  
  profile.behaviorAnalysis.lifetimeValue = monthlyValue * projectedLifetime;
}

function predictChurnRisk(profile) {
  const behavior = profile.behaviorAnalysis;
  const sales = profile.salesData;
  const service = profile.serviceData;
  
  let churnScore = 0;
  
  // 活跃度因子
  if (behavior.activityLevel === 'low') churnScore += 0.4;
  else if (behavior.activityLevel === 'medium') churnScore += 0.2;
  
  // 订单频率因子
  const daysSinceFirst = sales.firstOrderDate 
    ? Math.floor((new Date() - new Date(sales.firstOrderDate)) / (1000 * 60 * 60 * 24))
    : 1;
  const orderFrequency = sales.totalOrders / Math.max(1, daysSinceFirst / 30);
  
  if (orderFrequency < 0.5) churnScore += 0.3;
  
  // 服务满意度因子
  if (service.satisfactionScore < 3) churnScore += 0.2;
  
  // 支持请求频率
  if (service.totalTickets > sales.totalOrders) churnScore += 0.1;
  
  behavior.churnProbability = Math.min(1, churnScore);
}

function recommendNextAction(profile) {
  const behavior = profile.behaviorAnalysis;
  const sales = profile.salesData;
  const service = profile.serviceData;
  
  if (behavior.churnProbability > 0.7) {
    behavior.nextBestAction = {
      action: 'retention_campaign',
      priority: 'high',
      description: '立即启动挽留活动,提供特别优惠'
    };
  } else if (behavior.activityLevel === 'high' && sales.averageOrderValue > 1000) {
    behavior.nextBestAction = {
      action: 'upsell_campaign',
      priority: 'medium',
      description: '推荐高价值产品,增加客单价'
    };
  } else if (service.satisfactionScore < 3) {
    behavior.nextBestAction = {
      action: 'service_improvement',
      priority: 'high',
      description: '关注客户服务体验,主动联系解决问题'
    };
  } else {
    behavior.nextBestAction = {
      action: 'nurture_campaign',
      priority: 'low',
      description: '继续培育关系,定期发送相关内容'
    };
  }
}

// 输出整合后的客户档案
const profiles = Array.from(customerProfiles.values())
  .sort((a, b) => b.behaviorAnalysis.lifetimeValue - a.behaviorAnalysis.lifetimeValue);

console.log(`客户数据整合完成: ${profiles.length} 个客户档案`);

return profiles.map(profile => ({ json: profile }));

🚀 下一步学习

掌握数据转换技巧后,继续学习:

  1. 错误处理 - 处理转换过程中的异常
  2. 性能优化 - 优化大数据转换性能
  3. API集成 - 与外部系统交换数据

数据转换是工作流的核心能力。通过灵活运用各种转换技巧,你能够处理任何复杂的数据场景,构建强大的数据处理管道!