chore: 以本地为准,强制同步全部内容

This commit is contained in:
卡若
2026-01-31 12:07:46 +08:00
parent ce0a716d02
commit b4aa620c93
44 changed files with 7140 additions and 314 deletions

View File

@@ -1,52 +1,177 @@
import { type NextRequest, NextResponse } from "next/server"
import { getIntelligentSearchService } from "@/services/intelligent-search-service"
/**
* 智能搜索 API 路由
* 对接神射手 MongoDB - 跨库查询
*/
import { type NextRequest, NextResponse } from "next/server"
import {
intelligentSearch,
queryFullProfile,
queryPhoneByQQ,
UserValuationDoc
} from "@/lib/mongodb"
/**
* 脱敏手机号
*/
function maskPhone(phone: string | undefined): string {
if (!phone) return ''
if (phone.length !== 11) return phone
return `${phone.slice(0, 3)}****${phone.slice(-4)}`
}
/**
* 转换搜索结果
*/
function transformSearchResult(doc: UserValuationDoc, queryType: string): any {
const name = doc.name || '未知用户'
return {
id: doc._id?.toString(),
type: 'user',
title: name,
subtitle: doc.phone_masked || maskPhone(doc.phone),
description: `${doc.province || ''}${doc.city || ''} | ${doc.user_level || '未分级'} | RFM: ${doc.rfm_composite_score?.toFixed(2) || 'N/A'}`,
data: {
phone: doc.phone,
phone_masked: doc.phone_masked || maskPhone(doc.phone),
name: doc.name,
province: doc.province,
city: doc.city,
userLevel: doc.user_level,
rfmScore: doc.rfm_composite_score,
tags: doc.tags || [],
email: doc.email
},
matchedBy: queryType,
relevanceScore: doc.rfm_composite_score || 0
}
}
/**
* GET /api/search
* 智能搜索
*/
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url)
const query = searchParams.get("q") || ""
const type = (searchParams.get("type") as "user" | "traffic" | "all") || "all"
const limit = Number.parseInt(searchParams.get("limit") || "50")
const offset = Number.parseInt(searchParams.get("offset") || "0")
const useAI = searchParams.get("ai") === "true"
const includeInsights = searchParams.get("insights") === "true"
if (!query.trim()) {
return NextResponse.json({ error: "搜索查询不能为空" }, { status: 400 })
return NextResponse.json({
error: "搜索查询不能为空"
}, { status: 400 })
}
const searchService = getIntelligentSearchService()
// 执行智能搜索
const result = await intelligentSearch(query, { limit, offset })
// 转换结果
const items = result.users.map(doc => transformSearchResult(doc, result.queryType))
// 如果是 QQ 查询,补充 QQ 信息
if (result.queryType === 'qq' && result.users.length > 0) {
const qqInfo = await queryPhoneByQQ(query.trim())
if (qqInfo) {
items[0].data.qq = qqInfo.qq
items[0].data.qqScore = qqInfo.QQ号评分
items[0].data.carrier = qqInfo.
}
}
const results = await searchService.search(query, type, {
limit,
offset,
useAI,
includeInsights,
filters: {},
return NextResponse.json({
query,
queryType: result.queryType,
total: result.total,
items,
pagination: {
limit,
offset,
hasMore: offset + items.length < result.total
}
})
return NextResponse.json(results)
} catch (error) {
console.error("搜索API错误:", error)
return NextResponse.json({ error: "搜索失败,请稍后重试" }, { status: 500 })
return NextResponse.json({
error: "搜索失败,请稍后重试",
details: error instanceof Error ? error.message : 'Unknown error'
}, { status: 500 })
}
}
/**
* POST /api/search
* 高级搜索(支持更多参数)
* 返回格式适配前端 SearchResponse 接口
*/
export async function POST(request: NextRequest) {
try {
const startTime = Date.now()
const body = await request.json()
const { query, type = "all", options = {} } = body
if (!query || !query.trim()) {
return NextResponse.json({ error: "搜索查询不能为空" }, { status: 400 })
return NextResponse.json({
error: "搜索查询不能为空"
}, { status: 400 })
}
const searchService = getIntelligentSearchService()
const results = await searchService.search(query, type, options)
const limit = options.limit || 50
const offset = options.offset || 0
const result = await intelligentSearch(query, { limit, offset })
// 转换为前端期望的格式
const results = result.users.map(doc => {
const name = doc.name || '未知用户'
return {
id: doc._id?.toString() || '',
type: 'user' as const,
title: name,
description: `${doc.province || ''}${doc.city || ''} | 估值: ${doc.user_evaluation_score || 'N/A'}`,
tags: doc.tags || [],
relevanceScore: doc.user_evaluation_score || 0,
updatedAt: doc.computed_at?.toISOString() || new Date().toISOString(),
metadata: {
phone: doc.phone,
phone_masked: doc.phone_masked || maskPhone(doc.phone),
province: doc.province,
city: doc.city,
gender: doc.gender,
age_range: doc.age_range,
userLevel: doc.user_level,
rfmScore: doc.rfm_composite_score,
evaluationScore: doc.user_evaluation_score,
dataQuality: doc.data_quality
}
}
})
return NextResponse.json(results)
const queryTime = Date.now() - startTime
// 返回前端期望的 SearchResponse 格式
return NextResponse.json({
results,
stats: {
totalResults: result.total,
queryTime,
suggestions: [],
filters: {
queryType: result.queryType
}
},
hasMore: offset + results.length < result.total
})
} catch (error) {
console.error("搜索API错误:", error)
return NextResponse.json({ error: "搜索失败,请稍后重试" }, { status: 500 })
return NextResponse.json({
error: "搜索失败,请稍后重试",
results: [],
stats: { totalResults: 0, queryTime: 0, suggestions: [], filters: {} },
hasMore: false
}, { status: 500 })
}
}