/** * 数据源管理 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 = { '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 { 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 }) } }