From 2c9db4430c12d7d41ff28f2fb579b7fcfb714c36 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sun, 6 Sep 2026 12:38:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(#1714):=20complete=20=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E6=90=BA=E5=B8=A6=20file=5Fsize=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=90=8C=E5=90=8D=E5=85=9C=E5=BA=95=E8=AF=AF=E6=9D=80=E6=96=B0?= =?UTF-8?q?=E8=A7=86=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit completeDirectUpload 请求体新增 file_size,prepareDirectUploadHandle 的 complete 闭包透传 data.file.size(队列/非队列两条链路同时生效)。 后端 complete_direct_upload 的 _find_duplicate_asset 同名兜底分支 需要用 file_size 做大小严格校验:此前前端不传(后端收到 0), 导致 30 分钟内同名视频(如 iPhone IMG_2285.MOV)即使内容/大小全新 也被同名兜底误判重复跳过。 单测:completeDirectUpload 透传 file_size;uploadAssetDirect 老流程 断言 complete body 携带 file_size。 --- apps/web/src/api/assets/upload.ts | 4 ++++ apps/web/src/test/api/assets.test.ts | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/apps/web/src/api/assets/upload.ts b/apps/web/src/api/assets/upload.ts index 3a452945d..490b08df7 100644 --- a/apps/web/src/api/assets/upload.ts +++ b/apps/web/src/api/assets/upload.ts @@ -32,6 +32,8 @@ export const completeDirectUpload = async (data: { file_hash?: string /** 前端上传幂等 token(与 prepare 一致),同一次上传重发 complete 不重复建记录 */ client_upload_id?: string + /** 文件字节数;后端同名兜底去重需用它做大小校验,缺失(=0)时同名记录一律不判重 */ + file_size?: number }): Promise => { // complete 内含 OSS 存在性检查 + 建库 + 派单,放宽到 60s; // 超时不代表失败(记录可能已建成),调用方禁止超时后盲目重传整个文件 @@ -156,6 +158,8 @@ export const prepareDirectUploadHandle = async (data: { storage_key: prepared.storage_key, file_hash: data.fileHash, client_upload_id: data.clientUploadId, + // 透传文件字节数:后端同名兜底去重依赖大小校验,缺省会导致同名新视频被误判重复 + file_size: data.file.size, }), } } diff --git a/apps/web/src/test/api/assets.test.ts b/apps/web/src/test/api/assets.test.ts index 90967f1f5..cce914b2e 100644 --- a/apps/web/src/test/api/assets.test.ts +++ b/apps/web/src/test/api/assets.test.ts @@ -242,6 +242,20 @@ describe("assets API", () => { await expect(completeDirectUpload({ name: "test-item" })).resolves.not.toThrow() }) + it("请求体携带 file_size(后端同名兜底去重的大小校验依赖它)", async () => { + await completeDirectUpload({ + project_id: "p-1", + library_id: "l-1", + storage_key: "uploads/k.mp4", + file_size: 12345, + } as never) + const completeCalls = mockPost.mock.calls.filter( + ([u]: [string]) => u === "/upload/direct/complete", + ) + expect(completeCalls).toHaveLength(1) + expect(completeCalls[0][1]).toMatchObject({ file_size: 12345 }) + }) + it("should reject on API error", async () => { mockGet.mockRejectedValue(new Error("Network error")) mockPost.mockRejectedValue(new Error("Network error")) @@ -354,6 +368,8 @@ describe("assets API", () => { ([u]: [string]) => u === "/upload/direct/complete", ) expect(completeCalls).toHaveLength(1) + // complete 请求必须带上 file_size,否则后端同名兜底会误杀同名新视频 + expect(completeCalls[0][1]).toMatchObject({ file_size: file.size }) XMLHttpRequest.prototype.open = origOpen XMLHttpRequest.prototype.send = origSend if (origSetReadyState) { -- 2.54.0