diff --git a/app/composables/useAgentChat.ts b/app/composables/useAgentChat.ts index 60c7635..b88f955 100644 --- a/app/composables/useAgentChat.ts +++ b/app/composables/useAgentChat.ts @@ -503,14 +503,15 @@ export function useAgentChat(options: UseAgentChatOptions) { async function sendFeedback(messageId: string, feedback: "like" | "dislike") { if (!auth.loggedIn.value) return; + const msg = messages.value.find((m) => m.id === messageId); + const newFeedback = msg?.feedback === feedback ? null : feedback; try { await $fetch("/api/agent/feedback", { method: "POST", - body: { messageId, feedback }, + body: { messageId, feedback: newFeedback }, }); - const msg = messages.value.find((m) => m.id === messageId); if (msg) { - msg.feedback = msg.feedback === feedback ? null : feedback; + msg.feedback = newFeedback; } } catch { } diff --git a/packages/drizzle-pkg/db.sqlite b/packages/drizzle-pkg/db.sqlite index 1cfa76e..eb64840 100644 Binary files a/packages/drizzle-pkg/db.sqlite and b/packages/drizzle-pkg/db.sqlite differ diff --git a/server/api/agent/feedback.post.ts b/server/api/agent/feedback.post.ts index 6ce93d1..50b7004 100644 --- a/server/api/agent/feedback.post.ts +++ b/server/api/agent/feedback.post.ts @@ -15,11 +15,11 @@ export default defineWrappedResponseHandler(async (event) => { const body = await readBody(event); const { messageId, feedback, comment } = body as { messageId: string; - feedback: "like" | "dislike"; + feedback: "like" | "dislike" | null; comment?: string; }; - if (!messageId || !feedback || !["like", "dislike"].includes(feedback)) { + if (!messageId || (feedback !== null && !["like", "dislike"].includes(feedback))) { return R.error("参数无效", null); } @@ -39,6 +39,15 @@ export default defineWrappedResponseHandler(async (event) => { .where(and(eq(agentMessageFeedback.messageId, messageId), eq(agentMessageFeedback.userId, user.id))) .limit(1); + if (feedback === null) { + if (existing) { + await dbGlobal + .delete(agentMessageFeedback) + .where(eq(agentMessageFeedback.id, existing.id)); + } + return R.success({ messageId, feedback: null }); + } + if (existing) { await dbGlobal .update(agentMessageFeedback) diff --git a/server/api/agent/sessions/[id]/messages.get.ts b/server/api/agent/sessions/[id]/messages.get.ts index 92d576a..43c866f 100644 --- a/server/api/agent/sessions/[id]/messages.get.ts +++ b/server/api/agent/sessions/[id]/messages.get.ts @@ -19,6 +19,6 @@ export default defineWrappedResponseHandler(async (event) => { const before = query.before ? Number(query.before) : undefined; const limit = Number(query.limit) || 50; - const messages = await getMessagesBySession(id, { before, limit }); + const messages = await getMessagesBySession(id, { before, limit, userId: identity.userId }); return R.success({ messages, hasMore: messages.length === limit }); }); diff --git a/server/service/agent/session.ts b/server/service/agent/session.ts index 2259b7a..5fc587a 100644 --- a/server/service/agent/session.ts +++ b/server/service/agent/session.ts @@ -1,7 +1,7 @@ import { dbGlobal } from "drizzle-pkg/lib/db"; import { agentSessions, agentMessages, agentMessageFeedback } from "drizzle-pkg/lib/schema/agent"; import { eq, desc, asc, and, lt, isNull, sql, max } from "drizzle-orm"; -import type { AgentSessionRow, AgentMessageRow } from "./types"; +import type { AgentSessionRow, AgentMessageRow, AgentMessageWithFeedback } from "./types"; export async function createSession(params: { userId?: number | null; @@ -118,28 +118,47 @@ export async function softDeleteSession(id: string): Promise { export async function getMessagesBySession( sessionId: string, - params?: { before?: number; limit?: number; latest?: boolean }, -): Promise { - const { before, limit = 50, latest = false } = params ?? {}; + params?: { before?: number; limit?: number; latest?: boolean; userId?: number | null }, +): Promise { + const { before, limit = 50, latest = false, userId = null } = params ?? {}; const conditions = [eq(agentMessages.sessionId, sessionId)]; if (before !== undefined) { conditions.push(lt(agentMessages.sortOrder, before)); } - if (latest) { - const rows = await dbGlobal - .select() + + const baseQuery = () => + dbGlobal + .select({ + message: agentMessages, + feedback: agentMessageFeedback.feedback, + }) .from(agentMessages) - .where(and(...conditions)) + .leftJoin( + agentMessageFeedback, + and( + eq(agentMessageFeedback.messageId, agentMessages.id), + userId ? eq(agentMessageFeedback.userId, userId) : sql`false`, + ), + ) + .where(and(...conditions)); + + if (latest) { + const rows = await baseQuery() .orderBy(desc(agentMessages.sortOrder)) .limit(limit); - return rows.reverse(); + const reversed = rows.reverse(); + return reversed.map((r) => ({ + ...r.message, + feedback: (r.feedback ?? null) as "like" | "dislike" | null, + })); } - return dbGlobal - .select() - .from(agentMessages) - .where(and(...conditions)) + const rows = await baseQuery() .orderBy(asc(agentMessages.sortOrder)) .limit(limit); + return rows.map((r) => ({ + ...r.message, + feedback: (r.feedback ?? null) as "like" | "dislike" | null, + })); } export async function getMaxSortOrder(sessionId: string): Promise { diff --git a/server/service/agent/types.ts b/server/service/agent/types.ts index 331312c..3cf49fc 100644 --- a/server/service/agent/types.ts +++ b/server/service/agent/types.ts @@ -4,6 +4,10 @@ export type AgentSessionRow = typeof agentSessions.$inferSelect; export type AgentMessageRow = typeof agentMessages.$inferSelect; export type AgentMessageFeedbackRow = typeof agentMessageFeedback.$inferSelect; +export interface AgentMessageWithFeedback extends AgentMessageRow { + feedback: "like" | "dislike" | null; +} + export interface AgentIdentity { userId: number | null; tempToken: string | null;