Files
users/app/api/data-sources/route.ts

298 lines
10 KiB
TypeScript
Raw Permalink 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.

/**
* 数据源管理 API
* 提供数据源列表、连接状态检测、同步管理等功能
*/
import { NextRequest, NextResponse } from 'next/server'
import { getMongoClient, getDatabaseStats } from '@/lib/mongodb'
// 数据源接口定义
interface DataSource {
id: string
name: string
nameCn: string // 中文名称
description: string // 功能描述
type: 'mongodb' | 'mysql' | 'api' | 'webhook'
status: 'connected' | 'disconnected' | 'warning'
host?: string
database?: string
endpoint?: string
lastSync: string
recordCount: number
syncFrequency: string
collections?: number
tables?: number
latency?: number
dataCategory?: string // 数据分类
targetCollection?: string // 目标集合(数据中台分配)
}
// 数据库中文名称和描述映射
const DB_DESCRIPTIONS: Record<string, { nameCn: string; description: string; category: string }> = {
'KR': { nameCn: '核心用户库', description: '用户估值、RFM评分、统一画像', category: '用户画像' },
'KR_KR': { nameCn: '扩展用户库', description: '多源用户数据汇聚', category: '用户数据' },
'KR_Linkedln': { nameCn: '领英数据库', description: '职业社交平台用户数据', category: '社交数据' },
'KR_京东': { nameCn: '京东用户库', description: '京东电商用户消费数据', category: '电商数据' },
'KR_人才库': { nameCn: '人才数据库', description: '招聘平台人才信息', category: '人力资源' },
'KR_企业': { nameCn: '企业信息库', description: '企业工商注册信息', category: '企业数据' },
'KR_企业名录': { nameCn: '企业名录', description: '企业联系方式和经营信息', category: '企业数据' },
'KR_卡若私域': { nameCn: '卡若私域库', description: '私域运营用户数据', category: '私域数据' },
'KR_商城': { nameCn: '商城用户库', description: '电商平台用户交易数据', category: '电商数据' },
'KR_国外': { nameCn: '海外用户库', description: '海外平台用户数据', category: '国际数据' },
'KR_存客宝': { nameCn: '存客宝CRM', description: '私域CRM用户资产数据', category: '私域数据' },
'KR_存客宝_四表重构KR_KR版': { nameCn: '存客宝重构版', description: '存客宝数据统一重构', category: '私域数据' },
'KR_微博': { nameCn: '微博用户库', description: '微博UID与手机号关联', category: '社交数据' },
'KR_快递': { nameCn: '快递信息库', description: '快递收发地址信息', category: '物流数据' },
'KR_户口': { nameCn: '户籍信息库', description: '户籍地址信息', category: '身份数据' },
'KR_手机': { nameCn: '手机号库', description: '手机号归属地和运营商', category: '基础数据' },
'KR_投资': { nameCn: '投资信息库', description: '投资理财用户数据', category: '金融数据' },
'KR_淘宝': { nameCn: '淘宝用户库', description: '淘宝电商用户数据', category: '电商数据' },
'KR_游戏': { nameCn: '游戏用户库', description: '游戏平台用户数据', category: '娱乐数据' },
'KR_点了码': { nameCn: '点了码商户库', description: '点了码商户和用户统一视图', category: '商业数据' },
'KR_腾讯': { nameCn: '腾讯社交库', description: 'QQ号与手机号关联数据', category: '社交数据' },
'KR_酒店': { nameCn: '酒店住宿库', description: '酒店入住记录信息', category: '消费数据' },
'KR_顺丰': { nameCn: '顺丰快递库', description: '顺丰快递收发信息', category: '物流数据' },
'KR_魔兽世界': { nameCn: '魔兽世界库', description: '魔兽世界玩家数据', category: '游戏数据' },
}
// 获取 MongoDB 数据源列表
async function getMongoDBSources(): Promise<DataSource[]> {
try {
const client = await getMongoClient()
const dbList = await client.db().admin().listDatabases()
const sources: DataSource[] = []
for (const dbInfo of dbList.databases) {
if (dbInfo.name.startsWith('KR')) {
const db = client.db(dbInfo.name)
const collections = await db.listCollections().toArray()
let totalDocs = 0
for (const coll of collections) {
const count = await db.collection(coll.name).estimatedDocumentCount()
totalDocs += count
}
// 获取中文名称和描述
const dbMeta = DB_DESCRIPTIONS[dbInfo.name] || {
nameCn: dbInfo.name,
description: `${dbInfo.name} 数据库`,
category: '其他'
}
sources.push({
id: `mongo_${dbInfo.name}`,
name: dbInfo.name,
nameCn: dbMeta.nameCn,
description: dbMeta.description,
type: 'mongodb',
status: 'connected',
host: process.env.MONGODB_URI?.split('@')[1]?.split('/')[0] || 'localhost:27017',
database: dbInfo.name,
lastSync: '实时',
recordCount: totalDocs,
syncFrequency: '实时',
collections: collections.length,
latency: 0,
dataCategory: dbMeta.category,
targetCollection: 'KR.用户估值' // 默认目标集合
})
}
}
return sources
} catch (error) {
console.error('获取 MongoDB 数据源失败:', error)
return []
}
}
// 检测数据源连接状态
async function checkConnectionStatus(): Promise<{
mongodb: { connected: boolean; latency: number }
mysql: { connected: boolean; latency: number }
}> {
const startTime = Date.now()
let mongoConnected = false
let mongoLatency = 0
try {
const client = await getMongoClient()
await client.db().admin().ping()
mongoConnected = true
mongoLatency = Date.now() - startTime
} catch {
mongoLatency = Date.now() - startTime
}
return {
mongodb: { connected: mongoConnected, latency: mongoLatency },
mysql: { connected: false, latency: 0 } // MySQL 暂未实现
}
}
// GET: 获取数据源列表
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const action = searchParams.get('action')
try {
// 检查连接状态
if (action === 'status') {
const status = await checkConnectionStatus()
return NextResponse.json({
success: true,
status
})
}
// 获取数据库统计
if (action === 'stats') {
const stats = await getDatabaseStats()
return NextResponse.json({
success: true,
stats
})
}
// 获取完整数据源列表
const mongoSources = await getMongoDBSources()
// 计算统计信息
const stats = await getDatabaseStats()
// 预定义的外部数据源MySQL、API 等)
const externalSources: DataSource[] = [
{
id: 'mysql_ckb',
name: 'cunkebao_v3',
nameCn: '存客宝MySQL主库',
description: '存客宝CRM系统MySQL数据库包含客户、订单、营销数据',
type: 'mysql',
status: 'warning',
host: 'cdb-xxx.gz.tencentcdb.com:10050',
database: 'cunkebao_v3',
lastSync: '需要配置连接',
recordCount: 0,
syncFrequency: '增量同步',
tables: 45,
dataCategory: '私域数据',
targetCollection: 'KR_存客宝.用户资产统一视图'
},
{
id: 'mysql_dlm',
name: 'dianlema',
nameCn: '点了码MySQL库',
description: '点了码商户系统,包含商户、用户、交易数据',
type: 'mysql',
status: 'warning',
host: 'cdb-xxx.gz.tencentcdb.com:14413',
database: 'dianlema',
lastSync: '需要配置连接',
recordCount: 0,
syncFrequency: '增量同步',
tables: 28,
dataCategory: '商业数据',
targetCollection: 'KR_点了码.用户资产统一视图'
},
{
id: 'api_weibo',
name: 'weibo_api',
nameCn: '微博开放API',
description: '微博开放平台API获取用户公开信息和热点数据',
type: 'api',
status: 'disconnected',
endpoint: 'https://api.weibo.com/2',
lastSync: '未配置',
recordCount: 0,
syncFrequency: '按需调用',
dataCategory: '社交数据',
targetCollection: 'KR_微博.微博uid+手机'
},
{
id: 'webhook_feishu',
name: 'feishu_webhook',
nameCn: '飞书机器人',
description: '飞书机器人Webhook接收对话消息并触发查询',
type: 'webhook',
status: 'connected',
endpoint: '/api/feishu/webhook',
lastSync: '实时',
recordCount: 0,
syncFrequency: '实时',
dataCategory: '消息通道'
}
]
const allSources = [...mongoSources, ...externalSources]
return NextResponse.json({
success: true,
sources: allSources,
summary: {
total: allSources.length,
connected: allSources.filter(s => s.status === 'connected').length,
warning: allSources.filter(s => s.status === 'warning').length,
disconnected: allSources.filter(s => s.status === 'disconnected').length,
totalRecords: stats.totalDocuments,
totalSize: stats.totalSize,
latency: stats.latency
}
})
} catch (error: any) {
console.error('数据源 API 错误:', error)
return NextResponse.json({
success: false,
error: error.message,
sources: [],
summary: {
total: 0,
connected: 0,
warning: 0,
disconnected: 0,
totalRecords: 0,
totalSize: 0,
latency: 0
}
}, { status: 500 })
}
}
// POST: 测试数据源连接
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { type, config } = body
if (type === 'mongodb') {
const { MongoClient } = await import('mongodb')
const uri = config.uri || `mongodb://${config.username}:${config.password}@${config.host}/?authSource=admin`
const client = new MongoClient(uri, {
serverSelectionTimeoutMS: 5000
})
await client.connect()
await client.db().admin().ping()
await client.close()
return NextResponse.json({
success: true,
message: 'MongoDB 连接成功'
})
}
// 其他数据源类型暂未实现
return NextResponse.json({
success: false,
message: `${type} 类型暂未支持`
}, { status: 400 })
} catch (error: any) {
return NextResponse.json({
success: false,
error: error.message
}, { status: 500 })
}
}