| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- // 蒲公英博主字段 schema(结构化自《field_dictionary.md》)
- // 边界:仅沉淀字段定义 + 建表参考 + 选号查询映射,供未来建库/开发查询接口时直接消费,不在此建库。
- // 源码映射:hc_pgy_spider/pgy_field_map.py;采集接口共 9 个详情 + 1 个列表。
- // 采集接口(endpointId 供未来查询接口注册使用)
- const SOURCES = {
- list: { name: '列表 blogger/v2', method: 'POST', path: 'xiaohongshu-pgy/api/solar/cooperator/blogger/v2', priority: 3 },
- profile: { name: '个人简介', method: 'GET', path: 'cooperator/user/blogger/{id}', priority: 2 },
- notesRate: { name: '数据概括 notesRate?noteType=3', method: 'GET', path: 'kol/dataV3/notesRate', priority: 2 },
- noteData: { name: '笔记数据 notesRate×3', method: 'GET', path: 'kol/dataV3/notesRate', priority: 1 },
- notesDetail: { name: '笔记案例 notesDetail×3', method: 'GET', path: 'kol/dataV2/notesDetail', priority: 1 },
- fansSummary: { name: '粉丝概览 fansSummary', method: 'GET', path: 'kol/dataV3/fansSummary', priority: 2 },
- fansProfile: { name: '粉丝画像 fans_profile', method: 'GET', path: 'kol/data/{id}/fans_profile', priority: 1 },
- dataSummary: { name: '数据表现总览 data_summary', method: 'GET', path: 'kol/data_v3/data_summary', priority: 1 },
- costEffective: { name: '流量性价比 costEffective', method: 'GET', path: 'kol/dataV2/costEffective', priority: 0 }
- };
- // 选号查询维度 → 蒲公英字段 + MongoDB 查询示例
- // 这是未来「选号时开发接口查询」的核心映射:rank/platform-quality-rules 的每个维度对应哪些字段、怎么查。
- const DIMENSIONS = {
- fanRange: {
- zh: '粉丝范围',
- fields: ['follower_count'],
- type: 'number',
- unit: '人',
- query: { follower_count: { $gte: 50000, $lte: 500000 } },
- note: '腰部 KOL 5万-50万;对应 ranker.fanRange 与 platform-quality-rules 分级'
- },
- interaction: {
- zh: '互动率/互动中位数',
- fields: ['interaction_rate', 'interact_midnum', 'inter_midnum_30d_total'],
- type: 'number',
- unit: '% / 次',
- query: { interaction_rate: { $gte: 3 } },
- note: '小红书互动率 ≥3% 优秀、≥1% 合格;抖音 ≥3% 优秀、≥1.5% 合格'
- },
- price: {
- zh: '报价(必采)',
- fields: ['picture_price', 'video_price', 'lower_price'],
- type: 'number',
- unit: '元',
- query: { lower_price: { $lte: 3000 } },
- note: '⭐ 列表接口只采这 3 个报价字段'
- },
- verticality: {
- zh: '垂直度/类目',
- fields: ['trade_type', 'content_tags', 'feature_tags', 'note_type_share'],
- type: 'string/array',
- query: { trade_type: '美妆护肤', content_tags: { $all: ['成分党'] } },
- note: '小红书垂直度 ≥70% 合格、≥90% 优秀'
- },
- score: {
- zh: '评分/信用/等级',
- fields: ['credit_level', 'goodness_level', 'level_name'],
- type: 'string',
- query: { credit_level: { $in: ['Lv5', 'Lv6', 'Lv7'] } },
- note: '蒲公英评分 ≥4.0 合格、≥4.5 优秀、<3.5 暂不考虑'
- },
- location: {
- zh: '所在地/城市',
- fields: ['location'],
- type: 'string',
- query: { location: { $regex: '上海' } },
- note: '地域定向'
- },
- gender: {
- zh: '性别',
- fields: ['gender'],
- type: 'int',
- query: { gender: 1 },
- note: '1=女 2=男'
- },
- fansProfile: {
- zh: '粉丝画像(年龄/性别/城市)',
- fields: ['fans_age_dist', 'fans_gender_dist', 'fans_city_top'],
- type: 'array',
- query: { 'fans_age_dist': { $elemMatch: { age: '18-24', rate: { $gte: 40 } } } },
- note: '粉丝画像匹配度 ≥60% 合格、≥80% 优秀'
- },
- completionRate: {
- zh: '视频完播率',
- fields: ['video_full_view_rate', 'video_finish_rate'],
- type: 'number',
- unit: '%',
- query: { video_full_view_rate: { $gte: 20 } },
- note: '抖音完播率 ≥20% 合格、≥40% 优秀、<10% 危险'
- },
- likeCollectRatio: {
- zh: '赞藏比(收藏/点赞)',
- fields: ['like_median', 'collect_median'],
- type: 'number',
- query: { $expr: { $gte: [{ $divide: ['$collect_median', '$like_median'] }, 0.5] } },
- note: '小红书赞藏比 ≥0.5 合格、≥1.0 优秀'
- },
- cpm: {
- zh: 'CPM/CPE/阅读成本',
- fields: ['estimate_picture_cpm', 'estimate_video_cpm', 'estimate_picture_engage_cost', 'estimate_video_engage_cost'],
- type: 'number',
- unit: '元',
- query: { estimate_video_cpm: { $lte: 80 } },
- note: '抖音腰部 CPM 40-80、CPE 5-10'
- },
- growth: {
- zh: '粉丝增长',
- fields: ['fans_growth_rate', 'fans_30d_growth_rate', 'fans_30d_growth_num'],
- type: 'number',
- unit: '%',
- query: { fans_30d_growth_rate: { $gte: 0 } },
- note: '负增长 = 账号健康度风险(platform-quality-rules 已接入)'
- },
- activity: {
- zh: '活跃度',
- fields: ['active_days_in_last7', 'is_active', 'last_active_time'],
- type: 'mixed',
- query: { is_active: true, active_days_in_last7: { $gte: 3 } },
- note: '近30天无更新 = 数据真实性风险'
- },
- verified: {
- zh: '认证/合作权限',
- fields: ['verified', 'is_pgy_creator', 'has_buyer_auth', 'cooperate_state'],
- type: 'bool',
- query: { is_pgy_creator: true, has_buyer_auth: true },
- note: '蒲公英创作者 + 品牌合作权限是下单前提'
- }
- };
- // 核心字段表(字段名 → 中文/类型/来源接口/选号维度)
- const CORE_FIELDS = {
- // 顶级必采
- blogger_id: { zh: '博主ID', type: 'string', sources: ['list', 'profile'], dimension: 'id', primary: true },
- nickname: { zh: '昵称', type: 'string', sources: ['list', 'profile'], dimension: 'id' },
- follower_count: { zh: '粉丝数', type: 'int', sources: ['list', 'profile'], dimension: 'fanRange' },
- like_count: { zh: '获赞数', type: 'int', sources: ['list', 'profile'], dimension: 'interaction' },
- post_count: { zh: '笔记数', type: 'int', sources: ['list'], dimension: 'activity' },
- co_note_count: { zh: '合作笔记数', type: 'int', sources: ['list'], dimension: 'activity' },
- verified: { zh: '是否认证', type: 'bool', sources: ['list', 'profile'], dimension: 'verified' },
- tags: { zh: '个人标签', type: 'array', sources: ['list', 'profile'], dimension: 'verticality' },
- content_tags: { zh: '内容标签', type: 'array', sources: ['list'], dimension: 'verticality' },
- feature_tags: { zh: '特色标签', type: 'array', sources: ['list'], dimension: 'verticality' },
- trade_type: { zh: '行业', type: 'string', sources: ['list'], dimension: 'verticality' },
- gender: { zh: '性别', type: 'int', sources: ['list', 'profile'], dimension: 'gender' },
- location: { zh: '所在地', type: 'string', sources: ['list', 'profile'], dimension: 'location' },
- // 报价(必采)
- picture_price: { zh: '图文报价', type: 'number', sources: ['list'], dimension: 'price', priority: 3 },
- video_price: { zh: '视频报价', type: 'number', sources: ['list'], dimension: 'price', priority: 3 },
- lower_price: { zh: '报价下限', type: 'number', sources: ['list'], dimension: 'price', priority: 3 },
- // 互动/数据
- interaction_rate: { zh: '互动率', type: 'number', sources: ['notesRate'], dimension: 'interaction', priority: 2 },
- interact_midnum: { zh: '互动中位数', type: 'number', sources: ['list'], dimension: 'interaction' },
- read_midnum: { zh: '阅读中位数', type: 'number', sources: ['list'], dimension: 'cpm' },
- like_median: { zh: '中位点赞量', type: 'number', sources: ['notesRate'], dimension: 'likeCollectRatio' },
- collect_median: { zh: '中位收藏量', type: 'number', sources: ['notesRate'], dimension: 'likeCollectRatio' },
- video_full_view_rate: { zh: '视频完播率', type: 'number', sources: ['notesRate'], dimension: 'completionRate' },
- hundred_like_rate: { zh: '百赞笔记比例', type: 'number', sources: ['notesRate'], dimension: 'interaction' },
- kilo_like_rate: { zh: '千赞笔记比例', type: 'number', sources: ['notesRate'], dimension: 'interaction' },
- // 粉丝增长/画像
- fans_growth_rate: { zh: '30天增粉率', type: 'number', sources: ['fansSummary'], dimension: 'growth' },
- fans_30d_growth_rate: { zh: '30天粉丝增长率', type: 'number', sources: ['list', 'dataSummary'], dimension: 'growth' },
- fans_active_l28_rate: { zh: '28天活跃粉丝占比', type: 'number', sources: ['list'], dimension: 'activity' },
- fans_engage_30d_rate: { zh: '30天互动率', type: 'number', sources: ['list'], dimension: 'interaction' },
- fans_age_dist: { zh: '粉丝年龄分布', type: 'array', sources: ['fansProfile'], dimension: 'fansProfile' },
- fans_gender_dist: { zh: '粉丝性别分布', type: 'array', sources: ['fansProfile'], dimension: 'fansProfile' },
- fans_city_top: { zh: '粉丝城市TOP', type: 'array', sources: ['fansProfile'], dimension: 'fansProfile' },
- // 成本/CPM
- estimate_picture_cpm: { zh: '估算图文CPM', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
- estimate_video_cpm: { zh: '估算视频CPM', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
- estimate_picture_engage_cost: { zh: '估算图文互动成本', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
- estimate_video_engage_cost: { zh: '估算视频互动成本', type: 'number', sources: ['list', 'costEffective'], dimension: 'cpm' },
- // 信用/合作
- credit_level: { zh: '信用等级', type: 'string', sources: ['profile'], dimension: 'score' },
- goodness_level: { zh: '诚信等级', type: 'string', sources: ['profile'], dimension: 'score' },
- level_name: { zh: '等级名称', type: 'string', sources: ['profile'], dimension: 'score' },
- is_pgy_creator: { zh: '蒲公英创作者', type: 'bool', sources: ['profile'], dimension: 'verified' },
- has_buyer_auth: { zh: '品牌合作权限', type: 'bool', sources: ['list'], dimension: 'verified' },
- cooperate_state: { zh: '合作状态', type: 'string', sources: ['list'], dimension: 'verified' },
- active_days_in_last7: { zh: '近7天活跃天数', type: 'int', sources: ['dataSummary'], dimension: 'activity' },
- is_active: { zh: '是否活跃', type: 'bool', sources: ['dataSummary'], dimension: 'activity' },
- last_active_time: { zh: '最后活跃时间', type: 'string', sources: ['profile'], dimension: 'activity' },
- response_rate: { zh: '回复率', type: 'number', sources: ['dataSummary'], dimension: 'activity' }
- };
- // 建议索引(选号查询热路径)
- const INDEXES = [
- { name: 'uk_blogger_id', spec: { blogger_id: 1 }, options: { unique: true } },
- { name: 'idx_fans_desc', spec: { follower_count: -1 }, options: {} },
- { name: 'idx_trade_fans', spec: { trade_type: 1, follower_count: -1 }, options: {} },
- { name: 'idx_interaction_desc', spec: { interaction_rate: -1 }, options: {} },
- { name: 'idx_price_asc', spec: { lower_price: 1 }, options: {} },
- { name: 'idx_tags', spec: { content_tags: 1, feature_tags: 1 }, options: {} },
- { name: 'idx_location', spec: { location: 1 }, options: {} },
- { name: 'idx_active', spec: { is_active: 1, active_days_in_last7: -1 }, options: {} },
- { name: 'idx_cpm_asc', spec: { estimate_video_cpm: 1, estimate_picture_cpm: 1 }, options: {} }
- ];
- // 生成 MongoDB 建表 + 索引 DDL 文本(未来建库直接抄)
- function buildDdl(collectionName = 'pgy_blogger') {
- const lines = [];
- lines.push('// 蒲公英博主 collection(MongoDB)—— 由 pgy-schema 生成,未来建库直接执行');
- lines.push(`db.createCollection('${collectionName}');`);
- lines.push('');
- for (const index of INDEXES) {
- lines.push(`db.${collectionName}.createIndex(${JSON.stringify(index.spec)}, ${JSON.stringify({ name: index.name, ...index.options })});`);
- }
- lines.push('');
- lines.push(`// 必采报价字段(列表接口)示例文档:`);
- lines.push('// { blogger_id, nickname, follower_count, picture_price, video_price, lower_price, trade_type, content_tags }');
- return lines.join('\n');
- }
- // 生成选号查询映射 markdown(未来接口开发参考)
- function buildQueryMap() {
- const lines = [];
- lines.push('# 蒲公英选号查询映射');
- lines.push('');
- lines.push('> 由 pgy-schema 生成。选号 SOP 的每个筛选维度 → 蒲公英字段 → MongoDB 查询。');
- lines.push('');
- lines.push('| 选号维度 | 蒲公英字段 | 查询示例 |');
- lines.push('| --- | --- | --- |');
- for (const [key, dim] of Object.entries(DIMENSIONS)) {
- lines.push(`| ${dim.zh} | ${dim.fields.join('、')} | \`${JSON.stringify(dim.query)}\` |`);
- }
- lines.push('');
- lines.push('## 采集接口优先级');
- lines.push('');
- lines.push('| 优先级 | 接口 | 核心字段 |');
- lines.push('| --- | --- | --- |');
- const priorityMap = { 3: '⭐⭐⭐ 必采', 2: '⭐⭐ 高优先', 1: '⭐ 中优先', 0: '低优先' };
- for (const [key, source] of Object.entries(SOURCES)) {
- lines.push(`| ${priorityMap[source.priority] || '-'} | ${source.name}(\`${source.method} ${source.path}\`) | ${fieldsBySource(key).join('、') || '-'} |`);
- }
- lines.push('');
- lines.push('> 接口总数:9 个详情 + 1 个列表 = 10 个接口。');
- return lines.join('\n');
- }
- function fieldsBySource(sourceKey) {
- return Object.entries(CORE_FIELDS)
- .filter(([, field]) => field.sources.includes(sourceKey))
- .map(([name]) => name)
- .slice(0, 12);
- }
- module.exports = {
- SOURCES,
- DIMENSIONS,
- CORE_FIELDS,
- INDEXES,
- buildDdl,
- buildQueryMap
- };
|