104 lines
3.1 KiB
TypeScript
104 lines
3.1 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { getServerSession } from 'next-auth'
|
|
import { authOptions } from '@/lib/auth'
|
|
import { prisma } from '@/lib/prisma'
|
|
import {
|
|
buildMaterialWhereFromSearchParams,
|
|
MaterialQueryError,
|
|
} from '@/lib/material-query'
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const session = await getServerSession(authOptions)
|
|
if (!session?.user || !(session.user as any).id) {
|
|
return NextResponse.json({ error: '未登录', code: 'UNAUTHORIZED' }, { status: 401 })
|
|
}
|
|
|
|
const searchParams = request.nextUrl.searchParams
|
|
const q = searchParams.get('q')
|
|
|
|
if (!q || q.trim().length < 1) {
|
|
return NextResponse.json({ suggestions: [] })
|
|
}
|
|
|
|
const keyword = q.trim()
|
|
|
|
try {
|
|
const { where: visibleWhere, empty } = await buildMaterialWhereFromSearchParams(
|
|
new URLSearchParams(),
|
|
session.user as any,
|
|
)
|
|
|
|
if (empty) {
|
|
return NextResponse.json({ suggestions: [] })
|
|
}
|
|
|
|
const withVisibleWhere = (fieldWhere: any) => ({
|
|
AND: [
|
|
visibleWhere,
|
|
fieldWhere,
|
|
],
|
|
})
|
|
|
|
// 并行查询三个字段的前缀匹配和模糊匹配
|
|
// 为了性能,我们只取 DISTINCT 的值,并限制数量
|
|
|
|
// 使用 Promise.all 并行查询
|
|
const [spuResults, authorResults, sceneResults] = await Promise.all([
|
|
// SPU 型号建议
|
|
prisma.material.findMany({
|
|
where: withVisibleWhere({
|
|
productModel: { contains: keyword, mode: 'insensitive' },
|
|
}),
|
|
select: { productModel: true },
|
|
distinct: ['productModel'],
|
|
take: 5
|
|
}),
|
|
// 作者建议
|
|
prisma.material.findMany({
|
|
where: withVisibleWhere({
|
|
authorName: { contains: keyword, mode: 'insensitive' },
|
|
}),
|
|
select: { authorName: true },
|
|
distinct: ['authorName'],
|
|
take: 3
|
|
}),
|
|
// 场景建议
|
|
prisma.material.findMany({
|
|
where: withVisibleWhere({
|
|
sceneName: { contains: keyword, mode: 'insensitive' },
|
|
}),
|
|
select: { sceneName: true },
|
|
distinct: ['sceneName'],
|
|
take: 3
|
|
})
|
|
])
|
|
|
|
// 合并结果并去重
|
|
const suggestionsSet = new Set<string>()
|
|
const suggestions: Array<{ type: 'spu' | 'author' | 'scene', value: string }> = []
|
|
|
|
const addSuggestion = (type: 'spu' | 'author' | 'scene', value: string) => {
|
|
if (value && !suggestionsSet.has(value) && value.toLowerCase().includes(keyword.toLowerCase())) {
|
|
suggestionsSet.add(value)
|
|
suggestions.push({ type, value })
|
|
}
|
|
}
|
|
|
|
spuResults.forEach(i => addSuggestion('spu', i.productModel))
|
|
authorResults.forEach(i => addSuggestion('author', i.authorName))
|
|
sceneResults.forEach(i => addSuggestion('scene', i.sceneName))
|
|
|
|
return NextResponse.json({ suggestions: suggestions.slice(0, 10) })
|
|
} catch (error) {
|
|
if (error instanceof MaterialQueryError) {
|
|
return NextResponse.json(
|
|
{ error: error.message, code: error.code },
|
|
{ status: error.status },
|
|
)
|
|
}
|
|
|
|
console.error('Search suggestions error:', error)
|
|
return NextResponse.json({ suggestions: [] }, { status: 500 })
|
|
}
|
|
}
|