0.3.5 共享功能修复

This commit is contained in:
liaibo
2026-01-24 12:32:51 +08:00
parent 25923f308c
commit 3c3f407f4b
44 changed files with 3754 additions and 420 deletions
+100
View File
@@ -5,6 +5,7 @@ import { api, internal } from "./_generated/api";
import { lightragIngestText } from "./_utils/lightrag";
import { extractTextFromDocumentContent, extractTextFromMindmapData } from "./_utils/text";
import { enqueueIngestDocumentJob, enqueueIngestMediaAssetJob, enqueueIngestMindmapJob } from "./_utils/ingestJobs";
import { extractTextFromAttachment } from "./_utils/attachmentExtract";
export const get = query({
args: { userId: v.string(), id: v.string() },
@@ -199,6 +200,105 @@ export const run = internalAction({
return;
}
if (job.type === "extract.media_asset_text") {
const assetId = String(job.payload?.assetId ?? "").trim();
if (!assetId) throw new Error("缺少 assetId");
const asset = await ctx.runQuery(api.mediaAssets.getById, { userId: job.user_id, id: assetId });
if (!asset) throw new Error("资源不存在或无权限");
// 说明:只处理 file 类型的常见附件(pdf/docx/pptx/xlsx)。
if (String(asset.asset_type ?? "") !== "file") {
await ctx.runMutation(api.mediaAssets.patchById, {
userId: job.user_id,
id: assetId,
patch: {
ocr_status: "skipped",
ocr_payload: { reason: "non_file_asset" },
ocr_strategy: "attachment_extract",
},
});
await ctx.runMutation(internal.jobs.finishSuccess, {
id: args.id,
result: { ok: true, kind: "extract_media_asset_text", assetId, skipped: true, reason: "non_file_asset" },
});
return;
}
const fileSize = typeof asset.file_size === "number" ? asset.file_size : null;
const maxBytes = 25 * 1024 * 1024;
if (typeof fileSize === "number" && fileSize > maxBytes) {
await ctx.runMutation(api.mediaAssets.patchById, {
userId: job.user_id,
id: assetId,
patch: {
ocr_status: "failed",
ocr_payload: { error: `文件过大(${fileSize} bytes),暂不解析`, maxBytes },
ocr_strategy: "attachment_extract",
},
});
await ctx.runMutation(internal.jobs.finishFailure, {
id: args.id,
error: "文件过大,暂不解析",
});
return;
}
// 说明:Convex Files 的 getUrl 可能过期,先刷新并获取当前可用链接。
const refreshed = await ctx.runMutation(api.mediaAssets.refreshUrl, { userId: job.user_id, id: assetId });
const url = String((refreshed as any)?.signedUrl ?? asset.file_url ?? "").trim();
if (!url) throw new Error("缺少可用文件链接");
const res = await fetch(url);
if (!res.ok) {
throw new Error(`下载附件失败:${res.status}`);
}
const bytes = await res.arrayBuffer();
const extracted = await extractTextFromAttachment({
mimeType: (asset as any).mime_type ?? null,
fileName: (asset as any).file_name ?? null,
bytes,
});
if (!extracted.ok) {
await ctx.runMutation(api.mediaAssets.patchById, {
userId: job.user_id,
id: assetId,
patch: {
ocr_status: "failed",
ocr_payload: { error: extracted.reason, meta: extracted.meta ?? null },
ocr_strategy: extracted.strategy,
},
});
await ctx.runMutation(internal.jobs.finishFailure, { id: args.id, error: extracted.reason });
return;
}
await ctx.runMutation(api.mediaAssets.patchById, {
userId: job.user_id,
id: assetId,
patch: {
ocr_text: extracted.text,
ocr_status: "completed",
ocr_payload: extracted.meta ?? null,
ocr_strategy: extracted.strategy,
},
});
await ctx.runMutation(internal.jobs.finishSuccess, {
id: args.id,
result: {
ok: true,
kind: "extract_media_asset_text",
assetId,
strategy: extracted.strategy,
chars: extracted.text.length,
},
});
return;
}
throw new Error(`未知任务类型:${job.type}`);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);