语音转文本接口,bgm路径修复
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { getFileUrl } from "@/lib/minio";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
@@ -10,7 +11,6 @@ export async function GET(
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const skip = parseInt(searchParams.get("skip") || "0", 10);
|
||||
const take = parseInt(searchParams.get("take") || "20", 10);
|
||||
const mode = (searchParams.get("mode") || "").toLowerCase();
|
||||
|
||||
// ranked 模式参数(均为可选,提供合理默认值)
|
||||
const seed = searchParams.get("seed") || new Date().toISOString().slice(0, 10); // 默认按日期稳定
|
||||
@@ -21,6 +21,8 @@ export async function GET(
|
||||
const wTime = parseFloat(searchParams.get("wTime") || "2"); // 时间衰减权重
|
||||
const wJit = parseFloat(searchParams.get("wJit") || "10"); // 随机扰动权重(稳定随机)
|
||||
|
||||
|
||||
|
||||
try {
|
||||
// 查找是视频还是图文
|
||||
const [video, post] = await Promise.all([
|
||||
@@ -43,12 +45,11 @@ export async function GET(
|
||||
? { videoId: awemeId }
|
||||
: { imagePostId: awemeId };
|
||||
|
||||
// ranked 模式:按「热度 + 时间衰减 + 稳定随机扰动」打分排序;否则使用稳定的时间排序
|
||||
if (mode === "ranked") {
|
||||
const total = await prisma.comment.count({ where });
|
||||
// 按「热度 + 时间衰减 + 稳定随机扰动」打分排序;否则使用稳定的时间排序
|
||||
const total = await prisma.comment.count({ where });
|
||||
|
||||
// 计算稳定随机扰动:基于 (cid, seed) 的 md5 -> 取前4字节 -> 映射到 [0,1)
|
||||
const jitterExpr = Prisma.sql`(
|
||||
// 计算稳定随机扰动:基于 (cid, seed) 的 md5 -> 取前4字节 -> 映射到 [0,1)
|
||||
const jitterExpr = Prisma.sql`(
|
||||
(
|
||||
get_byte(decode(md5(c."cid" || ':' || ${seed}), 'hex'), 0)::double precision * 16777216.0 +
|
||||
get_byte(decode(md5(c."cid" || ':' || ${seed}), 'hex'), 1)::double precision * 65536.0 +
|
||||
@@ -57,27 +58,27 @@ export async function GET(
|
||||
) / 4294967295.0
|
||||
)`;
|
||||
|
||||
const snapshotTs = snapshot.toISOString();
|
||||
const snapshotTs = snapshot.toISOString();
|
||||
|
||||
// popularity: ln(1 + digg_count)
|
||||
// time decay: exp(- age_hours / halfLifeHours),使用固定 snapshot 基准保证会话内稳定
|
||||
const scoreExpr = Prisma.sql`(
|
||||
// popularity: ln(1 + digg_count)
|
||||
// time decay: exp(- age_hours / halfLifeHours),使用固定 snapshot 基准保证会话内稳定
|
||||
const scoreExpr = Prisma.sql`(
|
||||
${wPop} * ln(1 + c."digg_count"::numeric) +
|
||||
${wTime} * exp(- (extract(epoch from (${snapshotTs}::timestamptz - c."created_at")) / 3600.0) / ${halfLifeHours}) +
|
||||
${wJit} * ${jitterExpr}
|
||||
)`;
|
||||
|
||||
const whereField = (await video) ? Prisma.sql`c."videoId"` : Prisma.sql`c."imagePostId"`;
|
||||
const whereField = (await video) ? Prisma.sql`c."videoId"` : Prisma.sql`c."imagePostId"`;
|
||||
|
||||
const rows: Array<{
|
||||
cid: string;
|
||||
text: string;
|
||||
created_at: Date;
|
||||
digg_count: bigint;
|
||||
nickname: string;
|
||||
avatar_url: string | null;
|
||||
}> = await prisma.$queryRaw(
|
||||
Prisma.sql`
|
||||
const rows: Array<{
|
||||
cid: string;
|
||||
text: string;
|
||||
created_at: Date;
|
||||
digg_count: bigint;
|
||||
nickname: string;
|
||||
avatar_url: string | null;
|
||||
}> = await prisma.$queryRaw(
|
||||
Prisma.sql`
|
||||
SELECT
|
||||
c."cid",
|
||||
c."text",
|
||||
@@ -92,78 +93,44 @@ export async function GET(
|
||||
OFFSET ${skip}
|
||||
LIMIT ${take}
|
||||
`
|
||||
);
|
||||
);
|
||||
|
||||
// 批量查询每条评论的配图/贴纸
|
||||
const cids = rows.map(r => r.cid);
|
||||
const images = cids.length
|
||||
? await prisma.commentImage.findMany({
|
||||
where: { commentId: { in: cids } },
|
||||
orderBy: { order: 'asc' },
|
||||
select: { commentId: true, url: true, width: true, height: true, order: true },
|
||||
})
|
||||
: [];
|
||||
const group = new Map<string, { url: string; width?: number | null; height?: number | null; order: number }[]>();
|
||||
for (const img of images) {
|
||||
const arr = group.get(img.commentId) || [];
|
||||
arr.push({ url: img.url, width: img.width, height: img.height, order: img.order });
|
||||
group.set(img.commentId, arr);
|
||||
}
|
||||
const formattedComments = rows.map((c) => ({
|
||||
cid: c.cid,
|
||||
text: c.text,
|
||||
created_at: c.created_at,
|
||||
digg_count: Number(c.digg_count),
|
||||
user: {
|
||||
nickname: c.nickname,
|
||||
avatar_url: c.avatar_url || undefined,
|
||||
},
|
||||
images: (group.get(c.cid) || []).sort((a,b)=>a.order-b.order).map(i=>({ url: i.url, width: i.width ?? undefined, height: i.height ?? undefined })),
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
comments: formattedComments,
|
||||
total,
|
||||
hasMore: skip + take < total,
|
||||
// 回传用于稳定排序的参数,前端可在后续请求中透传
|
||||
seed,
|
||||
snapshot: snapshotTs,
|
||||
weights: { wPop, wTime, wJit, halfLifeHours },
|
||||
mode: "ranked",
|
||||
});
|
||||
// 批量查询每条评论的配图/贴纸
|
||||
const cids = rows.map(r => r.cid);
|
||||
const images = cids.length
|
||||
? await prisma.commentImage.findMany({
|
||||
where: { commentId: { in: cids } },
|
||||
orderBy: { order: 'asc' },
|
||||
select: { commentId: true, url: true, width: true, height: true, order: true },
|
||||
})
|
||||
: [];
|
||||
const group = new Map<string, { url: string; width?: number | null; height?: number | null; order: number }[]>();
|
||||
for (const img of images) {
|
||||
const arr = group.get(img.commentId) || [];
|
||||
arr.push({ url: img.url, width: img.width, height: img.height, order: img.order });
|
||||
group.set(img.commentId, arr);
|
||||
}
|
||||
|
||||
// 默认模式:获取评论总数和分页数据(使用稳定且不可变的排序,避免因 digg_count 变化导致翻页重复/遗漏)
|
||||
const [total, comments] = await Promise.all([
|
||||
prisma.comment.count({ where }),
|
||||
prisma.comment.findMany({
|
||||
where,
|
||||
orderBy: [
|
||||
{ created_at: "desc" },
|
||||
{ cid: "asc" },
|
||||
],
|
||||
include: { user: true, images: { orderBy: { order: 'asc' }, select: { url: true, width: true, height: true } } },
|
||||
skip,
|
||||
take,
|
||||
}),
|
||||
]);
|
||||
|
||||
const formattedComments = comments.map((c) => ({
|
||||
const formattedComments = rows.map((c) => ({
|
||||
cid: c.cid,
|
||||
text: c.text,
|
||||
created_at: c.created_at,
|
||||
digg_count: Number(c.digg_count),
|
||||
user: {
|
||||
nickname: c.user.nickname,
|
||||
avatar_url: c.user.avatar_url,
|
||||
nickname: c.nickname,
|
||||
avatar_url: getFileUrl(c.avatar_url || 'default_avatar.png'),
|
||||
},
|
||||
images: (c.images || []).map(i=>({ url: i.url, width: i.width ?? undefined, height: i.height ?? undefined })),
|
||||
images: (group.get(c.cid) || []).sort((a, b) => a.order - b.order).map(i => ({ url: getFileUrl(i.url), width: i.width ?? undefined, height: i.height ?? undefined })),
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
comments: formattedComments,
|
||||
total,
|
||||
hasMore: skip + take < total,
|
||||
// 回传用于稳定排序的参数,前端可在后续请求中透传
|
||||
seed,
|
||||
snapshot: snapshotTs,
|
||||
weights: { wPop, wTime, wJit, halfLifeHours },
|
||||
mode: "ranked",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("获取评论失败:", error);
|
||||
|
||||
Reference in New Issue
Block a user