From fa52d4ca60013681add2e4e5e33e58bb63509891 Mon Sep 17 00:00:00 2001 From: liangxu Date: Tue, 26 May 2026 18:53:36 +0800 Subject: [PATCH] feat(publish): unify image fetch failure message across adapters Consolidate per-platform cover/image fetch failure prompts into a single PUBLISH_IMAGE_FETCH_FAILED_MESSAGE constant shared by baijiahao, bilibili, dongchedi, qiehao, smzdm, and zol adapters, and generalize the normalizer to match any *_image_fetch_failed / *_cover_fetch_failed code. Also split desktop publish list loading into initial vs manual-refresh states so the refresh button no longer blanks the table, and fix admin-web active-task detection to cover all pending statuses. --- .../src/views/PublishManagementView.vue | 2 +- .../src/main/adapters/baijiahao.ts | 3 +- .../src/main/adapters/bilibili.ts | 3 +- .../src/main/adapters/dongchedi.ts | 9 +++-- .../src/main/adapters/qiehao.ts | 5 ++- .../desktop-client/src/main/adapters/smzdm.ts | 5 ++- apps/desktop-client/src/main/adapters/zol.ts | 3 +- .../renderer/views/PublishManagementView.vue | 39 ++++++++++++++----- .../src/shared/publisher-errors.test.ts | 18 ++++++--- .../src/shared/publisher-errors.ts | 14 +++---- 10 files changed, 67 insertions(+), 34 deletions(-) diff --git a/apps/admin-web/src/views/PublishManagementView.vue b/apps/admin-web/src/views/PublishManagementView.vue index 3304633..a13680e 100644 --- a/apps/admin-web/src/views/PublishManagementView.vue +++ b/apps/admin-web/src/views/PublishManagementView.vue @@ -99,7 +99,7 @@ function isPendingStatus(status: PublishTaskStatus): boolean { } function hasActivePublishingTask(page: TenantPublishTaskListResponse | undefined): boolean { - return Boolean(page?.items.some((task) => normalizePublishTaskStatus(task.status) === 'in_progress')) + return Boolean(page?.items.some((task) => isPendingStatus(normalizePublishTaskStatus(task.status)))) } function normalizeTaskErrorMessage(messageText: string | null, platform?: string | null): string | null { diff --git a/apps/desktop-client/src/main/adapters/baijiahao.ts b/apps/desktop-client/src/main/adapters/baijiahao.ts index b99c182..33ec1f2 100644 --- a/apps/desktop-client/src/main/adapters/baijiahao.ts +++ b/apps/desktop-client/src/main/adapters/baijiahao.ts @@ -7,6 +7,7 @@ import { prepareBaijiahaoMarkdown, } from '../../../../../packages/publisher-platforms/src/baijiahao' +import { PUBLISH_IMAGE_FETCH_FAILED_MESSAGE } from '../../shared/publisher-errors' import { resolveDesktopApiURL } from '../transport/api-client' import type { PublishAdapter, PublishAdapterContext } from './base' import { @@ -436,7 +437,7 @@ function failureResult( ) { return { status: 'failed', - summary: '百家号封面文件读取失败,请重新上传封面图后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'baijiahao_cover_fetch_failed', message, diff --git a/apps/desktop-client/src/main/adapters/bilibili.ts b/apps/desktop-client/src/main/adapters/bilibili.ts index 9630f2c..b2e8936 100644 --- a/apps/desktop-client/src/main/adapters/bilibili.ts +++ b/apps/desktop-client/src/main/adapters/bilibili.ts @@ -3,6 +3,7 @@ import { createHash } from 'node:crypto' import type { JsonValue } from '@geo/shared-types' import { renderTablesAsParagraphRows } from '../../../../../packages/publisher-platforms/src/baijiahao' +import { PUBLISH_IMAGE_FETCH_FAILED_MESSAGE } from '../../shared/publisher-errors' import { resolveDesktopApiURL } from '../transport/api-client' import type { PublishAdapter, PublishAdapterContext } from './base' import { @@ -639,7 +640,7 @@ function buildFailureResult( ) { return { status: 'failed', - summary: 'bilibili 正文图片读取失败,请检查文章图片后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'bilibili_image_fetch_failed', message, diff --git a/apps/desktop-client/src/main/adapters/dongchedi.ts b/apps/desktop-client/src/main/adapters/dongchedi.ts index a86a8ac..0947d97 100644 --- a/apps/desktop-client/src/main/adapters/dongchedi.ts +++ b/apps/desktop-client/src/main/adapters/dongchedi.ts @@ -2,7 +2,10 @@ import { createHash, createHmac } from 'node:crypto' import type { JsonValue } from '@geo/shared-types' -import { DONGCHEDI_PLATFORM_EXCEPTION_MESSAGE } from '../../shared/publisher-errors' +import { + DONGCHEDI_PLATFORM_EXCEPTION_MESSAGE, + PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, +} from '../../shared/publisher-errors' import type { PublishAdapter, PublishAdapterContext } from './base' import { ensureViewLoaded, @@ -944,7 +947,7 @@ function failureResult( ) { return { status: 'failed', - summary: '懂车帝封面文件读取失败,请重新上传封面图后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'dongchedi_cover_fetch_failed', message, @@ -958,7 +961,7 @@ function failureResult( ) { return { status: 'failed', - summary: '懂车帝正文图片读取失败,请检查文章图片后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'dongchedi_image_fetch_failed', message, diff --git a/apps/desktop-client/src/main/adapters/qiehao.ts b/apps/desktop-client/src/main/adapters/qiehao.ts index 8c27318..3234738 100644 --- a/apps/desktop-client/src/main/adapters/qiehao.ts +++ b/apps/desktop-client/src/main/adapters/qiehao.ts @@ -2,6 +2,7 @@ import type { JsonValue } from '@geo/shared-types' import { prepareQiehaoArticleHtml } from '../../../../../packages/publisher-platforms/src/qiehao' import { + PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, QIEHAO_REAL_NAME_AUTH_MESSAGE, isQiehaoRealNameAuthError, } from '../../shared/publisher-errors' @@ -425,7 +426,7 @@ function failureResult( if (message === 'qiehao_cover_fetch_failed' || message.startsWith('qiehao_cover_fetch_failed:')) { return { status: 'failed', - summary: '企鹅号封面文件读取失败,请重新上传封面图后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'qiehao_cover_fetch_failed', message, @@ -436,7 +437,7 @@ function failureResult( if (message === 'qiehao_image_fetch_failed' || message.startsWith('qiehao_image_fetch_failed:')) { return { status: 'failed', - summary: '企鹅号正文图片读取失败,请检查文章图片后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'qiehao_image_fetch_failed', message, diff --git a/apps/desktop-client/src/main/adapters/smzdm.ts b/apps/desktop-client/src/main/adapters/smzdm.ts index 4d69b87..2d0780a 100644 --- a/apps/desktop-client/src/main/adapters/smzdm.ts +++ b/apps/desktop-client/src/main/adapters/smzdm.ts @@ -4,6 +4,7 @@ import { createCipheriv, createHash } from 'node:crypto' import type { JsonValue } from '@geo/shared-types' import { nativeImage } from 'electron' +import { PUBLISH_IMAGE_FETCH_FAILED_MESSAGE } from '../../shared/publisher-errors' import { resolveDesktopApiURL } from '../transport/api-client' import type { PublishAdapter, PublishAdapterContext } from './base' import { @@ -776,7 +777,7 @@ function failureResult( if (message.startsWith('smzdm_cover_fetch_failed')) { return { status: 'failed', - summary: '什么值得买封面文件读取失败,请重新上传封面图后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'smzdm_cover_fetch_failed', message, @@ -787,7 +788,7 @@ function failureResult( if (message.startsWith('smzdm_image_fetch_failed')) { return { status: 'failed', - summary: '图片获取错误,请检查你文章和封面图片是否正确', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'smzdm_image_fetch_failed', message, diff --git a/apps/desktop-client/src/main/adapters/zol.ts b/apps/desktop-client/src/main/adapters/zol.ts index 3ede248..180bbfa 100644 --- a/apps/desktop-client/src/main/adapters/zol.ts +++ b/apps/desktop-client/src/main/adapters/zol.ts @@ -1,5 +1,6 @@ import type { JsonValue } from '@geo/shared-types' +import { PUBLISH_IMAGE_FETCH_FAILED_MESSAGE } from '../../shared/publisher-errors' import type { PublishAdapter, PublishAdapterContext } from './base' import { extractImageSources, @@ -488,7 +489,7 @@ function failureResult( if (message.startsWith('zol_cover_fetch_failed')) { return { status: 'failed', - summary: '中关村在线封面文件读取失败,请重新上传封面图后重试。', + summary: PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, error: { code: 'zol_cover_fetch_failed', message, diff --git a/apps/desktop-client/src/renderer/views/PublishManagementView.vue b/apps/desktop-client/src/renderer/views/PublishManagementView.vue index 8007341..9ce5de2 100644 --- a/apps/desktop-client/src/renderer/views/PublishManagementView.vue +++ b/apps/desktop-client/src/renderer/views/PublishManagementView.vue @@ -79,7 +79,7 @@ function platformShortName(platform: string): string { function statusLabel(status: DesktopTaskInfo['status']): string { const map: Record = { queued: '等待发布', - in_progress: '正在发送', + in_progress: '正在发布', succeeded: '发送成功', failed: '发送失败', unknown: '发送失败', @@ -324,7 +324,8 @@ const taskPage = ref(null) const currentPage = ref(1) const searchTitle = ref('') const appliedTitle = ref('') -const loading = ref(false) +const initialLoading = ref(false) +const refreshing = ref(false) const error = ref(null) const consolePendingTaskId = ref(null) const copiedErrorTaskId = ref(null) @@ -347,8 +348,17 @@ function notifyActionError(description: string) { }) } -async function refreshTasks(page = currentPage.value) { - loading.value = true +async function refreshTasks( + page = currentPage.value, + options: { initial?: boolean; manual?: boolean } = {}, +) { + const showInitialLoading = Boolean(options.initial && taskPage.value === null) + if (showInitialLoading) { + initialLoading.value = true + } + if (options.manual) { + refreshing.value = true + } error.value = null try { @@ -361,7 +371,7 @@ async function refreshTasks(page = currentPage.value) { const lastPage = response.total > 0 ? Math.ceil(response.total / PAGE_SIZE) : 1 if (response.total > 0 && page > lastPage) { currentPage.value = lastPage - await refreshTasks(lastPage) + await refreshTasks(lastPage, options) return } @@ -374,10 +384,19 @@ async function refreshTasks(page = currentPage.value) { stopActiveTaskPolling() } } finally { - loading.value = false + if (showInitialLoading) { + initialLoading.value = false + } + if (options.manual) { + refreshing.value = false + } } } +function refreshTasksManually() { + void refreshTasks(currentPage.value, { manual: true }) +} + function startActiveTaskPolling() { if (refreshTimer) { return @@ -554,7 +573,7 @@ watch(searchTitle, (value) => { onMounted(() => { bindPublishLeaseListener() - void refreshTasks() + void refreshTasks(currentPage.value, { initial: true }) }) onUnmounted(() => { @@ -661,8 +680,8 @@ const tableScroll = { x: 1080 } as const type="primary" ghost class="modern-btn" - :loading="loading" - @click="refreshTasks()" + :loading="refreshing" + @click="refreshTasksManually" > 刷新状态 @@ -722,7 +741,7 @@ const tableScroll = { x: 1080 } as const :columns="tableColumns" :data-source="publishTasks" :pagination="false" - :loading="loading" + :loading="initialLoading" :scroll="tableScroll" :row-class-name="rowClassName" > diff --git a/apps/desktop-client/src/shared/publisher-errors.test.ts b/apps/desktop-client/src/shared/publisher-errors.test.ts index 7159939..b272ff1 100644 --- a/apps/desktop-client/src/shared/publisher-errors.test.ts +++ b/apps/desktop-client/src/shared/publisher-errors.test.ts @@ -4,8 +4,8 @@ import { BAIJIAHAO_CHALLENGE_REQUIRED_MESSAGE, DONGCHEDI_PLATFORM_EXCEPTION_MESSAGE, PUBLISH_ACCOUNT_NOT_LOGGED_IN_MESSAGE, + PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, QIEHAO_REAL_NAME_AUTH_MESSAGE, - SMZDM_IMAGE_FETCH_FAILED_MESSAGE, WANGYIHAO_DRAFT_SAVE_PARAMETER_MESSAGE, WANGYIHAO_PUBLISH_CLICK_MESSAGE, WANGYIHAO_TOKEN_MISSING_MESSAGE, @@ -14,9 +14,9 @@ import { normalizeBaijiahaoPublishErrorMessage, normalizeDongchediPublishErrorMessage, normalizePublisherErrorMessage, + normalizePublisherImageFetchErrorMessage, normalizePublisherLoginErrorMessage, normalizeQiehaoPublishErrorMessage, - normalizeSmzdmPublishErrorMessage, normalizeWangyihaoPublishErrorMessage, normalizeWeixinGzhPublishErrorMessage, } from './publisher-errors' @@ -115,12 +115,18 @@ describe('publisher error normalization', () => { ) }) - it('normalizes SMZDM image fetch failures to the desktop Chinese prompt', () => { + it('normalizes platform image fetch failures to the desktop Chinese prompt', () => { expect(normalizePublisherErrorMessage('smzdm_image_fetch_failed', 'smzdm')).toBe( - SMZDM_IMAGE_FETCH_FAILED_MESSAGE, + PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, ) - expect(normalizeSmzdmPublishErrorMessage('Error: smzdm_image_fetch_failed')).toBe( - SMZDM_IMAGE_FETCH_FAILED_MESSAGE, + expect(normalizePublisherErrorMessage('dongchedi_cover_fetch_failed', 'dongchedi')).toBe( + PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, + ) + expect(normalizePublisherErrorMessage('qiehao_image_fetch_failed:timeout', 'qiehao')).toBe( + PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, + ) + expect(normalizePublisherImageFetchErrorMessage('Error: bilibili_image_fetch_failed')).toBe( + PUBLISH_IMAGE_FETCH_FAILED_MESSAGE, ) }) diff --git a/apps/desktop-client/src/shared/publisher-errors.ts b/apps/desktop-client/src/shared/publisher-errors.ts index 71a3682..6df1f7d 100644 --- a/apps/desktop-client/src/shared/publisher-errors.ts +++ b/apps/desktop-client/src/shared/publisher-errors.ts @@ -14,8 +14,8 @@ export const BAIJIAHAO_CHALLENGE_REQUIRED_MESSAGE = '百家号检测到网络环境异常,需要人机验证。请打开百家号后台手动完成一次发稿或验证后再重试发布。' export const DONGCHEDI_PLATFORM_EXCEPTION_MESSAGE = '懂车帝平台返回异常,请打开懂车帝后台查看并处理平台提示,确认账号状态后再重试。' -export const SMZDM_IMAGE_FETCH_FAILED_MESSAGE = - '图片获取错误,请检查你文章中图片是否正确' +export const PUBLISH_IMAGE_FETCH_FAILED_MESSAGE = + '图片获取错误,请检查你文章和封面图片是否正确' const PUBLISH_PLATFORM_LABELS: Record = { baijiahao: '百家号', @@ -266,7 +266,7 @@ export function normalizeDongchediPublishErrorMessage( return normalized } -export function normalizeSmzdmPublishErrorMessage( +export function normalizePublisherImageFetchErrorMessage( message: string | null | undefined, ): string | null { const normalized = normalizeErrorText(message) @@ -274,8 +274,8 @@ export function normalizeSmzdmPublishErrorMessage( return null } - if (/\bsmzdm_image_fetch_failed\b/i.test(normalized)) { - return SMZDM_IMAGE_FETCH_FAILED_MESSAGE + if (/\b[a-z0-9_]+_(?:image|cover)_fetch_failed\b/i.test(normalized)) { + return PUBLISH_IMAGE_FETCH_FAILED_MESSAGE } return normalized } @@ -295,7 +295,7 @@ export function normalizePublisherErrorMessage( const wangyihaoMessage = normalizeWangyihaoPublishErrorMessage(qiehaoMessage) const baijiahaoMessage = normalizeBaijiahaoPublishErrorMessage(wangyihaoMessage) const dongchediMessage = normalizeDongchediPublishErrorMessage(baijiahaoMessage, platform) - const smzdmMessage = normalizeSmzdmPublishErrorMessage(dongchediMessage) - const loginMessage = normalizePublisherLoginErrorMessage(smzdmMessage) + const imageFetchMessage = normalizePublisherImageFetchErrorMessage(dongchediMessage) + const loginMessage = normalizePublisherLoginErrorMessage(imageFetchMessage) return normalizeWeixinGzhPublishErrorMessage(loginMessage) }