feat: refactor data asset center for enhanced search and analytics
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>
This commit is contained in:
473
services/intelligent-search-service.ts
Normal file
473
services/intelligent-search-service.ts
Normal file
@@ -0,0 +1,473 @@
|
||||
// 智能搜索服务 - 实现亚秒级查询和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
|
||||
}
|
||||
Reference in New Issue
Block a user