diff --git a/apps/desktop-client/src/renderer/lib/client-errors.ts b/apps/desktop-client/src/renderer/lib/client-errors.ts index 2b92442..78358ff 100644 --- a/apps/desktop-client/src/renderer/lib/client-errors.ts +++ b/apps/desktop-client/src/renderer/lib/client-errors.ts @@ -1,3 +1,8 @@ +import { + normalizeDesktopAccountErrorMessage, + translatePublisherPlatform, +} from '../../shared/publisher-errors' + type ClientErrorTone = 'error' | 'warning' type ClientActionKind = 'bind-account' | 'open-console' | 'probe-account' | 'unbind-account' @@ -48,7 +53,9 @@ function presentClientError(kind: ClientActionKind, error: unknown): ClientError return { tone: 'warning', title: '授权已中断', - content: '授权窗口已关闭,本次绑定没有完成。重新点击“绑定账号”即可继续。', + content: + normalizeDesktopAccountErrorMessage(message) ?? + '授权窗口已关闭,本次绑定没有完成。重新点击“绑定账号”即可继续。', } } @@ -56,7 +63,9 @@ function presentClientError(kind: ClientActionKind, error: unknown): ClientError return { tone: 'warning', title: '授权窗口已达上限', - content: '当前最多同时打开 2 个授权窗口。请先完成或关闭已有授权窗口,再继续新的绑定。', + content: + normalizeDesktopAccountErrorMessage(message) ?? + '当前最多同时打开 2 个授权窗口。请先完成或关闭已有授权窗口,再继续新的绑定。', } } @@ -64,7 +73,9 @@ function presentClientError(kind: ClientActionKind, error: unknown): ClientError return { tone: 'error', title: '保存授权账号失败', - content: '平台侧授权已经完成,但客户端回写账号信息失败。请稍后重试。', + content: + normalizeDesktopAccountErrorMessage(message) ?? + '平台侧授权已经完成,但客户端回写账号信息失败。请稍后重试。', } } @@ -72,7 +83,7 @@ function presentClientError(kind: ClientActionKind, error: unknown): ClientError return { tone: 'error', title: '账号绑定失败', - content: '授权流程执行失败,请稍后重新发起绑定。', + content: normalizeDesktopAccountErrorMessage(message) ?? '授权流程执行失败,请稍后重新发起绑定。', } } @@ -81,7 +92,9 @@ function presentClientError(kind: ClientActionKind, error: unknown): ClientError return { tone: 'error', title: '平台暂不支持', - content: `${platformId} 暂时还不支持这个操作。`, + content: + normalizeDesktopAccountErrorMessage(message) ?? + `${translatePublisherPlatform(platformId)}暂时还不支持这个操作。`, } } @@ -89,17 +102,20 @@ function presentClientError(kind: ClientActionKind, error: unknown): ClientError return { tone: 'warning', title: kind === 'unbind-account' ? '账号状态已变化' : '账号状态已更新', - content: '当前账号状态刚刚发生了变化。请先刷新列表,再重试这次操作。', + content: + normalizeDesktopAccountErrorMessage(message) ?? + '当前账号状态刚刚发生了变化。请先刷新列表,再重试这次操作。', } } if (message.startsWith('desktop_account_session_expired:')) { const platformId = message.split(':')[1] || '当前平台' - const platformLabel = platformId === 'qiehao' ? '企鹅号' : platformId return { tone: 'warning', title: '授权已过期', - content: `${platformLabel} 登录态已失效,请重新授权后再打开工作台。`, + content: + normalizeDesktopAccountErrorMessage(message) ?? + `${translatePublisherPlatform(platformId)}登录态已失效,请重新授权后再打开工作台。`, } } @@ -107,7 +123,17 @@ function presentClientError(kind: ClientActionKind, error: unknown): ClientError return { tone: 'error', title: '账号解绑失败', - content: '服务端解绑没有成功完成,请稍后重试。', + content: + normalizeDesktopAccountErrorMessage(message) ?? '服务端解绑没有成功完成,请稍后重试。', + } + } + + const normalizedDesktopMessage = normalizeDesktopAccountErrorMessage(message) + if (normalizedDesktopMessage) { + return { + tone: message.includes('expired') || message.includes('challenge') ? 'warning' : 'error', + title: '操作失败', + content: normalizedDesktopMessage, } } diff --git a/apps/desktop-client/src/renderer/views/AccountsView.vue b/apps/desktop-client/src/renderer/views/AccountsView.vue index 5bfb3d9..c54b2b0 100644 --- a/apps/desktop-client/src/renderer/views/AccountsView.vue +++ b/apps/desktop-client/src/renderer/views/AccountsView.vue @@ -34,7 +34,7 @@ const batchProbeQueuedAccountIds = ref([]) const batchProbeTotalCount = ref(0) const actionSuccess = ref(null) -type AccountAuthState = 'authorized' | 'expired' | 'attention' | 'risk' +type AccountAuthState = 'authorized' | 'expired' | 'attention' type AccountRow = Readonly> & { tags: readonly string[] } const publishPlatformIds = new Set(desktopPublishMediaCatalog.map((item) => item.id)) @@ -43,9 +43,8 @@ const accounts = computed(() => ) const overview = computed(() => ({ - authorized: accounts.value.filter((item) => item.health === 'live').length, - attention: accounts.value.filter((item) => item.health === 'captcha').length, - risk: accounts.value.filter((item) => item.health === 'risk').length, + authorized: accounts.value.filter((item) => authState(item) === 'authorized').length, + attention: accounts.value.filter((item) => authState(item) !== 'authorized').length, onlineClients: snapshot.value?.summary.onlineClients ?? 0, })) @@ -76,7 +75,6 @@ const statusOptions = [ { value: 'all', label: '全部状态' }, { value: 'authorized', label: '已授权' }, { value: 'attention', label: '待处理' }, - { value: 'risk', label: '风险观察' }, { value: 'expired', label: '授权过期' }, ] as const @@ -105,7 +103,7 @@ function authState(account: AccountRow): AccountAuthState { case 'challenge_required': return 'attention' default: - return 'risk' + return 'attention' } } @@ -117,15 +115,19 @@ function authStateLabel(account: AccountRow): string { return '校验中' } - switch (authState(account)) { - case 'authorized': + switch (account.authState) { + case 'active': return '已授权' case 'expired': return '授权过期' - case 'attention': + case 'revoked': + return '已停用' + case 'challenge_required': return account.authReason === 'risk_control' ? '触发风控' : '需人工验证' - default: + case 'expiring_soon': return account.probeState === 'network_error' ? '最近校验失败' : '待重新校验' + default: + return account.probeState === 'network_error' ? '最近校验失败' : '待校验' } } @@ -134,13 +136,16 @@ function authTagColor(account: AccountRow): string { return 'processing' } - switch (authState(account)) { - case 'authorized': + switch (account.authState) { + case 'active': return 'success' case 'expired': + case 'revoked': return 'error' - case 'attention': + case 'challenge_required': return 'warning' + case 'expiring_soon': + return account.probeState === 'network_error' ? 'warning' : 'default' default: return 'default' } @@ -202,7 +207,7 @@ const filteredAccounts = computed(() => { return false } - if (selectedStatus.value !== 'all' && authState(account) !== selectedStatus.value) { + if (!matchesSelectedStatus(account)) { return false } @@ -223,6 +228,17 @@ const filteredAccounts = computed(() => { }) }) +function matchesSelectedStatus(account: AccountRow): boolean { + switch (selectedStatus.value) { + case 'all': + return true + case 'attention': + return authState(account) !== 'authorized' + default: + return authState(account) === selectedStatus.value + } +} + function selectPlatform(platform: string) { selectedPlatform.value = platform void refreshAccounts() @@ -462,10 +478,6 @@ const tableColumns = [
待处理
{{ overview.attention }}
-
-
风险观察
-
{{ overview.risk }}
-
在线客户端
{{ overview.onlineClients }}
diff --git a/apps/desktop-client/src/shared/publisher-errors.ts b/apps/desktop-client/src/shared/publisher-errors.ts index 367f078..e4f2f78 100644 --- a/apps/desktop-client/src/shared/publisher-errors.ts +++ b/apps/desktop-client/src/shared/publisher-errors.ts @@ -18,15 +18,21 @@ export const DONGCHEDI_PLATFORM_EXCEPTION_MESSAGE = const PUBLISH_PLATFORM_LABELS: Record = { baijiahao: '百家号', bilibili: 'bilibili', + deepseek: 'DeepSeek', dongchedi: '懂车帝', + doubao: '豆包', jianshu: '简书', juejin: '稀土掘金', + kimi: 'Kimi', qiehao: '企鹅号', + qwen: '通义千问', smzdm: '什么值得买', sohuhao: '搜狐号', toutiaohao: '头条号', wangyihao: '网易号', weixin_gzh: '微信公众号', + wenxin: '文心一言', + yuanbao: '混元 / 元宝', zhihu: '知乎', zol: '中关村在线', } @@ -36,6 +42,96 @@ function normalizeErrorText(value: string | null | undefined): string | null { return trimmed ? trimmed : null } +export function translatePublisherPlatform(platform: string | null | undefined): string { + const normalized = platform?.trim() + if (!normalized) { + return '当前平台' + } + return PUBLISH_PLATFORM_LABELS[normalized.toLowerCase()] ?? normalized +} + +function errorPlatformSuffix(message: string, code: string): string | null { + const prefix = `${code}:` + return message.toLowerCase().startsWith(prefix) + ? message.slice(prefix.length).trim() || null + : null +} + +export function normalizeDesktopAccountErrorMessage( + message: string | null | undefined, + platform?: string | null, +): string | null { + const normalized = normalizeErrorText(message) + if (!normalized) { + return null + } + + const sessionExpiredPlatform = errorPlatformSuffix( + normalized, + 'desktop_account_session_expired', + ) + if (sessionExpiredPlatform !== null) { + const platformLabel = translatePublisherPlatform(sessionExpiredPlatform || platform) + return `${platformLabel}登录态已失效,请重新授权后再试。` + } + + const unsupportedPlatform = errorPlatformSuffix(normalized, 'desktop_platform_not_supported') + if (unsupportedPlatform !== null) { + const platformLabel = translatePublisherPlatform(unsupportedPlatform || platform) + return `${platformLabel}暂时还不支持这个操作。` + } + + const platformLabel = translatePublisherPlatform(platform) + switch (normalized) { + case 'desktop_account_bind_window_closed': + return '授权窗口已关闭,本次绑定没有完成。重新点击“绑定账号”即可继续。' + case 'desktop_account_bind_limit_reached': + return '当前最多同时打开 2 个授权窗口。请先完成或关闭已有授权窗口,再继续新的绑定。' + case 'desktop_account_upsert_failed': + return '平台侧授权已经完成,但客户端回写账号信息失败。请稍后重试。' + case 'desktop_account_bind_failed': + return '授权流程执行失败,请稍后重新发起绑定。' + case 'desktop_account_sync_conflict': + return '当前账号状态刚刚发生了变化。请先刷新列表,再重试这次操作。' + case 'desktop_account_delete_failed': + return '服务端解绑没有成功完成,请稍后重试。' + case 'desktop_account_auth_expired': + return platform + ? `${platformLabel}登录态已失效,请重新授权后再试。` + : '发布账号登录态已失效,请重新授权后再试。' + case 'desktop_account_risk_control': + return platform + ? `${platformLabel}账号疑似触发风控,请打开平台处理验证或等待限制解除后再重试。` + : '发布账号疑似触发风控,请打开平台处理验证或等待限制解除后再重试。' + case 'desktop_account_challenge_required': + return platform + ? `${platformLabel}账号需要完成人机验证,请打开平台完成验证后再重试。` + : '发布账号需要完成人机验证,请打开平台完成验证后再重试。' + case 'desktop_account_probe_pending': + return platform + ? `${platformLabel}账号最近校验失败,已暂缓执行任务。请刷新状态后再重试。` + : '发布账号最近校验失败,已暂缓执行任务。请刷新状态后再重试。' + case 'desktop_account_client_missing': + return '所选账号还没有绑定桌面客户端,暂时无法发布。' + case 'desktop_account_not_found': + return '所选桌面账号不存在,请刷新后重试。' + case 'desktop_account_not_publishable': + return '所选账号需要重新授权后才能发布。' + case 'desktop_account_lookup_failed': + return '查询桌面账号失败,请刷新后重试。' + case 'desktop_accounts_query_failed': + return '查询桌面账号列表失败,请刷新后重试。' + case 'desktop_account_sync_version_required_existing': + return '账号同步版本已失效,请刷新后重试。' + case 'desktop_account_patch_failed': + return '更新桌面账号失败,请稍后重试。' + case 'desktop_account_request_delete_failed': + return '更新账号解绑状态失败,请稍后重试。' + default: + return null + } +} + function isDongchediPlatform(platform?: string | null): boolean { return platform?.trim().toLowerCase() === 'dongchedi' } @@ -172,6 +268,10 @@ export function normalizePublisherErrorMessage( message: string | null | undefined, platform?: string | null, ): string | null { + const desktopAccountMessage = normalizeDesktopAccountErrorMessage(message, platform) + if (desktopAccountMessage) { + return desktopAccountMessage + } if (message === 'compliance_blocked') { return '内容合规检测已阻断本次发布,请修改文章后重新发布。' }