chore: 以本地为准,上传全部并替换 GitHub
This commit is contained in:
315
app/api/ai-chat/route.ts
Normal file
315
app/api/ai-chat/route.ts
Normal file
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* AI 智能助手 API
|
||||
* 支持自然语言查询用户数据
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from "next/server"
|
||||
import { getMongoClient, intelligentSearch, queryFullProfile, getDatabaseStats } from "@/lib/mongodb"
|
||||
|
||||
// 消息类型
|
||||
interface ChatMessage {
|
||||
role: "user" | "assistant" | "system"
|
||||
content: string
|
||||
timestamp?: string
|
||||
data?: any
|
||||
}
|
||||
|
||||
// 解析用户意图
|
||||
function parseIntent(message: string): { type: string; query?: string; params?: any } {
|
||||
const msg = message.trim().toLowerCase()
|
||||
|
||||
// 查询手机号
|
||||
const phoneMatch = message.match(/(?:查|查询|搜索|找)?[::\s]*(\+?86)?1[3-9]\d{9}/g)
|
||||
if (phoneMatch) {
|
||||
const phone = phoneMatch[0].replace(/[查询搜索找::\s]/g, '').replace(/^\+?86/, '')
|
||||
return { type: "query_phone", query: phone }
|
||||
}
|
||||
|
||||
// 查询 QQ
|
||||
const qqMatch = message.match(/(?:qq|QQ)[::\s]*(\d{5,11})|(\d{5,11})\s*(?:qq|QQ)/i)
|
||||
if (qqMatch) {
|
||||
return { type: "query_qq", query: qqMatch[1] || qqMatch[2] }
|
||||
}
|
||||
|
||||
// 系统状态
|
||||
if (msg.includes("状态") || msg.includes("统计") || msg.includes("总量")) {
|
||||
return { type: "system_status" }
|
||||
}
|
||||
|
||||
// RFM 分析
|
||||
if (msg.includes("rfm") || msg.includes("估值") || msg.includes("价值")) {
|
||||
return { type: "rfm_analysis" }
|
||||
}
|
||||
|
||||
// 高价值用户
|
||||
if (msg.includes("高价值") || msg.includes("top") || msg.includes("排行")) {
|
||||
const limitMatch = msg.match(/(\d+)/)
|
||||
return { type: "high_value_users", params: { limit: limitMatch ? parseInt(limitMatch[1]) : 10 } }
|
||||
}
|
||||
|
||||
// 帮助
|
||||
if (msg.includes("帮助") || msg.includes("help") || msg === "?") {
|
||||
return { type: "help" }
|
||||
}
|
||||
|
||||
// 通用搜索
|
||||
return { type: "search", query: message }
|
||||
}
|
||||
|
||||
// 格式化用户数据
|
||||
function formatUserData(user: any): string {
|
||||
if (!user) return "未找到用户信息"
|
||||
|
||||
const lines: string[] = []
|
||||
|
||||
if (user.name) lines.push(`👤 姓名: ${user.name}`)
|
||||
if (user.phone_masked) lines.push(`📱 手机: ${user.phone_masked}`)
|
||||
if (user.qq) lines.push(`💬 QQ: ${user.qq}`)
|
||||
if (user.gender) lines.push(`⚧ 性别: ${user.gender}`)
|
||||
if (user.age_range) lines.push(`📅 年龄段: ${user.age_range}`)
|
||||
if (user.province || user.city) lines.push(`📍 地区: ${user.province || ''}${user.city || ''}`)
|
||||
if (user.evaluation_score || user.user_evaluation_score) {
|
||||
lines.push(`⭐ 估值分: ${user.evaluation_score || user.user_evaluation_score}`)
|
||||
}
|
||||
if (user.user_level) lines.push(`🏆 等级: ${user.user_level}`)
|
||||
if (user.carrier) lines.push(`📶 运营商: ${user.carrier}`)
|
||||
if (user.tags && user.tags.length > 0) lines.push(`🏷️ 标签: ${user.tags.join(', ')}`)
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
// 处理 AI 聊天
|
||||
async function processChat(message: string): Promise<ChatMessage> {
|
||||
const startTime = Date.now()
|
||||
const intent = parseIntent(message)
|
||||
|
||||
try {
|
||||
switch (intent.type) {
|
||||
case "query_phone": {
|
||||
const result = await queryFullProfile(intent.query!)
|
||||
if (result.valuation || result.qqPhone) {
|
||||
const user = {
|
||||
...result.valuation,
|
||||
qq: result.qqPhone?.qq,
|
||||
carrier: result.qqPhone?.运营商
|
||||
}
|
||||
return {
|
||||
role: "assistant",
|
||||
content: `🎯 手机号 ${intent.query} 查询结果:\n\n${formatUserData(user)}\n\n⏱️ 查询耗时: ${Date.now() - startTime}ms`,
|
||||
data: result
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
role: "assistant",
|
||||
content: `❌ 未找到手机号 ${intent.query} 的相关信息\n\n💡 提示: 请检查手机号是否正确(11位数字)`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "query_qq": {
|
||||
const client = await getMongoClient()
|
||||
const qqDb = client.db("KR_腾讯")
|
||||
let qqDoc = await qqDb.collection("QQ+手机").findOne({ qq: intent.query })
|
||||
if (!qqDoc) {
|
||||
qqDoc = await qqDb.collection("QQ+手机").findOne({ qq: parseInt(intent.query!) })
|
||||
}
|
||||
|
||||
if (qqDoc) {
|
||||
const phone = qqDoc.phone || qqDoc.手机号
|
||||
let userInfo = `🎯 QQ ${intent.query} 查询结果:\n\n`
|
||||
userInfo += `💬 QQ: ${qqDoc.qq}\n`
|
||||
userInfo += `📱 手机: ${phone ? phone.toString().replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') : '未知'}\n`
|
||||
userInfo += `📊 QQ评分: ${qqDoc.QQ号评分 || 'N/A'}\n`
|
||||
userInfo += `📊 手机评分: ${qqDoc.手机号评分 || 'N/A'}\n`
|
||||
userInfo += `📍 地区: ${qqDoc.省份 || ''}${qqDoc.地区 || ''}\n`
|
||||
userInfo += `📶 运营商: ${qqDoc.运营商 || 'N/A'}\n`
|
||||
userInfo += `\n⏱️ 查询耗时: ${Date.now() - startTime}ms`
|
||||
|
||||
return {
|
||||
role: "assistant",
|
||||
content: userInfo,
|
||||
data: qqDoc
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
role: "assistant",
|
||||
content: `❌ 未找到 QQ ${intent.query} 的相关信息`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "system_status": {
|
||||
const stats = await getDatabaseStats()
|
||||
return {
|
||||
role: "assistant",
|
||||
content: `📊 神射手系统状态\n\n` +
|
||||
`🟢 连接状态: ${stats.connected ? '正常' : '异常'}\n` +
|
||||
`📁 数据库数: ${stats.databases?.length || 0} 个\n` +
|
||||
`📄 总记录数: ${(stats.totalDocuments / 1e8).toFixed(2)} 亿条\n` +
|
||||
`💾 总数据量: ${(stats.totalSize / 1e9).toFixed(2)} GB\n` +
|
||||
`⏱️ 响应延迟: ${stats.latency}ms`,
|
||||
data: stats
|
||||
}
|
||||
}
|
||||
|
||||
case "rfm_analysis": {
|
||||
const client = await getMongoClient()
|
||||
const db = client.db("KR")
|
||||
const pipeline = [
|
||||
{ $group: { _id: "$user_level", count: { $sum: 1 } } },
|
||||
{ $sort: { count: -1 } }
|
||||
]
|
||||
const results = await db.collection("用户估值").aggregate(pipeline).toArray()
|
||||
|
||||
let content = `📈 RFM 用户分布统计\n\n`
|
||||
let total = 0
|
||||
results.forEach(r => {
|
||||
const level = r._id || '未分级'
|
||||
content += `${level}: ${(r.count / 10000).toFixed(1)}万\n`
|
||||
total += r.count
|
||||
})
|
||||
content += `\n📊 总用户数: ${(total / 10000).toFixed(1)}万`
|
||||
content += `\n⏱️ 统计耗时: ${Date.now() - startTime}ms`
|
||||
|
||||
return {
|
||||
role: "assistant",
|
||||
content,
|
||||
data: results
|
||||
}
|
||||
}
|
||||
|
||||
case "high_value_users": {
|
||||
const limit = intent.params?.limit || 10
|
||||
const client = await getMongoClient()
|
||||
const db = client.db("KR")
|
||||
const users = await db.collection("用户估值")
|
||||
.find({ user_evaluation_score: { $exists: true } })
|
||||
.sort({ user_evaluation_score: -1 })
|
||||
.limit(limit)
|
||||
.toArray()
|
||||
|
||||
let content = `🏆 高价值用户 TOP${limit}\n\n`
|
||||
users.forEach((u, i) => {
|
||||
const phone = u.phone?.replace(/(\+?86)?(\d{3})\d{4}(\d{4})/, '$2****$3') || '***'
|
||||
content += `${i + 1}. ${u.name || '未知'} (${phone}) - 估值: ${u.user_evaluation_score}\n`
|
||||
})
|
||||
content += `\n⏱️ 查询耗时: ${Date.now() - startTime}ms`
|
||||
|
||||
return {
|
||||
role: "assistant",
|
||||
content,
|
||||
data: users
|
||||
}
|
||||
}
|
||||
|
||||
case "help": {
|
||||
return {
|
||||
role: "assistant",
|
||||
content: `🎯 神射手 AI 助手使用指南\n\n` +
|
||||
`📱 查询手机号:\n "查 13800138000" 或 "13800138000"\n\n` +
|
||||
`💬 查询 QQ:\n "28533368 qq" 或 "qq 28533368"\n\n` +
|
||||
`📊 系统状态:\n "系统状态" 或 "统计"\n\n` +
|
||||
`📈 RFM 分析:\n "RFM分析" 或 "用户估值"\n\n` +
|
||||
`🏆 高价值用户:\n "高价值用户 TOP10"\n\n` +
|
||||
`💡 数据覆盖: 20亿+用户,207GB数据`
|
||||
}
|
||||
}
|
||||
|
||||
case "search":
|
||||
default: {
|
||||
// 尝试智能搜索
|
||||
const result = await intelligentSearch(intent.query || message, { limit: 5 })
|
||||
if (result.total > 0) {
|
||||
let content = `🔍 搜索结果 (共${result.total}条)\n\n`
|
||||
result.users.slice(0, 5).forEach((u, i) => {
|
||||
content += `${i + 1}. ${u.name || '未知'} - ${u.city || ''} - 估值: ${u.user_evaluation_score || 'N/A'}\n`
|
||||
})
|
||||
content += `\n⏱️ 查询耗时: ${Date.now() - startTime}ms`
|
||||
return {
|
||||
role: "assistant",
|
||||
content,
|
||||
data: result
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
role: "assistant",
|
||||
content: `🤔 我不太理解 "${message}"\n\n` +
|
||||
`💡 你可以尝试:\n` +
|
||||
`- 查询手机号: "查 13800138000"\n` +
|
||||
`- 查询QQ: "28533368 qq"\n` +
|
||||
`- 输入 "帮助" 查看更多功能`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("AI Chat 错误:", error)
|
||||
return {
|
||||
role: "assistant",
|
||||
content: `⚠️ 查询出错: ${error.message}\n\n请稍后重试或检查数据库连接`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/ai-chat
|
||||
* AI 对话接口
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { message, history = [] } = body
|
||||
|
||||
if (!message || !message.trim()) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: "消息不能为空"
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
const response = await processChat(message)
|
||||
response.timestamp = new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
response
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
console.error("AI Chat API 错误:", error)
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
response: {
|
||||
role: "assistant",
|
||||
content: `⚠️ 服务暂时不可用: ${error.message}`,
|
||||
timestamp: new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })
|
||||
}
|
||||
}, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/ai-chat
|
||||
* 获取 AI 助手状态
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const stats = await getDatabaseStats()
|
||||
return NextResponse.json({
|
||||
status: "online",
|
||||
model: "神射手 AI v1.0",
|
||||
capabilities: ["用户查询", "QQ查询", "RFM分析", "智能搜索"],
|
||||
database: {
|
||||
connected: stats.connected,
|
||||
totalUsers: stats.totalDocuments,
|
||||
latency: stats.latency
|
||||
}
|
||||
})
|
||||
} catch (error: any) {
|
||||
return NextResponse.json({
|
||||
status: "degraded",
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user