数据转换
掌握N8N中的数据转换技巧,灵活处理各种数据格式和结构
数据转换
数据转换是工作流处理的核心技能。在实际应用中,我们经常需要将来自不同系统的数据转换为目标格式,或者对数据进行清洗、聚合、分析等操作。本章将详细介绍 N8N 中的各种数据转换技巧。
🔄 数据转换概述
转换场景分类
转换类型 | 目的 | 常用节点 | 适用场景 |
---|---|---|---|
格式转换 | 改变数据格式 | Set, Function | JSON ↔ 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 }));
🚀 下一步学习
掌握数据转换技巧后,继续学习:
数据转换是工作流的核心能力。通过灵活运用各种转换技巧,你能够处理任何复杂的数据场景,构建强大的数据处理管道!