chore: 以本地为准,上传全部并替换 GitHub
This commit is contained in:
@@ -1,25 +1,195 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { getDatabases, getDatabaseStructure } from "@/lib/mongodb-mock-connector" // 更新导入路径
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getMongoClient } from '@/lib/mongodb'
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const database = searchParams.get("database")
|
||||
|
||||
if (database) {
|
||||
// 获取指定数据库的结构
|
||||
const structure = await getDatabaseStructure(database)
|
||||
return NextResponse.json({ success: true, data: structure })
|
||||
} else {
|
||||
// 获取所有数据库列表
|
||||
const databases = await getDatabases()
|
||||
return NextResponse.json({ success: true, data: databases })
|
||||
// 获取所有数据库结构
|
||||
async function getDatabaseStructure() {
|
||||
const client = await getMongoClient()
|
||||
const admin = client.db().admin()
|
||||
|
||||
// 获取数据库列表
|
||||
const dbList = await admin.listDatabases()
|
||||
const krDatabases = dbList.databases.filter(db => db.name.startsWith('KR'))
|
||||
|
||||
const structure = []
|
||||
|
||||
for (const dbInfo of krDatabases.slice(0, 10)) { // 限制前10个
|
||||
try {
|
||||
const db = client.db(dbInfo.name)
|
||||
const collections = await db.listCollections().toArray()
|
||||
|
||||
// 获取每个集合的字段示例
|
||||
const collectionDetails = []
|
||||
for (const col of collections.slice(0, 5)) { // 每个库最多5个集合
|
||||
try {
|
||||
const sample = await db.collection(col.name).findOne()
|
||||
const fields = sample ? Object.keys(sample).filter(k => k !== '_id').slice(0, 10) : []
|
||||
const count = await db.collection(col.name).estimatedDocumentCount()
|
||||
|
||||
collectionDetails.push({
|
||||
name: col.name,
|
||||
fields,
|
||||
count
|
||||
})
|
||||
} catch {
|
||||
// 忽略错误
|
||||
}
|
||||
}
|
||||
|
||||
structure.push({
|
||||
database: dbInfo.name,
|
||||
sizeGB: (dbInfo.sizeOnDisk / 1024 / 1024 / 1024).toFixed(2),
|
||||
collections: collectionDetails
|
||||
})
|
||||
} catch {
|
||||
// 忽略错误
|
||||
}
|
||||
}
|
||||
|
||||
return structure
|
||||
}
|
||||
|
||||
// 生成血缘节点
|
||||
function generateLineageNodes(structure: any[]) {
|
||||
const nodes: any[] = []
|
||||
const connections: any[] = []
|
||||
let yOffset = 50
|
||||
|
||||
// 数据源节点 (左侧)
|
||||
const colors = [
|
||||
'from-blue-400 to-blue-600',
|
||||
'from-orange-400 to-orange-600',
|
||||
'from-green-400 to-green-600',
|
||||
'from-red-400 to-red-600',
|
||||
'from-purple-400 to-purple-600',
|
||||
]
|
||||
|
||||
structure.forEach((db, i) => {
|
||||
const mainCol = db.collections[0]
|
||||
if (!mainCol) return
|
||||
|
||||
nodes.push({
|
||||
id: `source_${db.database}`,
|
||||
type: 'source',
|
||||
name: db.database.replace('KR_', ''),
|
||||
database: db.database,
|
||||
collection: mainCol.name,
|
||||
fields: mainCol.fields.slice(0, 5),
|
||||
x: 50,
|
||||
y: yOffset,
|
||||
color: colors[i % colors.length],
|
||||
count: mainCol.count,
|
||||
sizeGB: db.sizeGB
|
||||
})
|
||||
yOffset += 140
|
||||
})
|
||||
|
||||
// AI引擎节点 (中间)
|
||||
nodes.push({
|
||||
id: 'transform_ai',
|
||||
type: 'transform',
|
||||
name: 'AI标签引擎',
|
||||
fields: ['phone_norm', 'qq_norm', 'rfm_score', 'user_level', 'tags'],
|
||||
x: 400,
|
||||
y: 150,
|
||||
color: 'from-violet-400 to-violet-600'
|
||||
})
|
||||
|
||||
nodes.push({
|
||||
id: 'transform_clean',
|
||||
type: 'transform',
|
||||
name: '数据清洗',
|
||||
fields: ['unique_id', 'merged_data', 'quality'],
|
||||
x: 400,
|
||||
y: 350,
|
||||
color: 'from-yellow-400 to-yellow-600'
|
||||
})
|
||||
|
||||
// 目标节点 (右侧)
|
||||
nodes.push({
|
||||
id: 'target_valuation',
|
||||
type: 'target',
|
||||
name: '用户估值',
|
||||
database: 'KR',
|
||||
collection: '用户估值',
|
||||
fields: ['phone', 'qq', 'rfm_score', 'user_level', 'tags'],
|
||||
x: 750,
|
||||
y: 200,
|
||||
color: 'from-emerald-400 to-emerald-600'
|
||||
})
|
||||
|
||||
nodes.push({
|
||||
id: 'target_portrait',
|
||||
type: 'target',
|
||||
name: '用户画像',
|
||||
database: 'KR',
|
||||
collection: '用户画像',
|
||||
fields: ['user_id', 'portrait', 'behavior'],
|
||||
x: 750,
|
||||
y: 400,
|
||||
color: 'from-cyan-400 to-cyan-600'
|
||||
})
|
||||
|
||||
// 自动生成连接
|
||||
structure.forEach(db => {
|
||||
const sourceId = `source_${db.database}`
|
||||
const mainCol = db.collections[0]
|
||||
if (!mainCol) return
|
||||
|
||||
// 连接到AI引擎
|
||||
if (mainCol.fields.includes('phone') || mainCol.fields.includes('手机')) {
|
||||
connections.push({
|
||||
id: `conn_${sourceId}_phone`,
|
||||
sourceNode: sourceId,
|
||||
sourceField: mainCol.fields.includes('phone') ? 'phone' : '手机',
|
||||
targetNode: 'transform_ai',
|
||||
targetField: 'phone_norm'
|
||||
})
|
||||
}
|
||||
if (mainCol.fields.includes('qq') || mainCol.fields.includes('QQ')) {
|
||||
connections.push({
|
||||
id: `conn_${sourceId}_qq`,
|
||||
sourceNode: sourceId,
|
||||
sourceField: mainCol.fields.includes('qq') ? 'qq' : 'QQ',
|
||||
targetNode: 'transform_ai',
|
||||
targetField: 'qq_norm'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// AI引擎到目标
|
||||
connections.push({
|
||||
id: 'conn_ai_valuation',
|
||||
sourceNode: 'transform_ai',
|
||||
sourceField: 'rfm_score',
|
||||
targetNode: 'target_valuation',
|
||||
targetField: 'rfm_score'
|
||||
})
|
||||
connections.push({
|
||||
id: 'conn_clean_portrait',
|
||||
sourceNode: 'transform_clean',
|
||||
sourceField: 'merged_data',
|
||||
targetNode: 'target_portrait',
|
||||
targetField: 'portrait'
|
||||
})
|
||||
|
||||
return { nodes, connections }
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const action = searchParams.get('action') || 'structure'
|
||||
|
||||
try {
|
||||
const structure = await getDatabaseStructure()
|
||||
|
||||
if (action === 'lineage') {
|
||||
const lineage = generateLineageNodes(structure)
|
||||
return NextResponse.json({ success: true, ...lineage })
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true, databases: structure })
|
||||
} catch (error) {
|
||||
console.error("数据库结构查询失败:", error)
|
||||
return NextResponse.json(
|
||||
{ success: false, message: "数据库结构查询失败", error: (error as Error).message },
|
||||
{ status: 500 },
|
||||
)
|
||||
console.error('数据库结构查询失败:', error)
|
||||
return NextResponse.json({ error: '查询失败' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user