feat(API): 添加消息全局搜索接口
- 新增 /api/messages/search 搜索 API - 支持关键词模糊搜索消息内容 - 支持角色筛选(用户消息/AI回复/全部) - 支持分页查询,返回结果总数 - 仅查询当前用户的未归档对话
This commit is contained in:
parent
56b7ffa68d
commit
dcd757e584
146
src/app/api/messages/search/route.ts
Normal file
146
src/app/api/messages/search/route.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/drizzle/db';
|
||||
import { messages, conversations } from '@/drizzle/schema';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
import { eq, and, ilike, desc, sql, or } from 'drizzle-orm';
|
||||
|
||||
/**
|
||||
* 搜索结果类型
|
||||
*/
|
||||
export interface SearchResult {
|
||||
messageId: string;
|
||||
conversationId: string;
|
||||
conversationTitle: string;
|
||||
role: string;
|
||||
content: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/messages/search - 全局消息搜索
|
||||
*
|
||||
* Query Parameters:
|
||||
* - q: string (必填) - 搜索关键词
|
||||
* - role: 'user' | 'assistant' | 'all' (可选) - 角色筛选,默认 'all'
|
||||
* - page: number (可选) - 页码,默认 1
|
||||
* - limit: number (可选) - 每页数量,默认 20,最大 50
|
||||
*/
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
// 验证用户身份
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: '请先登录' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
const { searchParams } = new URL(request.url);
|
||||
const query = searchParams.get('q')?.trim();
|
||||
const role = searchParams.get('role') || 'all';
|
||||
const page = Math.max(1, parseInt(searchParams.get('page') || '1', 10));
|
||||
const limit = Math.min(50, Math.max(1, parseInt(searchParams.get('limit') || '20', 10)));
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 验证搜索关键词
|
||||
if (!query || query.length < 1) {
|
||||
return NextResponse.json(
|
||||
{ error: '请输入搜索关键词' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 关键词长度限制
|
||||
if (query.length > 100) {
|
||||
return NextResponse.json(
|
||||
{ error: '搜索关键词过长,请限制在 100 字符以内' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 构建搜索条件
|
||||
const searchPattern = `%${query}%`;
|
||||
|
||||
// 基础条件:用户的对话
|
||||
const baseConditions = [
|
||||
eq(conversations.userId, user.userId),
|
||||
eq(conversations.isArchived, false), // 排除已归档对话
|
||||
];
|
||||
|
||||
// 角色筛选条件
|
||||
const roleCondition = role === 'all'
|
||||
? or(eq(messages.role, 'user'), eq(messages.role, 'assistant'))
|
||||
: eq(messages.role, role);
|
||||
|
||||
// 内容搜索条件
|
||||
const contentCondition = ilike(messages.content, searchPattern);
|
||||
|
||||
// 执行搜索查询
|
||||
const results = await db
|
||||
.select({
|
||||
messageId: messages.messageId,
|
||||
conversationId: messages.conversationId,
|
||||
conversationTitle: conversations.title,
|
||||
role: messages.role,
|
||||
content: messages.content,
|
||||
createdAt: messages.createdAt,
|
||||
})
|
||||
.from(messages)
|
||||
.innerJoin(
|
||||
conversations,
|
||||
eq(messages.conversationId, conversations.conversationId)
|
||||
)
|
||||
.where(
|
||||
and(
|
||||
...baseConditions,
|
||||
roleCondition,
|
||||
contentCondition
|
||||
)
|
||||
)
|
||||
.orderBy(desc(messages.createdAt))
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
// 获取总数(用于分页)
|
||||
const countResult = await db
|
||||
.select({
|
||||
count: sql<number>`count(*)::int`,
|
||||
})
|
||||
.from(messages)
|
||||
.innerJoin(
|
||||
conversations,
|
||||
eq(messages.conversationId, conversations.conversationId)
|
||||
)
|
||||
.where(
|
||||
and(
|
||||
...baseConditions,
|
||||
roleCondition,
|
||||
contentCondition
|
||||
)
|
||||
);
|
||||
|
||||
const total = countResult[0]?.count || 0;
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
results,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages,
|
||||
query,
|
||||
role,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Search messages error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '搜索失败,请稍后重试' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user