fix(admin-web): invalidate workspace cache after every AI-points-consuming call

Top-nav quota chip (会员到期 / 生成文章 / AI 点数) only refreshes when its
query is invalidated. Several mutations and stream-based flows that debit
points or article quota on the server were not invalidating it, so users
had to refresh the page to see updated balances.

- TemplateWizardView: invalidate `['workspace']` inside `endAssistTask()`,
  covering analyze / title / outline assist tasks in one place.
- FreeCreateView: invalidate after `articlesApi.create` success.
- GenerateTaskDrawer: add `['workspace']` to the instant-generate
  `Promise.all` alongside the existing `articles` / `instantTasks` keys.
- KolPromptEditor: invalidate in the `finally` of both `runAiAssistStream`
  (generate) and `generateSelectionAiPreview` (optimize) so abort / error
  paths also refresh.
- ArticleEditorCanvas: bring in `useQueryClient` and invalidate in the
  selection-optimize stream `finally`.

Together with the existing invalidations in TemplateWizard.generate,
ImitationGenerate, KolGenerate, and article-regenerate, every quota-
debiting call site now feeds the cache refresh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 01:54:12 +08:00
parent 793c310560
commit 51d32fa96d
5 changed files with 8 additions and 0 deletions
@@ -57,6 +57,7 @@ import {
} from '@milkdown/kit/prose/state'
import { Decoration, DecorationSet, type EditorView } from '@milkdown/kit/prose/view'
import { callCommand, replaceAll } from '@milkdown/kit/utils'
import { useQueryClient } from '@tanstack/vue-query'
import { message } from 'ant-design-vue'
// NOTE: import each common stylesheet explicitly instead of the aggregate
// `theme/common/style.css`. The aggregate transitively `@import`s `latex.css`,
@@ -121,6 +122,7 @@ const emit = defineEmits<{
}>()
const { t } = useI18n()
const queryClient = useQueryClient()
const crepeRef = shallowRef<Crepe | null>(null)
const syncingExternalMarkdown = ref(false)
const editorRootRef = ref<HTMLElement | null>(null)
@@ -1481,6 +1483,7 @@ async function generateAiOptimizePreview(): Promise<void> {
if (aiOptimizeAbortController === controller) {
aiOptimizeAbortController = null
}
void queryClient.invalidateQueries({ queryKey: ['workspace'] })
}
}
@@ -173,6 +173,7 @@ const instantGenerateMutation = useMutation({
await Promise.all([
queryClient.invalidateQueries({ queryKey: ['articles'] }),
queryClient.invalidateQueries({ queryKey: ['instantTasks'] }),
queryClient.invalidateQueries({ queryKey: ['workspace'] }),
])
},
onError: (error) => message.error(formatError(error)),
@@ -275,6 +275,7 @@ async function runAiAssistStream(payload: KolAssistRequest) {
if (aiAssistAbortController === controller) {
aiAssistAbortController = null
}
void queryClient.invalidateQueries({ queryKey: ['workspace'] })
}
}
@@ -411,6 +412,7 @@ async function generateSelectionAiPreview() {
if (selectionAiAbortController === controller) {
selectionAiAbortController = null
}
void queryClient.invalidateQueries({ queryKey: ['workspace'] })
}
}
@@ -95,6 +95,7 @@ const deleteMutation = useMutation({
const createMutation = useMutation({
mutationFn: () => articlesApi.create({}),
onSuccess: (data) => {
void queryClient.invalidateQueries({ queryKey: ['workspace'] })
void router.push({ name: 'article-editor', params: { id: String(data.id) } })
},
onError: (error) => {
@@ -839,6 +839,7 @@ function beginAssistTask(kind: 'analyze' | 'title' | 'outline'): void {
}
function endAssistTask(): void {
void queryClient.invalidateQueries({ queryKey: ['workspace'] })
window.setTimeout(() => {
assistModalOpen.value = false
assistBusy.value = false