文件
OmniCard/backend/test/modules/crm/attribution.test.ts

83 行
3.3 KiB
TypeScript

import {
RadarAttributionService,
normalizeTrafficChannel,
calculateAttributionFunnel,
ChannelAttributionEvent,
} from '../../../src/modules/crm/service/attribution';
describe('Point 46: 访客雷达多渠道来源归因与转化漏斗分析', () => {
let service: RadarAttributionService;
beforeEach(() => {
service = new RadarAttributionService();
});
describe('1. 流量渠道归一化解析 (normalizeTrafficChannel)', () => {
it('应精准识别 NFC 碰一碰与展位二维码', () => {
expect(normalizeTrafficChannel('nfc', 'tap')).toBe('nfc_tap');
expect(normalizeTrafficChannel('booth', 'qrcode')).toBe('booth_qr');
});
it('应精准识别 WhatsApp 与 LinkedIn 国际社交渠道', () => {
expect(normalizeTrafficChannel('whatsapp', 'im')).toBe('whatsapp_chat');
expect(normalizeTrafficChannel('linkedin', 'social')).toBe('linkedin_share');
expect(normalizeTrafficChannel('email', 'sig')).toBe('email_signature');
});
it('未知或直接访问归入 direct_or_organic', () => {
expect(normalizeTrafficChannel('', '')).toBe('direct_or_organic');
expect(normalizeTrafficChannel(undefined, undefined)).toBe('direct_or_organic');
});
});
describe('2. calculateAttributionFunnel (5阶段留资转化漏斗计算)', () => {
const mockEvents: ChannelAttributionEvent[] = [
// 访客 1: 全链路完成
{ visitorId: 'v1', channel: 'nfc_tap', stage: 'impression' },
{ visitorId: 'v1', channel: 'nfc_tap', stage: 'interactive' },
{ visitorId: 'v1', channel: 'nfc_tap', stage: 'ai_consult' },
{ visitorId: 'v1', channel: 'nfc_tap', stage: 'lead_submitted' },
{ visitorId: 'v1', channel: 'nfc_tap', stage: 'meeting_booked' },
// 访客 2: 停留于 AI 咨询
{ visitorId: 'v2', channel: 'nfc_tap', stage: 'impression' },
{ visitorId: 'v2', channel: 'nfc_tap', stage: 'interactive' },
{ visitorId: 'v2', channel: 'nfc_tap', stage: 'ai_consult' },
// 访客 3: WhatsApp 渠道完成留资
{ visitorId: 'v3', channel: 'whatsapp_chat', stage: 'impression' },
{ visitorId: 'v3', channel: 'whatsapp_chat', stage: 'lead_submitted' },
// 访客 4: 仅浏览展位
{ visitorId: 'v4', channel: 'booth_qr', stage: 'impression' },
];
it('应精确统计各漏斗阶段的独立访客数与流失率', () => {
const funnel = calculateAttributionFunnel(mockEvents);
expect(funnel.totalVisitors).toBe(4);
expect(funnel.stages.impression).toBe(4);
expect(funnel.stages.interactive).toBe(2);
expect(funnel.stages.ai_consult).toBe(2);
expect(funnel.stages.lead_submitted).toBe(2);
expect(funnel.stages.meeting_booked).toBe(1);
// 留资总体转化率: 2 / 4 = 50%
expect(funnel.overallConversionRate).toBe(0.5);
});
it('应支持按渠道拆分多维度漏斗表现', () => {
const breakdown = service.analyzeChannelPerformance(mockEvents);
expect(breakdown['nfc_tap']).toBeDefined();
expect(breakdown['nfc_tap'].visitors).toBe(2);
expect(breakdown['nfc_tap'].leads).toBe(1);
expect(breakdown['nfc_tap'].leadRate).toBe(0.5);
expect(breakdown['whatsapp_chat'].visitors).toBe(1);
expect(breakdown['whatsapp_chat'].leads).toBe(1);
expect(breakdown['whatsapp_chat'].leadRate).toBe(1.0);
});
});
});