chore: 以本地为准,上传全部并替换 GitHub
This commit is contained in:
286
app/api/crowd-pools/route.ts
Normal file
286
app/api/crowd-pools/route.ts
Normal file
@@ -0,0 +1,286 @@
|
||||
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 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user