316 lines
10 KiB
TypeScript
316 lines
10 KiB
TypeScript
/**
|
||
* 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
|
||
})
|
||
}
|
||
}
|