Files
users/app/api/crowd-pools/route.ts

287 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextRequest, NextResponse } from 'next/server'
import { getMongoClient } from '@/lib/mongodb'
// 项目定义(基于真实数据库)
const PROJECTS = {
ckb: {
id: 'ckb',
name: '存客宝',
database: 'KR_存客宝',
collection: '用户资产统一视图',
color: 'blue',
icon: 'Users'
},
dlm: {
id: 'dlm',
name: '点了码',
database: 'KR_点了码',
collection: '用户资产统一视图',
color: 'green',
icon: 'QrCode'
},
weibo: {
id: 'weibo',
name: '微博',
database: 'KR_微博',
collection: '微博uid+手机',
color: 'orange',
icon: 'Globe'
},
qq: {
id: 'qq',
name: 'QQ社交',
database: 'KR_腾讯',
collection: 'QQ+手机',
color: 'purple',
icon: 'MessageCircle'
}
}
// 获取项目列表及统计
async function getProjectStats() {
const client = await getMongoClient()
const stats = []
for (const [key, project] of Object.entries(PROJECTS)) {
try {
const db = client.db(project.database)
const count = await db.collection(project.collection).estimatedDocumentCount()
stats.push({
...project,
userCount: count,
status: 'active'
})
} catch {
stats.push({
...project,
userCount: 0,
status: 'error'
})
}
}
return stats
}
// 获取项目下的流量池/标签分类
async function getProjectPools(projectId: string) {
const client = await getMongoClient()
const project = PROJECTS[projectId as keyof typeof PROJECTS]
if (!project) {
return { error: '项目不存在' }
}
const db = client.db(project.database)
const collection = db.collection(project.collection)
// 根据项目类型获取不同的分组字段
let pools: any[] = []
if (projectId === 'ckb') {
// 存客宝按流量池分组
const poolStats = await collection.aggregate([
{ $sample: { size: 50000 } },
{ $group: {
_id: '$traffic_pool.pool_name',
count: { $sum: 1 },
avgScore: { $avg: '$user_evaluation_score' }
}},
{ $sort: { count: -1 } }
], { maxTimeMS: 10000 }).toArray()
// 按标签分组
const tagStats = await collection.aggregate([
{ $sample: { size: 50000 } },
{ $unwind: '$unified_tags' },
{ $group: { _id: '$unified_tags', count: { $sum: 1 } }},
{ $sort: { count: -1 } },
{ $limit: 20 }
], { maxTimeMS: 10000 }).toArray()
const total = await collection.estimatedDocumentCount()
const ratio = total / 50000
pools = [
...poolStats.filter(p => p._id).map(p => ({
id: `pool_${p._id}`,
name: p._id,
type: 'pool',
count: Math.round(p.count * ratio),
avgScore: Math.round(p.avgScore || 0)
})),
...tagStats.filter(t => t._id).map(t => ({
id: `tag_${t._id}`,
name: t._id,
type: 'tag',
count: Math.round(t.count * ratio)
}))
]
} else if (projectId === 'dlm') {
// 点了码按角色标签分组
const roleStats = await collection.aggregate([
{ $unwind: { path: '$角色标签', preserveNullAndEmptyArrays: true } },
{ $group: { _id: '$角色标签', count: { $sum: 1 } }},
{ $sort: { count: -1 } }
], { maxTimeMS: 10000 }).toArray()
// 按用户等级分组
const levelStats = await collection.aggregate([
{ $group: { _id: '$用户等级', count: { $sum: 1 } }},
{ $sort: { count: -1 } }
], { maxTimeMS: 10000 }).toArray()
pools = [
...roleStats.filter(r => r._id).map(r => ({
id: `role_${r._id}`,
name: r._id,
type: 'role',
count: r.count
})),
...levelStats.filter(l => l._id).map(l => ({
id: `level_${l._id}`,
name: `${l._id}级用户`,
type: 'level',
count: l.count
}))
]
} else if (projectId === 'qq') {
// QQ按省份分组
const provinceStats = await collection.aggregate([
{ $sample: { size: 100000 } },
{ $match: { '省份': { $exists: true, $ne: null } } },
{ $group: { _id: '$省份', count: { $sum: 1 } }},
{ $sort: { count: -1 } },
{ $limit: 20 }
], { maxTimeMS: 10000 }).toArray()
const total = await collection.estimatedDocumentCount()
const ratio = total / 100000
pools = provinceStats.map(p => ({
id: `province_${p._id}`,
name: p._id,
type: 'province',
count: Math.round(p.count * ratio)
}))
} else if (projectId === 'weibo') {
// 微博简单统计
const total = await collection.estimatedDocumentCount()
pools = [{
id: 'weibo_all',
name: '微博用户',
type: 'all',
count: total
}]
}
return {
project,
pools,
totalPools: pools.length
}
}
// 获取流量池内的用户列表
async function getPoolUsers(projectId: string, poolId: string, page: number = 1, limit: number = 20) {
const client = await getMongoClient()
const project = PROJECTS[projectId as keyof typeof PROJECTS]
if (!project) {
return { error: '项目不存在' }
}
const db = client.db(project.database)
const collection = db.collection(project.collection)
// 解析poolId构建查询条件
let query: any = {}
const [type, ...nameParts] = poolId.split('_')
const name = nameParts.join('_')
console.log('Pool query:', { type, name, projectId, poolId })
if (type === 'pool') {
query['traffic_pool.pool_name'] = name
} else if (type === 'tag') {
query['unified_tags'] = name
} else if (type === 'role') {
query['角色标签'] = name
} else if (type === 'level') {
query['用户等级'] = name.replace('级用户', '')
} else if (type === 'province') {
query['省份'] = name
}
// 如果没有匹配条件尝试直接用name搜索
if (Object.keys(query).length === 0 || type === 'all') {
query = {}
}
const skip = (page - 1) * limit
const [users, total] = await Promise.all([
collection.find(query)
.project({
phone_masked: 1,
name: 1,
'core_profile.name': 1,
user_evaluation_score: 1,
unified_tags: 1,
'角色标签': 1,
'用户等级': 1,
'rfm_scores.user_level': 1,
created_at: 1
})
.skip(skip)
.limit(limit)
.toArray(),
collection.countDocuments(query)
])
return {
users: users.map(u => ({
id: u._id.toString(),
phone: u.phone_masked || '未知',
name: u.name || u.core_profile?.name || '未知用户',
score: u.user_evaluation_score || 0,
level: u.rfm_scores?.user_level || u['用户等级'] || '-',
tags: u.unified_tags || u['角色标签'] || [],
createdAt: u.created_at
})),
total,
page,
totalPages: Math.ceil(total / limit)
}
}
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const action = searchParams.get('action') || 'projects'
const projectId = searchParams.get('projectId')
const poolId = searchParams.get('poolId')
const page = parseInt(searchParams.get('page') || '1')
try {
if (action === 'projects') {
const stats = await getProjectStats()
return NextResponse.json({ success: true, projects: stats })
}
if (action === 'pools' && projectId) {
const pools = await getProjectPools(projectId)
return NextResponse.json({ success: true, ...pools })
}
if (action === 'users' && projectId && poolId) {
const users = await getPoolUsers(projectId, poolId, page)
return NextResponse.json({ success: true, ...users })
}
return NextResponse.json({ error: '无效的操作' }, { status: 400 })
} catch (error) {
console.error('流量池API错误:', error)
return NextResponse.json({ error: '查询失败' }, { status: 500 })
}
}