From 95c5d771338f4ae64cb4a2b1a3ec4bcd9b98a357 Mon Sep 17 00:00:00 2001 From: eezd Date: Sat, 31 Oct 2026 22:11:40 +0800 Subject: [PATCH] Guard news Skill refresh results after session changes Discard the Skill list response if the session changes after draft creation, and cover the race with a regression test. Frontend lint, type checking, all 50 tests and production build pass. --- web/src/pages/home/index.vue | 4 +++- web/tests/workspaces.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/web/src/pages/home/index.vue b/web/src/pages/home/index.vue index 8f34513..68e8479 100644 --- a/web/src/pages/home/index.vue +++ b/web/src/pages/home/index.vue @@ -144,7 +144,9 @@ async function createTemplates() { const value = await api.createNewsSkillTemplatesApi() if (version !== user.sessionVersion || !settings.value) return Object.assign(settings.value, value) - newsSkills.value = (await listSkillTreeApi()).filter(s => s.scope === "news" && s.kind === "skill") + const skills = await listSkillTreeApi() + if (version !== user.sessionVersion) return + newsSkills.value = skills.filter(s => s.scope === "news" && s.kind === "skill") ElMessage.success("已创建草稿,请前往 Skills 页面编辑并发布") } async function save(enable: boolean) { diff --git a/web/tests/workspaces.test.ts b/web/tests/workspaces.test.ts index 6b0a106..70e19b2 100644 --- a/web/tests/workspaces.test.ts +++ b/web/tests/workspaces.test.ts @@ -711,3 +711,31 @@ test("72小时阅读列表分页保留全部包,更新缩页后回到有效页 assert.equal(page.readingPage.value, 1) assert.equal(page.readingRows.value[0].id, 0) }) + +test("创建新闻 Skills 后刷新列表,旧会话响应不能覆盖新会话", async () => { + const user = vue.reactive({ sessionVersion: 1 }) + const response = Promise.withResolvers() + let notifications = 0 + const load = moduleLoader({ + "@/common/apis/risk": { createNewsSkillTemplatesApi: async () => ({ grouping_skill_id: 1 }) }, + "@@/apis/skills": { listSkillTreeApi: () => response.promise }, + "@/common/apis/settings": {}, + "@/pinia/stores/user": { useUserStore: () => user } + }, { + ...globals, + ElMessage: { success() { + notifications++ + } } + }) + const page = load("@/pages/home/index.vue").default.setup({}, { expose() {} }) + page.settings.value = { grouping_skill_id: null } + const pending = page.createTemplates() + await setImmediate() + user.sessionVersion++ + await vue.nextTick() + page.newsSkills.value = [{ id: 2, scope: "news", kind: "skill", name: "Current" }] + response.resolve([{ id: 1, scope: "news", kind: "skill", name: "Old" }]) + await pending + assert.equal(page.newsSkills.value[0].id, 2) + assert.equal(notifications, 0) +})