feat(article): support regenerating failed articles

This commit is contained in:
2026-05-07 10:55:44 +08:00
parent ff2bb77cdd
commit 7d82c5c193
21 changed files with 659 additions and 23 deletions
+14 -5
View File
@@ -13,16 +13,16 @@ import type {
BrandRequest,
Competitor,
CompetitorRequest,
CreateArticleRequest,
CreateKolPackageRequest,
CreateKolPromptRequest,
CreateAckRequest,
ComplianceAckRecord,
ComplianceCheckArticleRequest,
ComplianceCheckPlainRequest,
ComplianceCheckResult,
ComplianceManualReview,
ComplianceRuntimeStatus,
CreateAckRequest,
CreateArticleRequest,
CreateKolPackageRequest,
CreateKolPromptRequest,
CreatePluginSessionRequest,
CreatePluginSessionResponse,
CreatePublishBatchRequest,
@@ -92,6 +92,7 @@ import type {
QuotaSummary,
RecentArticle,
RefreshResponse,
RegenerateArticleResponse,
RegisterPluginInstallationRequest,
RegisterPluginInstallationResponse,
ScheduleTask,
@@ -626,6 +627,12 @@ export const articlesApi = {
payload,
)
},
regenerate(id: number) {
return apiClient.post<RegenerateArticleResponse, null>(
`/api/tenant/articles/${id}/regenerate`,
null,
)
},
detail(id: number) {
return apiClient.get<ArticleDetail>(`/api/tenant/articles/${id}`)
},
@@ -1115,7 +1122,9 @@ export const complianceApi = {
)
},
latestArticleCheck(articleId: number) {
return apiClient.get<ComplianceCheckResult>(`/api/tenant/articles/${articleId}/compliance/latest`)
return apiClient.get<ComplianceCheckResult>(
`/api/tenant/articles/${articleId}/compliance/latest`,
)
},
createAck(recordId: number, payload: CreateAckRequest) {
return apiClient.post<ComplianceAckRecord, CreateAckRequest>(
+22 -1
View File
@@ -6,9 +6,26 @@ export interface ArticleActionState {
showPreview: boolean
showCopy: boolean
showDelete: boolean
showRegenerate: boolean
}
export function resolveArticleActionState(generateStatus?: string | null): ArticleActionState {
export interface ArticleActionSource {
generate_status?: string | null
source_type?: string | null
}
const regeneratableSourceTypes = new Set(['template', 'custom_generation', 'imitation', 'kol'])
export function resolveArticleActionState(
articleOrGenerateStatus?: ArticleActionSource | string | null,
): ArticleActionState {
const generateStatus =
typeof articleOrGenerateStatus === 'string'
? articleOrGenerateStatus
: articleOrGenerateStatus?.generate_status
const sourceType =
typeof articleOrGenerateStatus === 'string' ? undefined : articleOrGenerateStatus?.source_type
if (generateStatus === 'completed') {
return {
showPublish: true,
@@ -16,6 +33,7 @@ export function resolveArticleActionState(generateStatus?: string | null): Artic
showPreview: true,
showCopy: true,
showDelete: true,
showRegenerate: false,
}
}
@@ -26,6 +44,7 @@ export function resolveArticleActionState(generateStatus?: string | null): Artic
showPreview: false,
showCopy: false,
showDelete: true,
showRegenerate: false,
}
}
@@ -36,6 +55,7 @@ export function resolveArticleActionState(generateStatus?: string | null): Artic
showPreview: false,
showCopy: false,
showDelete: true,
showRegenerate: sourceType ? regeneratableSourceTypes.has(sourceType) : false,
}
}
@@ -45,6 +65,7 @@ export function resolveArticleActionState(generateStatus?: string | null): Artic
showPreview: true,
showCopy: false,
showDelete: true,
showRegenerate: false,
}
}
@@ -0,0 +1,43 @@
import type { ArticleListItem, RecentArticle } from '@geo/shared-types'
import { useMutation, useQueryClient } from '@tanstack/vue-query'
import { message } from 'ant-design-vue'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { articlesApi } from '@/lib/api'
import { formatError } from '@/lib/errors'
type RegeneratableArticle = Pick<ArticleListItem | RecentArticle, 'id'>
export function useArticleRegenerateAction() {
const queryClient = useQueryClient()
const { t } = useI18n()
const regeneratingArticleId = ref<number | null>(null)
const regenerateMutation = useMutation({
mutationFn: (article: RegeneratableArticle) => articlesApi.regenerate(article.id),
onSuccess: async () => {
message.success(t('common.regenerateSuccess'))
regeneratingArticleId.value = null
await Promise.all([
queryClient.invalidateQueries({ queryKey: ['articles'] }),
queryClient.invalidateQueries({ queryKey: ['workspace'] }),
])
},
onError: (error) => {
regeneratingArticleId.value = null
message.error(formatError(error) || t('common.regenerateError'))
},
})
async function handleRegenerate(article: RegeneratableArticle): Promise<void> {
regeneratingArticleId.value = article.id
await regenerateMutation.mutateAsync(article)
}
return {
regeneratingArticleId,
regenerateMutation,
handleRegenerate,
}
}