feat(desktop-client): expand publisher error normalization

新增登录态失效与微信公众号外链缺失两类发布错误归一化,
将原本散落在适配器里的中文提示集中到 shared/publisher-errors,
让渲染层与 main 进程共用同一份用户可读文案。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-01 10:21:54 +08:00
parent 3436d9536d
commit 7b6ae0f6d0
3 changed files with 88 additions and 3 deletions
@@ -8,6 +8,10 @@ import {
sessionFetchText,
uploadHtmlImages,
} from "./common";
import {
WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
normalizeWeixinGzhPublishErrorMessage,
} from "../../shared/publisher-errors";
import {
fetchImageAssetBlob,
getCenteredCropRect,
@@ -1932,10 +1936,10 @@ function failureResult(error: unknown): ReturnType<PublishAdapter["publish"]> ex
if (message.startsWith("weixin_gzh_public_url_missing")) {
return {
status: "failed",
summary: "微信公众号已提交发布,但未能获取外部文章链接,请在公众号后台发表记录确认。",
summary: WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
error: {
code: "weixin_gzh_public_url_missing",
message,
message: normalizeWeixinGzhPublishErrorMessage(message) ?? WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
},
};
}
@@ -1,14 +1,18 @@
import { describe, expect, it } from "vitest";
import {
PUBLISH_ACCOUNT_NOT_LOGGED_IN_MESSAGE,
QIEHAO_REAL_NAME_AUTH_MESSAGE,
WANGYIHAO_DRAFT_SAVE_PARAMETER_MESSAGE,
WANGYIHAO_PUBLISH_CLICK_MESSAGE,
WANGYIHAO_TOKEN_MISSING_MESSAGE,
WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
isQiehaoRealNameAuthError,
normalizePublisherErrorMessage,
normalizePublisherLoginErrorMessage,
normalizeQiehaoPublishErrorMessage,
normalizeWangyihaoPublishErrorMessage,
normalizeWeixinGzhPublishErrorMessage,
} from "./publisher-errors";
describe("publisher error normalization", () => {
@@ -23,6 +27,24 @@ describe("publisher error normalization", () => {
expect(normalizeQiehaoPublishErrorMessage("publish rejected", -5022)).toBe(QIEHAO_REAL_NAME_AUTH_MESSAGE);
});
it("normalizes publisher login-state failures", () => {
expect(normalizePublisherErrorMessage("wangyihao_not_logged_in")).toBe(
"网易号账号登录态已失效,请重新登录该平台后再重试。",
);
expect(normalizePublisherErrorMessage("Error: weixin_gzh_not_logged_in")).toBe(
"微信公众号账号登录态已失效,请重新登录该平台后再重试。",
);
expect(normalizePublisherErrorMessage("qiehao_cookie_missing")).toBe(
"企鹅号账号登录态已失效,请重新登录该平台后再重试。",
);
});
it("uses a generic Chinese message for unknown publisher login-state failures", () => {
expect(normalizePublisherLoginErrorMessage("unknown_platform_not_logged_in")).toBe(
PUBLISH_ACCOUNT_NOT_LOGGED_IN_MESSAGE,
);
});
it("normalizes Wangyihao token missing failures", () => {
expect(normalizePublisherErrorMessage("wangyihao_token_missing")).toBe(WANGYIHAO_TOKEN_MISSING_MESSAGE);
expect(normalizeWangyihaoPublishErrorMessage("Error: wangyihao_token_missing")).toBe(
@@ -48,6 +70,15 @@ describe("publisher error normalization", () => {
);
});
it("normalizes Weixin GZH missing public URL failures", () => {
expect(normalizePublisherErrorMessage("weixin_gzh_public_url_missing:100000093")).toBe(
WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
);
expect(normalizeWeixinGzhPublishErrorMessage("Error: weixin_gzh_public_url_missing")).toBe(
WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
);
});
it("leaves unknown publisher errors unchanged", () => {
expect(normalizePublisherErrorMessage("qiehao_publish_failed")).toBe("qiehao_publish_failed");
});
@@ -1,9 +1,28 @@
export const QIEHAO_REAL_NAME_AUTH_MESSAGE = "企鹅号账号未完成实名认证,请先到企鹅号后台完成实名认证后再重试。";
export const PUBLISH_ACCOUNT_NOT_LOGGED_IN_MESSAGE = "发布账号登录态已失效,请重新打开平台后台完成登录后再重试。";
export const WANGYIHAO_TOKEN_MISSING_MESSAGE = "网易号发布凭证获取失败,请重新打开网易号后台确认登录状态。";
export const WANGYIHAO_DRAFT_SAVE_PARAMETER_MESSAGE =
"网易号草稿保存失败:参数错误。请检查标题、正文、封面或账号状态后重试。";
export const WANGYIHAO_PUBLISH_CLICK_MESSAGE =
"网易号草稿已保存,但自动发布未完成。请打开网易号后台确认草稿状态后重试。";
export const WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE =
"微信公众号可能已触发管理员/运营者授权发布,桌面端未能获取到文章外链。请打开公众号后台完成授权发布,并在发表记录中确认文章链接。";
const PUBLISH_PLATFORM_LABELS: Record<string, string> = {
baijiahao: "百家号",
bilibili: "bilibili",
dongchedi: "懂车帝",
jianshu: "简书",
juejin: "稀土掘金",
qiehao: "企鹅号",
smzdm: "什么值得买",
sohuhao: "搜狐号",
toutiaohao: "头条号",
wangyihao: "网易号",
weixin_gzh: "微信公众号",
zhihu: "知乎",
zol: "中关村在线",
};
function normalizeErrorText(value: string | null | undefined): string | null {
const trimmed = value?.trim();
@@ -42,6 +61,23 @@ export function normalizeQiehaoPublishErrorMessage(
return isQiehaoRealNameAuthError(normalized, code) ? QIEHAO_REAL_NAME_AUTH_MESSAGE : normalized;
}
export function normalizePublisherLoginErrorMessage(message: string | null | undefined): string | null {
const normalized = normalizeErrorText(message);
if (!normalized) {
return null;
}
const match = normalized.match(
/\b([a-z0-9_]+?)_(not_logged_in|token_missing|cookie_missing|csrf_missing)\b/i,
);
if (!match) {
return normalized;
}
const platformLabel = PUBLISH_PLATFORM_LABELS[match[1]?.toLowerCase() ?? ""];
return platformLabel ? `${platformLabel}账号登录态已失效,请重新登录该平台后再重试。` : PUBLISH_ACCOUNT_NOT_LOGGED_IN_MESSAGE;
}
export function normalizeWangyihaoPublishErrorMessage(message: string | null | undefined): string | null {
const normalized = normalizeErrorText(message);
if (!normalized) {
@@ -63,7 +99,21 @@ export function normalizeWangyihaoPublishErrorMessage(message: string | null | u
return normalized;
}
export function normalizeWeixinGzhPublishErrorMessage(message: string | null | undefined): string | null {
const normalized = normalizeErrorText(message);
if (!normalized) {
return null;
}
if (/\bweixin_gzh_public_url_missing\b/i.test(normalized)) {
return WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE;
}
return normalized;
}
export function normalizePublisherErrorMessage(message: string | null | undefined): string | null {
const qiehaoMessage = normalizeQiehaoPublishErrorMessage(message);
return normalizeWangyihaoPublishErrorMessage(qiehaoMessage);
const wangyihaoMessage = normalizeWangyihaoPublishErrorMessage(qiehaoMessage);
const loginMessage = normalizePublisherLoginErrorMessage(wangyihaoMessage);
return normalizeWeixinGzhPublishErrorMessage(loginMessage);
}