feat: 添加分页功能和缓存管理,优化问答记录展示
This commit is contained in:
+21
-17
@@ -1,4 +1,6 @@
|
||||
// server/api/search.ts - OCS AnswererWrapper 兼容搜索接口
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
import {
|
||||
getQuery,
|
||||
type H3Event,
|
||||
@@ -16,11 +18,10 @@ import {
|
||||
type SearchParams
|
||||
} from "~~/server/utils/answer";
|
||||
import { getAuthSession } from "~~/server/utils/auth";
|
||||
import { answerCache } from "~~/server/utils/cache";
|
||||
import { prisma } from "~~/server/utils/db";
|
||||
import { createApiLogger, toSafeLogError } from "~~/server/utils/logging";
|
||||
import { askAnswerStream } from "~~/server/utils/openai";
|
||||
import { addQaRecord } from "~~/server/utils/runtimeState";
|
||||
import { addQaRecord, lookupCachedAnswer } from "~~/server/utils/runtimeState";
|
||||
|
||||
/**
|
||||
* 把 query/body/form 中的值统一转成字符串
|
||||
@@ -138,17 +139,25 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
// 鉴权:优先 session(浏览器登录),无 session 则用 body.token 查 apiToken
|
||||
// OCS 油猴脚本跨域无法携带 cookie,需在 body 中传入用户自己的 apiToken
|
||||
// 同时取出 cacheClearedAt,用于 DB 缓存过滤
|
||||
let userId: string | null = null;
|
||||
let cacheClearedAt: Date | null = null;
|
||||
|
||||
const session = await getAuthSession(event);
|
||||
if (session) {
|
||||
userId = session.user.id;
|
||||
const userData = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { cacheClearedAt: true }
|
||||
});
|
||||
cacheClearedAt = userData?.cacheClearedAt ?? null;
|
||||
} else if (params.token) {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { apiToken: params.token },
|
||||
select: { id: true }
|
||||
select: { id: true, cacheClearedAt: true }
|
||||
});
|
||||
userId = user?.id ?? null;
|
||||
cacheClearedAt = user?.cacheClearedAt ?? null;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
@@ -167,12 +176,12 @@ export default defineEventHandler(async (event) => {
|
||||
return createOcsErrorResponse("未提供问题内容");
|
||||
}
|
||||
|
||||
// 缓存 key 包含题目、题型和选项;同题不同选项不能共用答案
|
||||
const cachedAnswer = answerCache?.get(
|
||||
params.title,
|
||||
params.type,
|
||||
params.options
|
||||
);
|
||||
// 计算缓存 key:题目+题型+选项的 MD5;同题不同选项不能共用答案
|
||||
const hash = createHash("md5")
|
||||
.update(`${params.title}|${params.type}|${params.options}`, "utf8")
|
||||
.digest("hex");
|
||||
|
||||
const cachedAnswer = await lookupCachedAnswer(userId, hash, cacheClearedAt);
|
||||
|
||||
if (cachedAnswer) {
|
||||
logger.info("cache_hit");
|
||||
@@ -191,19 +200,14 @@ export default defineEventHandler(async (event) => {
|
||||
});
|
||||
const processedAnswer = extractAnswer(streamResult.answer, params.type);
|
||||
|
||||
// 先缓存再记录;这两步失败风险很低,且都是内存操作,不会阻塞主链路
|
||||
answerCache?.set(
|
||||
params.title,
|
||||
processedAnswer,
|
||||
params.type,
|
||||
params.options
|
||||
);
|
||||
// 写入 DB 记录(带 hash);记录本身即为后续缓存查询的来源
|
||||
await addQaRecord({
|
||||
userId,
|
||||
question: params.title,
|
||||
type: params.type,
|
||||
options: params.options,
|
||||
answer: processedAnswer
|
||||
answer: processedAnswer,
|
||||
hash
|
||||
});
|
||||
|
||||
logger.info("finish_success", {
|
||||
|
||||
Reference in New Issue
Block a user