Refactor homepage for focused search and data display; streamline data platform; enhance user and tag management; focus AI assistant on data analysis and report generation. Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
474 lines
14 KiB
TypeScript
474 lines
14 KiB
TypeScript
// 智能搜索服务 - 实现亚秒级查询和AI增强搜索
|
||
import { getMindsDBConnector, type SearchRequest, type AIQueryRequest } from "@/lib/mindsdb-connector"
|
||
|
||
export interface SearchResult {
|
||
id: string
|
||
type: "user" | "traffic" | "insight"
|
||
title: string
|
||
description: string
|
||
tags: string[]
|
||
relevanceScore: number
|
||
updatedAt: string
|
||
metadata?: Record<string, any>
|
||
}
|
||
|
||
export interface SearchStats {
|
||
totalResults: number
|
||
queryTime: number
|
||
suggestions: string[]
|
||
filters: Record<string, any>
|
||
}
|
||
|
||
export interface SearchResponse {
|
||
results: SearchResult[]
|
||
stats: SearchStats
|
||
hasMore: boolean
|
||
}
|
||
|
||
export class IntelligentSearchService {
|
||
private mindsDB = getMindsDBConnector()
|
||
private searchHistory: string[] = []
|
||
private popularQueries: Map<string, number> = new Map()
|
||
|
||
// 智能搜索主入口
|
||
async search(
|
||
query: string,
|
||
type: "user" | "traffic" | "all" = "all",
|
||
options: {
|
||
limit?: number
|
||
offset?: number
|
||
filters?: Record<string, any>
|
||
useAI?: boolean
|
||
includeInsights?: boolean
|
||
} = {},
|
||
): Promise<SearchResponse> {
|
||
const startTime = Date.now()
|
||
|
||
try {
|
||
// 记录搜索历史
|
||
this.addToSearchHistory(query)
|
||
|
||
// 如果启用AI增强搜索
|
||
if (options.useAI) {
|
||
return await this.aiEnhancedSearch(query, type, options)
|
||
}
|
||
|
||
// 标准搜索
|
||
const searchRequest: SearchRequest = {
|
||
keyword: query,
|
||
type,
|
||
filters: options.filters,
|
||
limit: options.limit || 50,
|
||
offset: options.offset || 0,
|
||
}
|
||
|
||
const rawResults = await this.mindsDB.intelligentSearch(searchRequest)
|
||
const results = this.formatSearchResults(rawResults)
|
||
|
||
// 如果需要包含AI洞察
|
||
if (options.includeInsights) {
|
||
const insights = await this.generateSearchInsights(query, results)
|
||
results.push(...insights)
|
||
}
|
||
|
||
const queryTime = Date.now() - startTime
|
||
|
||
return {
|
||
results,
|
||
stats: {
|
||
totalResults: results.length,
|
||
queryTime,
|
||
suggestions: await this.generateSuggestions(query),
|
||
filters: this.extractAvailableFilters(results),
|
||
},
|
||
hasMore: results.length === (options.limit || 50),
|
||
}
|
||
} catch (error) {
|
||
console.error("搜索失败:", error)
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// AI增强搜索
|
||
private async aiEnhancedSearch(
|
||
query: string,
|
||
type: "user" | "traffic" | "all",
|
||
options: any,
|
||
): Promise<SearchResponse> {
|
||
const startTime = Date.now()
|
||
|
||
// 使用AI理解查询意图
|
||
const aiRequest: AIQueryRequest = {
|
||
query: `分析这个搜索查询的意图并提供相关的搜索建议: "${query}"`,
|
||
model: "gpt4",
|
||
useCache: true,
|
||
}
|
||
|
||
const aiAnalysis = await this.mindsDB.aiQuery(aiRequest)
|
||
|
||
// 基于AI分析结果优化搜索参数
|
||
const enhancedSearchRequest: SearchRequest = {
|
||
keyword: query,
|
||
type,
|
||
filters: {
|
||
...options.filters,
|
||
...this.extractFiltersFromAI(aiAnalysis),
|
||
},
|
||
limit: options.limit || 50,
|
||
offset: options.offset || 0,
|
||
}
|
||
|
||
const rawResults = await this.mindsDB.intelligentSearch(enhancedSearchRequest)
|
||
const results = this.formatSearchResults(rawResults)
|
||
|
||
// AI生成的相关洞察
|
||
const aiInsights = await this.generateAIInsights(query, results)
|
||
results.push(...aiInsights)
|
||
|
||
const queryTime = Date.now() - startTime
|
||
|
||
return {
|
||
results,
|
||
stats: {
|
||
totalResults: results.length,
|
||
queryTime,
|
||
suggestions: await this.generateAISuggestions(query, aiAnalysis),
|
||
filters: this.extractAvailableFilters(results),
|
||
},
|
||
hasMore: results.length === (options.limit || 50),
|
||
}
|
||
}
|
||
|
||
// 格式化搜索结果
|
||
private formatSearchResults(rawResults: any[]): SearchResult[] {
|
||
return rawResults.map((result) => ({
|
||
id: result.id || result.user_id || result.keyword_id,
|
||
type: result.type || "user",
|
||
title: result.title || result.username || result.keyword,
|
||
description: result.description || this.generateDescription(result),
|
||
tags: this.parseTags(result.tags),
|
||
relevanceScore: result.relevance_score || 0,
|
||
updatedAt: result.updated_at || result.last_active || result.last_updated,
|
||
metadata: {
|
||
...result,
|
||
searchType: result.type,
|
||
},
|
||
}))
|
||
}
|
||
|
||
// 生成描述
|
||
private generateDescription(result: any): string {
|
||
if (result.type === "user") {
|
||
return `${result.phone || ""} | ${result.email || ""} | RFM: ${result.rfm_score || "N/A"}`
|
||
} else if (result.type === "traffic") {
|
||
return `搜索量: ${result.search_volume || "N/A"} | 竞争度: ${result.competition || "N/A"} | CPC: ${result.cpc || "N/A"}`
|
||
}
|
||
return result.description || ""
|
||
}
|
||
|
||
// 解析标签
|
||
private parseTags(tags: any): string[] {
|
||
if (typeof tags === "string") {
|
||
try {
|
||
return JSON.parse(tags)
|
||
} catch {
|
||
return tags.split(",").map((tag) => tag.trim())
|
||
}
|
||
}
|
||
return Array.isArray(tags) ? tags : []
|
||
}
|
||
|
||
// 生成搜索建议
|
||
private async generateSuggestions(query: string): Promise<string[]> {
|
||
const suggestions: string[] = []
|
||
|
||
// 基于搜索历史的建议
|
||
const historySuggestions = this.searchHistory
|
||
.filter((h) => h.toLowerCase().includes(query.toLowerCase()) && h !== query)
|
||
.slice(0, 3)
|
||
|
||
suggestions.push(...historySuggestions)
|
||
|
||
// 基于热门查询的建议
|
||
const popularSuggestions = Array.from(this.popularQueries.entries())
|
||
.sort((a, b) => b[1] - a[1])
|
||
.map(([q]) => q)
|
||
.filter((q) => q.toLowerCase().includes(query.toLowerCase()) && q !== query)
|
||
.slice(0, 3)
|
||
|
||
suggestions.push(...popularSuggestions)
|
||
|
||
// 智能补全建议
|
||
const completionSuggestions = await this.generateCompletionSuggestions(query)
|
||
suggestions.push(...completionSuggestions)
|
||
|
||
return [...new Set(suggestions)].slice(0, 8)
|
||
}
|
||
|
||
// 生成AI建议
|
||
private async generateAISuggestions(query: string, aiAnalysis: any): Promise<string[]> {
|
||
try {
|
||
const aiRequest: AIQueryRequest = {
|
||
query: `基于查询"${query}"和分析结果,生成5个相关的搜索建议`,
|
||
model: "gpt4",
|
||
useCache: true,
|
||
}
|
||
|
||
const result = await this.mindsDB.aiQuery(aiRequest)
|
||
return this.parseAISuggestions(result)
|
||
} catch (error) {
|
||
console.error("生成AI建议失败:", error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
// 生成补全建议
|
||
private async generateCompletionSuggestions(query: string): Promise<string[]> {
|
||
// 这里可以集成更复杂的自动补全逻辑
|
||
const commonSuffixes = ["分析", "统计", "趋势", "预测", "报告", "用户", "流量", "关键词", "转化", "留存"]
|
||
|
||
return commonSuffixes
|
||
.map((suffix) => `${query} ${suffix}`)
|
||
.filter((suggestion) => suggestion.length <= 50)
|
||
.slice(0, 3)
|
||
}
|
||
|
||
// 解析AI建议
|
||
private parseAISuggestions(aiResult: any): string[] {
|
||
try {
|
||
// 假设AI返回的是建议列表
|
||
if (aiResult.suggestions && Array.isArray(aiResult.suggestions)) {
|
||
return aiResult.suggestions
|
||
}
|
||
|
||
// 如果是文本格式,尝试解析
|
||
if (typeof aiResult === "string") {
|
||
const lines = aiResult.split("\n")
|
||
return lines
|
||
.filter((line) => line.trim().length > 0)
|
||
.map((line) => line.replace(/^\d+\.\s*/, "").trim())
|
||
.slice(0, 5)
|
||
}
|
||
|
||
return []
|
||
} catch (error) {
|
||
console.error("解析AI建议失败:", error)
|
||
return []
|
||
}
|
||
}
|
||
|
||
// 从AI分析中提取过滤器
|
||
private extractFiltersFromAI(aiAnalysis: any): Record<string, any> {
|
||
const filters: Record<string, any> = {}
|
||
|
||
try {
|
||
if (aiAnalysis.filters) {
|
||
Object.assign(filters, aiAnalysis.filters)
|
||
}
|
||
|
||
// 基于AI分析结果添加智能过滤器
|
||
if (aiAnalysis.intent === "high_value_users") {
|
||
filters.rfm_score = { $gte: 80 }
|
||
}
|
||
|
||
if (aiAnalysis.intent === "recent_activity") {
|
||
filters.last_active = { $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
|
||
}
|
||
} catch (error) {
|
||
console.error("提取AI过滤器失败:", error)
|
||
}
|
||
|
||
return filters
|
||
}
|
||
|
||
// 生成搜索洞察
|
||
private async generateSearchInsights(query: string, results: SearchResult[]): Promise<SearchResult[]> {
|
||
const insights: SearchResult[] = []
|
||
|
||
try {
|
||
// 用户相关洞察
|
||
const userResults = results.filter((r) => r.type === "user")
|
||
if (userResults.length > 0) {
|
||
const userInsight = await this.generateUserInsight(query, userResults)
|
||
if (userInsight) insights.push(userInsight)
|
||
}
|
||
|
||
// 流量相关洞察
|
||
const trafficResults = results.filter((r) => r.type === "traffic")
|
||
if (trafficResults.length > 0) {
|
||
const trafficInsight = await this.generateTrafficInsight(query, trafficResults)
|
||
if (trafficInsight) insights.push(trafficInsight)
|
||
}
|
||
} catch (error) {
|
||
console.error("生成搜索洞察失败:", error)
|
||
}
|
||
|
||
return insights
|
||
}
|
||
|
||
// 生成AI洞察
|
||
private async generateAIInsights(query: string, results: SearchResult[]): Promise<SearchResult[]> {
|
||
const insights: SearchResult[] = []
|
||
|
||
try {
|
||
const aiRequest: AIQueryRequest = {
|
||
query: `基于搜索查询"${query}"和${results.length}个结果,生成3个关键业务洞察`,
|
||
model: "gpt4",
|
||
useCache: true,
|
||
}
|
||
|
||
const aiResult = await this.mindsDB.aiQuery(aiRequest)
|
||
const aiInsights = this.parseAIInsights(aiResult)
|
||
|
||
insights.push(...aiInsights)
|
||
} catch (error) {
|
||
console.error("生成AI洞察失败:", error)
|
||
}
|
||
|
||
return insights
|
||
}
|
||
|
||
// 解析AI洞察
|
||
private parseAIInsights(aiResult: any): SearchResult[] {
|
||
const insights: SearchResult[] = []
|
||
|
||
try {
|
||
if (aiResult.insights && Array.isArray(aiResult.insights)) {
|
||
aiResult.insights.forEach((insight: any, index: number) => {
|
||
insights.push({
|
||
id: `ai_insight_${Date.now()}_${index}`,
|
||
type: "insight",
|
||
title: insight.title || `AI洞察 ${index + 1}`,
|
||
description: insight.description || insight.content,
|
||
tags: ["AI洞察", "智能分析"],
|
||
relevanceScore: insight.confidence || 0.8,
|
||
updatedAt: new Date().toISOString(),
|
||
metadata: {
|
||
source: "ai",
|
||
confidence: insight.confidence,
|
||
type: "insight",
|
||
},
|
||
})
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error("解析AI洞察失败:", error)
|
||
}
|
||
|
||
return insights
|
||
}
|
||
|
||
// 生成用户洞察
|
||
private async generateUserInsight(query: string, userResults: SearchResult[]): Promise<SearchResult | null> {
|
||
try {
|
||
const totalUsers = userResults.length
|
||
const avgRelevance = userResults.reduce((sum, r) => sum + r.relevanceScore, 0) / totalUsers
|
||
|
||
return {
|
||
id: `user_insight_${Date.now()}`,
|
||
type: "insight",
|
||
title: "用户搜索洞察",
|
||
description: `找到 ${totalUsers} 个相关用户,平均相关度 ${avgRelevance.toFixed(2)}`,
|
||
tags: ["用户分析", "搜索洞察"],
|
||
relevanceScore: 0.9,
|
||
updatedAt: new Date().toISOString(),
|
||
metadata: {
|
||
totalUsers,
|
||
avgRelevance,
|
||
type: "user_insight",
|
||
},
|
||
}
|
||
} catch (error) {
|
||
console.error("生成用户洞察失败:", error)
|
||
return null
|
||
}
|
||
}
|
||
|
||
// 生成流量洞察
|
||
private async generateTrafficInsight(query: string, trafficResults: SearchResult[]): Promise<SearchResult | null> {
|
||
try {
|
||
const totalKeywords = trafficResults.length
|
||
const avgRelevance = trafficResults.reduce((sum, r) => sum + r.relevanceScore, 0) / totalKeywords
|
||
|
||
return {
|
||
id: `traffic_insight_${Date.now()}`,
|
||
type: "insight",
|
||
title: "流量关键词洞察",
|
||
description: `找到 ${totalKeywords} 个相关关键词,平均相关度 ${avgRelevance.toFixed(2)}`,
|
||
tags: ["流量分析", "关键词洞察"],
|
||
relevanceScore: 0.9,
|
||
updatedAt: new Date().toISOString(),
|
||
metadata: {
|
||
totalKeywords,
|
||
avgRelevance,
|
||
type: "traffic_insight",
|
||
},
|
||
}
|
||
} catch (error) {
|
||
console.error("生成流量洞察失败:", error)
|
||
return null
|
||
}
|
||
}
|
||
|
||
// 提取可用过滤器
|
||
private extractAvailableFilters(results: SearchResult[]): Record<string, any> {
|
||
const filters: Record<string, any> = {}
|
||
|
||
// 提取类型过滤器
|
||
const types = [...new Set(results.map((r) => r.type))]
|
||
if (types.length > 1) {
|
||
filters.type = types
|
||
}
|
||
|
||
// 提取标签过滤器
|
||
const allTags = results.flatMap((r) => r.tags)
|
||
const uniqueTags = [...new Set(allTags)]
|
||
if (uniqueTags.length > 0) {
|
||
filters.tags = uniqueTags.slice(0, 20) // 限制标签数量
|
||
}
|
||
|
||
return filters
|
||
}
|
||
|
||
// 添加到搜索历史
|
||
private addToSearchHistory(query: string): void {
|
||
if (query.trim().length === 0) return
|
||
|
||
// 更新搜索历史
|
||
this.searchHistory.unshift(query)
|
||
this.searchHistory = [...new Set(this.searchHistory)].slice(0, 100) // 保留最近100个唯一查询
|
||
|
||
// 更新热门查询统计
|
||
const count = this.popularQueries.get(query) || 0
|
||
this.popularQueries.set(query, count + 1)
|
||
}
|
||
|
||
// 获取搜索统计
|
||
getSearchStats(): any {
|
||
return {
|
||
totalSearches: this.searchHistory.length,
|
||
uniqueQueries: new Set(this.searchHistory).size,
|
||
popularQueries: Array.from(this.popularQueries.entries())
|
||
.sort((a, b) => b[1] - a[1])
|
||
.slice(0, 10)
|
||
.map(([query, count]) => ({ query, count })),
|
||
}
|
||
}
|
||
|
||
// 清理搜索历史
|
||
clearSearchHistory(): void {
|
||
this.searchHistory = []
|
||
this.popularQueries.clear()
|
||
}
|
||
}
|
||
|
||
// 单例模式
|
||
let searchServiceInstance: IntelligentSearchService | null = null
|
||
|
||
export function getIntelligentSearchService(): IntelligentSearchService {
|
||
if (!searchServiceInstance) {
|
||
searchServiceInstance = new IntelligentSearchService()
|
||
}
|
||
return searchServiceInstance
|
||
}
|