#!/usr/bin/env node const { normalizeProviderCreators } = require('../mcp/src/features/tihao-sourcing/live-provider'); const payload = { data: { data: { model: { itemList: [ { id: 'xhs-real-001', redId: 'tiya824824', nickname: '真实探店号', xiaohongshuUrl: 'https://www.xiaohongshu.com/user/profile/xhs-real-001', fans_count: '63,666', picture_price: '¥2,300', video_price: '2500元', city: '北京', attribute_datas: { image_cpm: 496.01, video_cpm: 551.09, image_cpe: 5.82, video_cpe: 6.09, commercial_order_count: 41, business_read_median: 1283, business_interaction_median: 158, business_exposure_median_30d: 11381, fan_growth_rate_30d: '50%', invite_reply_rate_48h: '98.8%', is_low_activity: false, content_theme_labels_180d: ['美食', '出行旅游'], author_thin_mid_word_association_index: ['探店', '氛围感'] } }, { user_id: 'xhs-real-002', name: '只给嵌套报价号', fansNum: 2988, attributeDatas: { quotation: 1800, note_cpm: '223.13', note_cpe: '13.85', city: '北京' } } ] } } } }; const creators = normalizeProviderCreators(payload, 'xiaohongshu'); assert(creators.length === 2, 'should normalize provider creator list'); assert(creators[0].displayName === '真实探店号', 'should map display name'); assert(creators[0].platformShortId === 'tiya824824', 'should map xiaohongshu redId as platform short id'); assert(creators[0].fansCount === 63666, 'should parse comma fan count'); assert(creators[0].imagePrice === 2300, 'should parse snake_case picture price'); assert(creators[0].videoPrice === 2500, 'should parse snake_case video price'); assert(creators[0].minPrice === 2300, 'should derive min price from quotes'); assert(creators[0].quoteStatus === '已获取报价', 'should mark quote as fetched'); assert(creators[0].imageCpm === 496.01, 'should parse image CPM'); assert(creators[0].videoCpm === 551.09, 'should parse video CPM'); assert(creators[0].imageCpe === 5.82, 'should parse image CPE'); assert(creators[0].videoCpe === 6.09, 'should parse video CPE'); assert(creators[0].commercialOrderCount === 41, 'should parse commercial order count'); assert(creators[0].commercialReadMedian === 1283, 'should parse business read median'); assert(creators[0].commercialInteractionMedian === 158, 'should parse business interaction median'); assert(creators[0].commercialExposureMedian30d === 11381, 'should parse 30d exposure median'); assert(creators[0].fanGrowthRate30d === '50%', 'should keep fan growth rate'); assert(creators[0].inviteReplyRate48h === '98.8%', 'should keep 48h reply rate'); assert(creators[0].lowActivity === false, 'should parse low activity boolean'); assert(creators[1].minPrice === 1800, 'should parse nested generic quotation as min price'); assert(creators[1].imageCpm === 223.13, 'should map note CPM to image CPM'); assert(creators[1].imageCpe === 13.85, 'should map note CPE to image CPE'); const douyinCreators = normalizeProviderCreators({ data: { model: { authors: [ { sec_uid: 'MS4wLjABAAAA-real-sec-uid', user_id: '123456789', nickName: '抖音护肤号', followerCount: 51071, task_infos: [{ quote: 1500 }] } ] } } }, 'douyin'); assert(douyinCreators.length === 1, 'should normalize douyin authors'); assert(douyinCreators[0].platformUserId === 'MS4wLjABAAAA-real-sec-uid', 'should prefer douyin sec_uid as profile id'); assert(douyinCreators[0].profileUrl === 'https://www.douyin.com/user/MS4wLjABAAAA-real-sec-uid', 'should build douyin profile url'); assert(douyinCreators[0].videoPrice === 1500, 'should parse douyin task quote'); console.log(JSON.stringify({ ok: true, creators: creators.length, douyinCreators: douyinCreators.length }, null, 2)); function assert(condition, message) { if (!condition) throw new Error(message); }