chore: 以本地为准,上传全部并替换 GitHub

This commit is contained in:
卡若
2026-02-03 11:36:53 +08:00
parent 1219166526
commit b404bf546e
131 changed files with 37618 additions and 3930 deletions

View File

@@ -0,0 +1,297 @@
/**
* 数据源管理 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 })
}
}