From b839e0770cf4f4fe4bc9916ccbe7e0389549b671 Mon Sep 17 00:00:00 2001 From: momen Date: Tue, 19 May 2026 01:02:53 +0800 Subject: [PATCH] fix(account): bind pending downloads to user --- src/App.vue | 32 +++++++++-- .../unit/App.download-records.test.ts | 57 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/App.vue b/src/App.vue index b070ea5a..60867b38 100644 --- a/src/App.vue +++ b/src/App.vue @@ -409,7 +409,12 @@ const favoriteLoading = ref(false); const favoriteError = ref(""); const favoriteRequestGeneration = ref(0); const systemInfo = ref({ distro: "unknown" }); -type PendingDownloadRecord = Omit; +type PendingDownloadRecord = Omit< + DownloadedAppRecord, + "id" | "downloadedAt" +> & { + userId: number; +}; const pendingDownloadRecords = new Map(); /** 启动参数 --no-apm => 仅 Spark;--no-spark => 仅 APM;由主进程 IPC 提供 */ @@ -1245,9 +1250,11 @@ const onDetailRemove = (app: App) => { const onDetailInstall = async (app: App) => { const download = await handleInstall(app); - if (!download || !isLoggedIn.value) return; + const initiatingUserId = currentUser.value?.id; + if (!download || !isLoggedIn.value || initiatingUserId === undefined) return; pendingDownloadRecords.set(download.id, { + userId: initiatingUserId, appKey: buildFavoriteAppKey(app), pkgname: app.pkgname, name: app.name, @@ -1265,10 +1272,26 @@ const handleInstallCompleteForDownloadRecord = async ( const pendingRecord = pendingDownloadRecords.get(result.id); if (!pendingRecord) return; - if (!result.success || !isLoggedIn.value) return; + if ( + !result.success || + !isLoggedIn.value || + currentUser.value?.id !== pendingRecord.userId + ) { + return; + } + + const downloadRecord: Omit = { + appKey: pendingRecord.appKey, + pkgname: pendingRecord.pkgname, + name: pendingRecord.name, + category: pendingRecord.category, + selectedOrigin: pendingRecord.selectedOrigin, + version: pendingRecord.version, + packageArch: pendingRecord.packageArch, + }; try { - await recordDownloadedApp(pendingRecord); + await recordDownloadedApp(downloadRecord); } catch (error: unknown) { logger.warn({ err: error }, "记录下载应用失败"); } finally { @@ -1372,6 +1395,7 @@ const isCurrentFavoriteRequest = (generation: number): boolean => const handleLogout = () => { logout(); + pendingDownloadRecords.clear(); clearFavoriteState(); showLoginModal.value = false; showLoginPrompt.value = false; diff --git a/src/__tests__/unit/App.download-records.test.ts b/src/__tests__/unit/App.download-records.test.ts index f101275c..726aa1f3 100644 --- a/src/__tests__/unit/App.download-records.test.ts +++ b/src/__tests__/unit/App.download-records.test.ts @@ -269,4 +269,61 @@ describe("App download records", () => { ); }); }); + + it("does not record a queued install under a later logged-in user", async () => { + render(App); + + await fireEvent.click( + await screen.findByRole("button", { name: "全部应用 1" }), + ); + await fireEvent.click(await screen.findByText("WPS")); + await fireEvent.click(await screen.findByRole("button", { name: "安装" })); + + await waitFor(() => { + expect(send).toHaveBeenCalledWith( + "queue-install", + expect.stringContaining('"pkgname":"wps"'), + ); + }); + + const queuedPayload = vi + .mocked(send) + .mock.calls.find( + ([channel]) => channel === "queue-install", + )?.[1] as string; + const queuedDownload = JSON.parse(queuedPayload) as { id: number }; + + await fireEvent.click(screen.getByRole("button", { name: "Momen" })); + await fireEvent.click(await screen.findByText("退出登录")); + + setAuthSession({ + accessToken: "backend-token-b", + tokenType: "bearer", + user: { + id: 2, + flarumUserId: "84", + username: "second", + displayName: "Second User", + avatarUrl: "https://bbs.spark-app.store/avatar-b.png", + forumLevel: "用户", + forumGroups: ["用户"], + }, + }); + + const completion: DownloadResult = { + id: queuedDownload.id, + time: Date.now(), + message: "installed", + success: true, + exitCode: 0, + status: "completed", + origin: "apm", + }; + + ipcHandlers.get("install-complete")?.({}, completion); + + await waitFor(() => { + expect(recordDownloadedApp).not.toHaveBeenCalled(); + }); + }); });