From c178c0e1ef5b2d5d00f19ac30f0ae62e62801ce9 Mon Sep 17 00:00:00 2001 From: wikiboss Date: Mon, 21 Sep 2026 11:14:57 +0800 Subject: [PATCH] =?UTF-8?q?test(crm):=20=E5=AE=8C=E5=96=84=E9=AB=98?= =?UTF-8?q?=E5=81=9C=E7=95=99=E9=9B=B7=E8=BE=BEPush=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E4=B8=8E=E6=94=B6=E4=BB=B6=E7=AE=B1=E5=8D=95=E6=B5=8B=E8=A6=86?= =?UTF-8?q?=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/test/modules/crm/push.test.ts | 11 +++++++ backend/test/modules/crm/radar.test.ts | 44 ++++++++++++++++++++++++++ mpuniapp/common/types.uts | 37 +++++++++++++++++++++- 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/backend/test/modules/crm/push.test.ts b/backend/test/modules/crm/push.test.ts index af89fae..70e9c10 100644 --- a/backend/test/modules/crm/push.test.ts +++ b/backend/test/modules/crm/push.test.ts @@ -1,6 +1,7 @@ import { CrmRadarPushService, RadarPushPayload, + isHotRadarPush, } from '../../../src/modules/crm/service/push'; import { RadarTrack } from '../../../src/comm/types/omni'; @@ -91,6 +92,16 @@ describe('Task 35: 雷达 Push 通知内存渠道 (CrmRadarPushService)', () => expect(cardB[0].visitorId).toBe('vis_b'); }); + it('停留满 3 分钟或浏览 3 个模块视为高意向推送', () => { + expect(isHotRadarPush({ stayDurationMs: 179999, viewedBlocks: ['hero'] })).toBe( + false + ); + expect(isHotRadarPush({ stayDurationMs: 180000, viewedBlocks: [] })).toBe(true); + expect( + isHotRadarPush({ stayDurationMs: 1000, viewedBlocks: ['hero', 'case', 'article'] }) + ).toBe(true); + }); + it('unsubscribe 后不再收到后续 publish', () => { const received: RadarPushPayload[] = []; const listener = (payload: RadarPushPayload) => { diff --git a/backend/test/modules/crm/radar.test.ts b/backend/test/modules/crm/radar.test.ts index 3f0de80..d3a5226 100644 --- a/backend/test/modules/crm/radar.test.ts +++ b/backend/test/modules/crm/radar.test.ts @@ -262,6 +262,50 @@ describe('Task 11: 后端访客雷达模块 (modules/crm) 单元测试', () => { expect(result.country).toBe('Singapore'); expect(result.city).toBe('Singapore'); }); + + it('高停留足迹会走雷达 Push,普通足迹不推', async () => { + const published: any[] = []; + radarService.crmRadarPushService = { + publish: (track: any) => { + published.push(track); + return track; + }, + } as any; + + await radarService.recordTrack({ + cardId: 101, + stayDurationMs: 5000, + viewedBlocks: ['hero'], + }); + await radarService.recordTrack({ + cardId: 101, + stayDurationMs: 180000, + viewedBlocks: ['hero'], + }); + + expect(published).toHaveLength(1); + expect(published[0].stayDurationMs).toBe(180000); + }); + }); + + describe('P2 高意向雷达收件箱', () => { + it('hotInbox 只统计当前用户名片的高意向足迹', async () => { + const radarService = new CrmRadarService(); + radarService.cardInfoEntity = { + find: jest.fn(async () => [{ id: 101, userId: 11 }]), + } as any; + radarService.crmTrackEntity = { + find: jest.fn(async () => [ + { id: 1, cardId: 101, stayDurationMs: 180000, viewedBlocks: [] }, + { id: 2, cardId: 101, stayDurationMs: 1000, viewedBlocks: ['hero'] }, + { id: 3, cardId: 202, stayDurationMs: 200000, viewedBlocks: [] }, + ]), + } as any; + + const inbox = await radarService.hotInbox(11); + expect(inbox.unread).toBe(1); + expect(inbox.list[0].cardId).toBe(101); + }); }); // ========================================== diff --git a/mpuniapp/common/types.uts b/mpuniapp/common/types.uts index 2dcea22..438fe56 100644 --- a/mpuniapp/common/types.uts +++ b/mpuniapp/common/types.uts @@ -87,7 +87,37 @@ export type LeadStatus = 'new' | 'contacted' | 'hot' | 'won' | 'archived'; export type LeadSource = 'nfc' | 'qr' | 'link' | 'ai_chat' | 'exchange'; -export type CardContactSource = 'exchange' | 'collect' | 'scan'; +export type CardContactSource = 'exchange' | 'collect' | 'scan' | 'event'; + +export type CardEventStatus = 'active' | 'ended'; + +export interface CardEvent { + id?: string | number; + code: string; + name: string; + status: CardEventStatus; +} + +export interface CardEventMember { + id?: string | number; + eventId: number; + userId: number; + cardId: number; +} + +export interface CardEventJoinResult { + event: CardEvent; + member: CardEventMember; +} + +export interface TeamRankItem { + memberId: number; + memberName: string; + leads: number; + touches: number; + score: number; + rank: number; +} export interface CardContact { id?: string | number; @@ -147,6 +177,11 @@ export interface RadarTrack { createdAt?: string | Date; } +export interface RadarHotInbox { + unread: number; + list: RadarTrack[]; +} + export interface RecordRadarTrackParams { cardId: string | number; visitorId: string;