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); }); }); });