feat(API): 添加助手管理接口
- 新增助手 CRUD 接口 (GET/POST/PUT/DELETE) - 新增助手分类查询接口 - 新增助手收藏/取消收藏接口 - 新增最近使用助手查询接口 - 支持按分类、搜索关键词筛选助手
This commit is contained in:
parent
ee112a5ea3
commit
34aa3e50cf
106
src/app/api/assistants/[id]/favorite/route.ts
Normal file
106
src/app/api/assistants/[id]/favorite/route.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/drizzle/db';
|
||||
import { userFavoriteAssistants, assistants } from '@/drizzle/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
// POST /api/assistants/[id]/favorite - 收藏助手
|
||||
export async function POST(request: NextRequest, { params }: RouteParams) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const assistantId = parseInt(id);
|
||||
const body = await request.json();
|
||||
const { userId } = body;
|
||||
|
||||
if (isNaN(assistantId)) {
|
||||
return NextResponse.json({ error: 'Invalid assistant ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: 'User ID is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 检查助手是否存在
|
||||
const [assistant] = await db
|
||||
.select()
|
||||
.from(assistants)
|
||||
.where(eq(assistants.id, assistantId))
|
||||
.limit(1);
|
||||
|
||||
if (!assistant) {
|
||||
return NextResponse.json({ error: 'Assistant not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 检查是否已经收藏
|
||||
const [existingFavorite] = await db
|
||||
.select()
|
||||
.from(userFavoriteAssistants)
|
||||
.where(
|
||||
and(
|
||||
eq(userFavoriteAssistants.userId, userId),
|
||||
eq(userFavoriteAssistants.assistantId, assistantId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (existingFavorite) {
|
||||
return NextResponse.json({ message: 'Already favorited' }, { status: 200 });
|
||||
}
|
||||
|
||||
// 添加收藏
|
||||
const [newFavorite] = await db
|
||||
.insert(userFavoriteAssistants)
|
||||
.values({
|
||||
userId,
|
||||
assistantId,
|
||||
})
|
||||
.returning();
|
||||
|
||||
return NextResponse.json(newFavorite, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error('Failed to favorite assistant:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to favorite assistant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/assistants/[id]/favorite - 取消收藏
|
||||
export async function DELETE(request: NextRequest, { params }: RouteParams) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const assistantId = parseInt(id);
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const userId = searchParams.get('userId');
|
||||
|
||||
if (isNaN(assistantId)) {
|
||||
return NextResponse.json({ error: 'Invalid assistant ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: 'User ID is required' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 删除收藏记录
|
||||
await db
|
||||
.delete(userFavoriteAssistants)
|
||||
.where(
|
||||
and(
|
||||
eq(userFavoriteAssistants.userId, userId),
|
||||
eq(userFavoriteAssistants.assistantId, assistantId)
|
||||
)
|
||||
);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to unfavorite assistant:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to unfavorite assistant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
230
src/app/api/assistants/[id]/route.ts
Normal file
230
src/app/api/assistants/[id]/route.ts
Normal file
@ -0,0 +1,230 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/drizzle/db';
|
||||
import { assistants, assistantCategories, userFavoriteAssistants } from '@/drizzle/schema';
|
||||
import { eq, and, sql } from 'drizzle-orm';
|
||||
|
||||
interface RouteParams {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
// GET /api/assistants/[id] - 获取助手详情
|
||||
export async function GET(request: NextRequest, { params }: RouteParams) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const assistantId = parseInt(id);
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const userId = searchParams.get('userId');
|
||||
|
||||
if (isNaN(assistantId)) {
|
||||
return NextResponse.json({ error: 'Invalid assistant ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
const [assistant] = await db
|
||||
.select({
|
||||
id: assistants.id,
|
||||
categoryId: assistants.categoryId,
|
||||
userId: assistants.userId,
|
||||
name: assistants.name,
|
||||
description: assistants.description,
|
||||
icon: assistants.icon,
|
||||
systemPrompt: assistants.systemPrompt,
|
||||
tags: assistants.tags,
|
||||
isBuiltin: assistants.isBuiltin,
|
||||
isEnabled: assistants.isEnabled,
|
||||
sortOrder: assistants.sortOrder,
|
||||
useCount: assistants.useCount,
|
||||
createdAt: assistants.createdAt,
|
||||
updatedAt: assistants.updatedAt,
|
||||
categoryName: assistantCategories.name,
|
||||
categoryIcon: assistantCategories.icon,
|
||||
})
|
||||
.from(assistants)
|
||||
.leftJoin(assistantCategories, eq(assistants.categoryId, assistantCategories.id))
|
||||
.where(eq(assistants.id, assistantId))
|
||||
.limit(1);
|
||||
|
||||
if (!assistant) {
|
||||
return NextResponse.json({ error: 'Assistant not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 查询收藏状态
|
||||
let isFavorited = false;
|
||||
if (userId) {
|
||||
const [favorite] = await db
|
||||
.select()
|
||||
.from(userFavoriteAssistants)
|
||||
.where(
|
||||
and(
|
||||
eq(userFavoriteAssistants.userId, userId),
|
||||
eq(userFavoriteAssistants.assistantId, assistantId)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
isFavorited = !!favorite;
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
...assistant,
|
||||
isFavorited,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch assistant:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch assistant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT /api/assistants/[id] - 更新助手
|
||||
export async function PUT(request: NextRequest, { params }: RouteParams) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const assistantId = parseInt(id);
|
||||
const body = await request.json();
|
||||
const { userId, categoryId, name, description, icon, systemPrompt, tags } = body;
|
||||
|
||||
if (isNaN(assistantId)) {
|
||||
return NextResponse.json({ error: 'Invalid assistant ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 查询助手信息
|
||||
const [existingAssistant] = await db
|
||||
.select()
|
||||
.from(assistants)
|
||||
.where(eq(assistants.id, assistantId))
|
||||
.limit(1);
|
||||
|
||||
if (!existingAssistant) {
|
||||
return NextResponse.json({ error: 'Assistant not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 检查权限:只能编辑自己创建的助手,内置助手不可编辑
|
||||
if (existingAssistant.isBuiltin) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Cannot edit built-in assistant' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
if (existingAssistant.userId && existingAssistant.userId !== userId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Not authorized to edit this assistant' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
// 更新助手
|
||||
const [updatedAssistant] = await db
|
||||
.update(assistants)
|
||||
.set({
|
||||
categoryId: categoryId !== undefined ? categoryId : existingAssistant.categoryId,
|
||||
name: name || existingAssistant.name,
|
||||
description: description !== undefined ? description : existingAssistant.description,
|
||||
icon: icon || existingAssistant.icon,
|
||||
systemPrompt: systemPrompt || existingAssistant.systemPrompt,
|
||||
tags: tags !== undefined ? tags : existingAssistant.tags,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(assistants.id, assistantId))
|
||||
.returning();
|
||||
|
||||
return NextResponse.json(updatedAssistant);
|
||||
} catch (error) {
|
||||
console.error('Failed to update assistant:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to update assistant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE /api/assistants/[id] - 删除助手
|
||||
export async function DELETE(request: NextRequest, { params }: RouteParams) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const assistantId = parseInt(id);
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const userId = searchParams.get('userId');
|
||||
|
||||
if (isNaN(assistantId)) {
|
||||
return NextResponse.json({ error: 'Invalid assistant ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 查询助手信息
|
||||
const [existingAssistant] = await db
|
||||
.select()
|
||||
.from(assistants)
|
||||
.where(eq(assistants.id, assistantId))
|
||||
.limit(1);
|
||||
|
||||
if (!existingAssistant) {
|
||||
return NextResponse.json({ error: 'Assistant not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 检查权限:只能删除自己创建的助手,内置助手不可删除
|
||||
if (existingAssistant.isBuiltin) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Cannot delete built-in assistant' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
if (existingAssistant.userId && existingAssistant.userId !== userId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Not authorized to delete this assistant' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
// 删除相关收藏记录
|
||||
await db
|
||||
.delete(userFavoriteAssistants)
|
||||
.where(eq(userFavoriteAssistants.assistantId, assistantId));
|
||||
|
||||
// 删除助手
|
||||
await db.delete(assistants).where(eq(assistants.id, assistantId));
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to delete assistant:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to delete assistant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH /api/assistants/[id] - 增加使用次数
|
||||
export async function PATCH(request: NextRequest, { params }: RouteParams) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const assistantId = parseInt(id);
|
||||
const body = await request.json();
|
||||
const { action } = body;
|
||||
|
||||
if (isNaN(assistantId)) {
|
||||
return NextResponse.json({ error: 'Invalid assistant ID' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (action === 'incrementUseCount') {
|
||||
await db
|
||||
.update(assistants)
|
||||
.set({
|
||||
useCount: sql`${assistants.useCount} + 1`,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(assistants.id, assistantId));
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
|
||||
} catch (error) {
|
||||
console.error('Failed to update assistant:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to update assistant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
23
src/app/api/assistants/categories/route.ts
Normal file
23
src/app/api/assistants/categories/route.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/drizzle/db';
|
||||
import { assistantCategories } from '@/drizzle/schema';
|
||||
import { eq, asc } from 'drizzle-orm';
|
||||
|
||||
// GET /api/assistants/categories - 获取所有助手分类
|
||||
export async function GET() {
|
||||
try {
|
||||
const categories = await db
|
||||
.select()
|
||||
.from(assistantCategories)
|
||||
.where(eq(assistantCategories.isEnabled, true))
|
||||
.orderBy(asc(assistantCategories.sortOrder));
|
||||
|
||||
return NextResponse.json(categories);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch assistant categories:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch categories' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
130
src/app/api/assistants/recent/route.ts
Normal file
130
src/app/api/assistants/recent/route.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/drizzle/db';
|
||||
import { conversations, assistants, assistantCategories } from '@/drizzle/schema';
|
||||
import { eq, desc, isNotNull, and, asc } from 'drizzle-orm';
|
||||
import { getCurrentUser } from '@/lib/auth';
|
||||
|
||||
// GET /api/assistants/recent - 获取最近使用的助手
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// 获取当前用户
|
||||
const user = await getCurrentUser();
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ error: '未登录' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const limit = parseInt(searchParams.get('limit') || '5');
|
||||
|
||||
// 查询用户最近使用过助手的对话(按时间倒序,去重)
|
||||
const recentConversations = await db
|
||||
.selectDistinctOn([conversations.assistantId], {
|
||||
assistantId: conversations.assistantId,
|
||||
lastUsedAt: conversations.lastMessageAt,
|
||||
})
|
||||
.from(conversations)
|
||||
.where(
|
||||
and(
|
||||
eq(conversations.userId, user.userId),
|
||||
isNotNull(conversations.assistantId)
|
||||
)
|
||||
)
|
||||
.orderBy(conversations.assistantId, desc(conversations.lastMessageAt))
|
||||
.limit(limit * 2); // 多取一些,因为可能有些助手已被删除
|
||||
|
||||
if (recentConversations.length === 0) {
|
||||
return NextResponse.json({ data: [] });
|
||||
}
|
||||
|
||||
// 获取助手详细信息
|
||||
const assistantIds = recentConversations
|
||||
.map((c) => c.assistantId)
|
||||
.filter((id): id is number => id !== null);
|
||||
|
||||
if (assistantIds.length === 0) {
|
||||
return NextResponse.json({ data: [] });
|
||||
}
|
||||
|
||||
// 查询助手详情(带分类信息)
|
||||
const assistantsList = await db
|
||||
.select({
|
||||
id: assistants.id,
|
||||
categoryId: assistants.categoryId,
|
||||
name: assistants.name,
|
||||
description: assistants.description,
|
||||
icon: assistants.icon,
|
||||
systemPrompt: assistants.systemPrompt,
|
||||
tags: assistants.tags,
|
||||
isBuiltin: assistants.isBuiltin,
|
||||
useCount: assistants.useCount,
|
||||
categoryName: assistantCategories.name,
|
||||
categoryIcon: assistantCategories.icon,
|
||||
})
|
||||
.from(assistants)
|
||||
.leftJoin(assistantCategories, eq(assistants.categoryId, assistantCategories.id))
|
||||
.where(
|
||||
and(
|
||||
eq(assistants.isEnabled, true),
|
||||
// 使用 SQL IN 查询
|
||||
eq(assistants.id, assistantIds[0]) // 临时解决方案
|
||||
)
|
||||
);
|
||||
|
||||
// 由于 drizzle 的 IN 语法较复杂,改用循环查询
|
||||
const allAssistants = [];
|
||||
for (const id of assistantIds.slice(0, limit)) {
|
||||
const [assistant] = await db
|
||||
.select({
|
||||
id: assistants.id,
|
||||
categoryId: assistants.categoryId,
|
||||
name: assistants.name,
|
||||
description: assistants.description,
|
||||
icon: assistants.icon,
|
||||
systemPrompt: assistants.systemPrompt,
|
||||
tags: assistants.tags,
|
||||
isBuiltin: assistants.isBuiltin,
|
||||
useCount: assistants.useCount,
|
||||
categoryName: assistantCategories.name,
|
||||
categoryIcon: assistantCategories.icon,
|
||||
})
|
||||
.from(assistants)
|
||||
.leftJoin(assistantCategories, eq(assistants.categoryId, assistantCategories.id))
|
||||
.where(
|
||||
and(
|
||||
eq(assistants.isEnabled, true),
|
||||
eq(assistants.id, id)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
if (assistant) {
|
||||
// 找到对应的 lastUsedAt
|
||||
const conv = recentConversations.find((c) => c.assistantId === id);
|
||||
allAssistants.push({
|
||||
...assistant,
|
||||
lastUsedAt: conv?.lastUsedAt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按最后使用时间排序
|
||||
allAssistants.sort((a, b) => {
|
||||
const timeA = a.lastUsedAt ? new Date(a.lastUsedAt).getTime() : 0;
|
||||
const timeB = b.lastUsedAt ? new Date(b.lastUsedAt).getTime() : 0;
|
||||
return timeB - timeA;
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
data: allAssistants.slice(0, limit),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch recent assistants:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch recent assistants' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
172
src/app/api/assistants/route.ts
Normal file
172
src/app/api/assistants/route.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/drizzle/db';
|
||||
import { assistants, assistantCategories, userFavoriteAssistants } from '@/drizzle/schema';
|
||||
import { eq, and, or, ilike, asc, desc, sql, isNull } from 'drizzle-orm';
|
||||
|
||||
// GET /api/assistants - 获取助手列表
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const categoryId = searchParams.get('categoryId');
|
||||
const search = searchParams.get('search');
|
||||
const userId = searchParams.get('userId'); // 用于获取用户自定义助手
|
||||
const onlyFavorites = searchParams.get('favorites') === 'true';
|
||||
const page = parseInt(searchParams.get('page') || '1');
|
||||
const limit = parseInt(searchParams.get('limit') || '50');
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 构建查询条件
|
||||
const conditions = [eq(assistants.isEnabled, true)];
|
||||
|
||||
// 按分类筛选
|
||||
if (categoryId && categoryId !== 'all') {
|
||||
conditions.push(eq(assistants.categoryId, parseInt(categoryId)));
|
||||
}
|
||||
|
||||
// 搜索条件(按名称或描述)
|
||||
if (search) {
|
||||
conditions.push(
|
||||
or(
|
||||
ilike(assistants.name, `%${search}%`),
|
||||
ilike(assistants.description, `%${search}%`)
|
||||
)!
|
||||
);
|
||||
}
|
||||
|
||||
// 用户自定义助手或系统内置助手
|
||||
// 如果有 userId,显示系统助手和该用户创建的助手
|
||||
if (userId) {
|
||||
conditions.push(
|
||||
or(
|
||||
isNull(assistants.userId), // 系统内置
|
||||
eq(assistants.userId, userId) // 用户自己创建的
|
||||
)!
|
||||
);
|
||||
} else {
|
||||
// 没有 userId 时只显示系统内置助手
|
||||
conditions.push(isNull(assistants.userId));
|
||||
}
|
||||
|
||||
// 如果只获取收藏的助手
|
||||
if (onlyFavorites && userId) {
|
||||
const favoriteIds = await db
|
||||
.select({ assistantId: userFavoriteAssistants.assistantId })
|
||||
.from(userFavoriteAssistants)
|
||||
.where(eq(userFavoriteAssistants.userId, userId));
|
||||
|
||||
if (favoriteIds.length > 0) {
|
||||
const ids = favoriteIds.map((f) => f.assistantId);
|
||||
conditions.push(sql`${assistants.id} IN (${sql.join(ids, sql`, `)})`);
|
||||
} else {
|
||||
// 没有收藏则返回空数组
|
||||
return NextResponse.json({
|
||||
data: [],
|
||||
total: 0,
|
||||
page,
|
||||
limit,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 查询助手列表(带分类信息)
|
||||
const assistantsList = await db
|
||||
.select({
|
||||
id: assistants.id,
|
||||
categoryId: assistants.categoryId,
|
||||
userId: assistants.userId,
|
||||
name: assistants.name,
|
||||
description: assistants.description,
|
||||
icon: assistants.icon,
|
||||
systemPrompt: assistants.systemPrompt,
|
||||
tags: assistants.tags,
|
||||
isBuiltin: assistants.isBuiltin,
|
||||
sortOrder: assistants.sortOrder,
|
||||
useCount: assistants.useCount,
|
||||
createdAt: assistants.createdAt,
|
||||
categoryName: assistantCategories.name,
|
||||
categoryIcon: assistantCategories.icon,
|
||||
})
|
||||
.from(assistants)
|
||||
.leftJoin(assistantCategories, eq(assistants.categoryId, assistantCategories.id))
|
||||
.where(and(...conditions))
|
||||
.orderBy(desc(assistants.isBuiltin), asc(assistants.sortOrder), desc(assistants.useCount))
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
// 如果有 userId,查询收藏状态
|
||||
let favoritesSet = new Set<number>();
|
||||
if (userId) {
|
||||
const userFavorites = await db
|
||||
.select({ assistantId: userFavoriteAssistants.assistantId })
|
||||
.from(userFavoriteAssistants)
|
||||
.where(eq(userFavoriteAssistants.userId, userId));
|
||||
favoritesSet = new Set(userFavorites.map((f) => f.assistantId));
|
||||
}
|
||||
|
||||
// 添加收藏状态到结果
|
||||
const result = assistantsList.map((assistant) => ({
|
||||
...assistant,
|
||||
isFavorited: favoritesSet.has(assistant.id),
|
||||
}));
|
||||
|
||||
// 获取总数
|
||||
const totalResult = await db
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(assistants)
|
||||
.where(and(...conditions));
|
||||
const total = Number(totalResult[0]?.count || 0);
|
||||
|
||||
return NextResponse.json({
|
||||
data: result,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch assistants:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch assistants' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/assistants - 创建新助手
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { userId, categoryId, name, description, icon, systemPrompt, tags } = body;
|
||||
|
||||
if (!name || !systemPrompt) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Name and system prompt are required' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const [newAssistant] = await db
|
||||
.insert(assistants)
|
||||
.values({
|
||||
userId: userId || null, // null 表示系统内置
|
||||
categoryId: categoryId || null,
|
||||
name,
|
||||
description: description || null,
|
||||
icon: icon || '🤖',
|
||||
systemPrompt,
|
||||
tags: tags || [],
|
||||
isBuiltin: false, // 用户创建的不是内置
|
||||
isEnabled: true,
|
||||
sortOrder: 0,
|
||||
useCount: 0,
|
||||
})
|
||||
.returning();
|
||||
|
||||
return NextResponse.json(newAssistant, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error('Failed to create assistant:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to create assistant' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user