From ee916acb1b8b83d3ed47f22b5458a73fd72cdfb5 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Thu, 23 Jul 2026 18:29:31 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix(web):=20=E4=BF=AE=E5=A4=8D=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E5=9B=9E=E8=B0=83=E9=A1=B5token=E6=97=B6=E5=BA=8Fbug?= =?UTF-8?q?=20-=20=E5=85=88=E5=AD=98token=E5=86=8D=E5=8F=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WechatCallback页面存在与#734相同的时序问题: getCurrentUser()在setAuth()之前执行,导致请求拦截器 从localStorage取不到token,返回401后触发登出逻辑跳回登录页。 修复方式与#734一致:先手动存token到localStorage, 再调用getCurrentUser(),最后setAuth同步store状态。 关联: #558, #734 --- apps/web/src/pages/auth/WechatCallback.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/web/src/pages/auth/WechatCallback.tsx b/apps/web/src/pages/auth/WechatCallback.tsx index 783198149..3dc75a6e9 100644 --- a/apps/web/src/pages/auth/WechatCallback.tsx +++ b/apps/web/src/pages/auth/WechatCallback.tsx @@ -39,7 +39,13 @@ const WechatCallback: React.FC = () => { const result = await wechatCallback(code, state) - // 获取用户信息 + // 先把 token 存到 localStorage,让请求拦截器能拿到(getCurrentUser 需要带 token) + localStorage.setItem("access_token", result.access_token) + if (result.refresh_token) { + localStorage.setItem("refresh_token", result.refresh_token) + } + + // 获取用户信息(这时候请求拦截器能拿到 token 了) const userData = await getCurrentUser() const user: User = normalizeUser(userData) setAuth(user, result.access_token, result.refresh_token) -- 2.54.0 From 2f237c80cf19d4440b720e133c9573b47eab228f Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Thu, 23 Jul 2026 18:56:24 +0800 Subject: [PATCH 2/4] =?UTF-8?q?test(web):=20=E6=B7=BB=E5=8A=A0WechatCallba?= =?UTF-8?q?ck=E9=A1=B5=E9=9D=A2=E5=9F=BA=E7=A1=80=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/pages/auth/WechatCallback.test.tsx | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 apps/web/src/test/pages/auth/WechatCallback.test.tsx diff --git a/apps/web/src/test/pages/auth/WechatCallback.test.tsx b/apps/web/src/test/pages/auth/WechatCallback.test.tsx new file mode 100644 index 000000000..abedadec1 --- /dev/null +++ b/apps/web/src/test/pages/auth/WechatCallback.test.tsx @@ -0,0 +1,72 @@ +import { describe, expect, it, vi, beforeEach } from "vitest" +import { render, screen } from "@testing-library/react" +import { MemoryRouter } from "react-router-dom" +import WechatCallback from "@/pages/auth/WechatCallback" + +// 重置 localStorage +beforeEach(() => { + vi.spyOn(Storage.prototype, "getItem").mockReturnValue(null) + vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => {}) + vi.spyOn(Storage.prototype, "removeItem").mockImplementation(() => {}) +}) + +vi.mock("react-router-dom", async () => { + const actual = await vi.importActual("react-router-dom") + return { + ...actual, + useNavigate: () => vi.fn(), + useSearchParams: () => [new URLSearchParams()], + } +}) + +vi.mock("@/api/auth", () => ({ + wechatCallback: vi.fn(), + getCurrentUser: vi.fn(), + normalizeUser: (u: unknown) => u, +})) + +vi.mock("@/store/authStore", () => ({ + useAuthStore: () => ({ + setAuth: vi.fn(), + }), +})) + +vi.mock("@/components/auth/BindContactModal", () => ({ + default: ({ open }: { open: boolean }) => ( +
+ BindContactModal +
+ ), +})) + +vi.mock("antd", async () => { + const actual = await vi.importActual("antd") + return { + ...actual, + message: { + success: vi.fn(), + error: vi.fn(), + }, + } +}) + +describe("WechatCallback Page", () => { + it("should render without crashing", () => { + const { container } = render( + + + , + ) + expect(container).toBeTruthy() + }) + + it("should show loading state initially", () => { + render( + + + , + ) + // 初始状态应该显示加载中 + expect(screen.getByText("正在登录...")).toBeTruthy() + }) +}) -- 2.54.0 From 375692e838b3f02e73247fa8cf48ce6784c62ed1 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Thu, 23 Jul 2026 19:14:44 +0800 Subject: [PATCH 3/4] =?UTF-8?q?test(web):=20=E4=BF=AE=E5=A4=8DWechatCallba?= =?UTF-8?q?ck=E6=B5=8B=E8=AF=95=20-=20mock=E6=AD=A3=E7=A1=AE=E7=9A=84code/?= =?UTF-8?q?state=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/test/pages/auth/WechatCallback.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/test/pages/auth/WechatCallback.test.tsx b/apps/web/src/test/pages/auth/WechatCallback.test.tsx index abedadec1..a924b372d 100644 --- a/apps/web/src/test/pages/auth/WechatCallback.test.tsx +++ b/apps/web/src/test/pages/auth/WechatCallback.test.tsx @@ -15,12 +15,12 @@ vi.mock("react-router-dom", async () => { return { ...actual, useNavigate: () => vi.fn(), - useSearchParams: () => [new URLSearchParams()], + useSearchParams: () => [new URLSearchParams({ code: "test_code", state: "test_state" })], } }) vi.mock("@/api/auth", () => ({ - wechatCallback: vi.fn(), + wechatCallback: vi.fn(() => new Promise(() => {})), // pending promise,保持loading getCurrentUser: vi.fn(), normalizeUser: (u: unknown) => u, })) @@ -60,13 +60,13 @@ describe("WechatCallback Page", () => { expect(container).toBeTruthy() }) - it("should show loading state initially", () => { + it("should show loading state while processing", () => { render( , ) - // 初始状态应该显示加载中 + // wechatCallback 返回 pending promise,所以应该显示 loading expect(screen.getByText("正在登录...")).toBeTruthy() }) }) -- 2.54.0 From 5bf572b2257febbdc25915a27e75368a68cae02e Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Thu, 23 Jul 2026 19:21:52 +0800 Subject: [PATCH 4/4] =?UTF-8?q?test(web):=20=E4=BF=AE=E5=A4=8DWechatCallba?= =?UTF-8?q?ck=E6=B5=8B=E8=AF=95=20-=20mock=20localStorage=E7=9A=84wechat?= =?UTF-8?q?=5Fstate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/pages/auth/WechatCallback.test.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/web/src/test/pages/auth/WechatCallback.test.tsx b/apps/web/src/test/pages/auth/WechatCallback.test.tsx index a924b372d..fe558f930 100644 --- a/apps/web/src/test/pages/auth/WechatCallback.test.tsx +++ b/apps/web/src/test/pages/auth/WechatCallback.test.tsx @@ -3,13 +3,6 @@ import { render, screen } from "@testing-library/react" import { MemoryRouter } from "react-router-dom" import WechatCallback from "@/pages/auth/WechatCallback" -// 重置 localStorage -beforeEach(() => { - vi.spyOn(Storage.prototype, "getItem").mockReturnValue(null) - vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => {}) - vi.spyOn(Storage.prototype, "removeItem").mockImplementation(() => {}) -}) - vi.mock("react-router-dom", async () => { const actual = await vi.importActual("react-router-dom") return { @@ -51,6 +44,20 @@ vi.mock("antd", async () => { }) describe("WechatCallback Page", () => { + beforeEach(() => { + // mock localStorage,设置wechat_state匹配,让校验通过 + const store: Record = { + wechat_state: "test_state", + } + vi.spyOn(Storage.prototype, "getItem").mockImplementation((key) => store[key] || null) + vi.spyOn(Storage.prototype, "setItem").mockImplementation((key, val) => { + store[key] = val + }) + vi.spyOn(Storage.prototype, "removeItem").mockImplementation((key) => { + delete store[key] + }) + }) + it("should render without crashing", () => { const { container } = render( -- 2.54.0