live-provider-normalization-smoke.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #!/usr/bin/env node
  2. const { normalizeProviderCreators } = require('../mcp/src/features/tihao-sourcing/live-provider');
  3. const payload = {
  4. data: {
  5. data: {
  6. model: {
  7. itemList: [
  8. {
  9. id: 'xhs-real-001',
  10. redId: 'tiya824824',
  11. nickname: '真实探店号',
  12. xiaohongshuUrl: 'https://www.xiaohongshu.com/user/profile/xhs-real-001',
  13. fans_count: '63,666',
  14. picture_price: '¥2,300',
  15. video_price: '2500元',
  16. city: '北京',
  17. attribute_datas: {
  18. image_cpm: 496.01,
  19. video_cpm: 551.09,
  20. image_cpe: 5.82,
  21. video_cpe: 6.09,
  22. commercial_order_count: 41,
  23. business_read_median: 1283,
  24. business_interaction_median: 158,
  25. business_exposure_median_30d: 11381,
  26. fan_growth_rate_30d: '50%',
  27. invite_reply_rate_48h: '98.8%',
  28. is_low_activity: false,
  29. content_theme_labels_180d: ['美食', '出行旅游'],
  30. author_thin_mid_word_association_index: ['探店', '氛围感']
  31. }
  32. },
  33. {
  34. user_id: 'xhs-real-002',
  35. name: '只给嵌套报价号',
  36. fansNum: 2988,
  37. attributeDatas: {
  38. quotation: 1800,
  39. note_cpm: '223.13',
  40. note_cpe: '13.85',
  41. city: '北京'
  42. }
  43. }
  44. ]
  45. }
  46. }
  47. }
  48. };
  49. const creators = normalizeProviderCreators(payload, 'xiaohongshu');
  50. assert(creators.length === 2, 'should normalize provider creator list');
  51. assert(creators[0].displayName === '真实探店号', 'should map display name');
  52. assert(creators[0].platformShortId === 'tiya824824', 'should map xiaohongshu redId as platform short id');
  53. assert(creators[0].fansCount === 63666, 'should parse comma fan count');
  54. assert(creators[0].imagePrice === 2300, 'should parse snake_case picture price');
  55. assert(creators[0].videoPrice === 2500, 'should parse snake_case video price');
  56. assert(creators[0].minPrice === 2300, 'should derive min price from quotes');
  57. assert(creators[0].quoteStatus === '已获取报价', 'should mark quote as fetched');
  58. assert(creators[0].imageCpm === 496.01, 'should parse image CPM');
  59. assert(creators[0].videoCpm === 551.09, 'should parse video CPM');
  60. assert(creators[0].imageCpe === 5.82, 'should parse image CPE');
  61. assert(creators[0].videoCpe === 6.09, 'should parse video CPE');
  62. assert(creators[0].commercialOrderCount === 41, 'should parse commercial order count');
  63. assert(creators[0].commercialReadMedian === 1283, 'should parse business read median');
  64. assert(creators[0].commercialInteractionMedian === 158, 'should parse business interaction median');
  65. assert(creators[0].commercialExposureMedian30d === 11381, 'should parse 30d exposure median');
  66. assert(creators[0].fanGrowthRate30d === '50%', 'should keep fan growth rate');
  67. assert(creators[0].inviteReplyRate48h === '98.8%', 'should keep 48h reply rate');
  68. assert(creators[0].lowActivity === false, 'should parse low activity boolean');
  69. assert(creators[1].minPrice === 1800, 'should parse nested generic quotation as min price');
  70. assert(creators[1].imageCpm === 223.13, 'should map note CPM to image CPM');
  71. assert(creators[1].imageCpe === 13.85, 'should map note CPE to image CPE');
  72. const douyinCreators = normalizeProviderCreators({
  73. data: {
  74. model: {
  75. authors: [
  76. {
  77. sec_uid: 'MS4wLjABAAAA-real-sec-uid',
  78. user_id: '123456789',
  79. nickName: '抖音护肤号',
  80. followerCount: 51071,
  81. task_infos: [{ quote: 1500 }]
  82. }
  83. ]
  84. }
  85. }
  86. }, 'douyin');
  87. assert(douyinCreators.length === 1, 'should normalize douyin authors');
  88. assert(douyinCreators[0].platformUserId === 'MS4wLjABAAAA-real-sec-uid', 'should prefer douyin sec_uid as profile id');
  89. assert(douyinCreators[0].profileUrl === 'https://www.douyin.com/user/MS4wLjABAAAA-real-sec-uid', 'should build douyin profile url');
  90. assert(douyinCreators[0].videoPrice === 1500, 'should parse douyin task quote');
  91. console.log(JSON.stringify({ ok: true, creators: creators.length, douyinCreators: douyinCreators.length }, null, 2));
  92. function assert(condition, message) {
  93. if (!condition) throw new Error(message);
  94. }