diff --git a/apps/desktop-client/src/main/adapters/qiehao.test.ts b/apps/desktop-client/src/main/adapters/qiehao.test.ts
new file mode 100644
index 0000000..3147702
--- /dev/null
+++ b/apps/desktop-client/src/main/adapters/qiehao.test.ts
@@ -0,0 +1,30 @@
+import { describe, expect, it } from "vitest";
+
+import { prepareQiehaoArticleHtml } from "../../../../../packages/publisher-platforms/src/qiehao";
+import { normalizeArticleHtml } from "./common";
+
+describe("qiehao article content", () => {
+ it("downgrades wide markdown tables to paragraph rows before publishing", () => {
+ const html = prepareQiehaoArticleHtml(
+ normalizeArticleHtml({
+ article_id: 1,
+ title: "表格测试",
+ html_content: null,
+ markdown_content: [
+ "| 品牌 | 品牌定位 | 核心优势 |",
+ "| --- | --- | --- |",
+ "| 我乐家居 | 主打原创设计的全国性定制品牌,定位偏向时尚轻奢风格 | 拥有多项外观设计专利,产品颜值辨识度高;采用无醛添加环保板材 |",
+ "| 志邦家居 | 国内上市定制品牌,整体实力稳健 | 全国布局超4000家门店,合肥本地服务网络完善 |",
+ "",
+ "下一段中文说明",
+ ].join("\n"),
+ cover_asset_url: null,
+ }),
+ );
+
+ expect(html).not.toContain("
品牌 品牌定位 核心优势");
+ expect(html).toContain("我乐家居 主打原创设计");
+ expect(html).toContain("下一段中文说明
");
+ });
+});
diff --git a/apps/desktop-client/src/main/adapters/qiehao.ts b/apps/desktop-client/src/main/adapters/qiehao.ts
index f5f1e5c..163799d 100644
--- a/apps/desktop-client/src/main/adapters/qiehao.ts
+++ b/apps/desktop-client/src/main/adapters/qiehao.ts
@@ -1,4 +1,5 @@
import type { JsonValue } from "@geo/shared-types";
+import { prepareQiehaoArticleHtml } from "../../../../../packages/publisher-platforms/src/qiehao";
import { resolveDesktopApiURL } from "../transport/api-client";
import {
@@ -9,6 +10,10 @@ import {
uploadHtmlImages,
} from "./common";
import type { PublishAdapter, PublishAdapterContext } from "./base";
+import {
+ QIEHAO_REAL_NAME_AUTH_MESSAGE,
+ isQiehaoRealNameAuthError,
+} from "../../shared/publisher-errors";
const QIEHAO_APP_ID = "LA6zXi1lWzAioIzdiAD6iM10aHarlHF6";
const QIEHAO_ORIGIN = "https://om.qq.com";
@@ -356,6 +361,18 @@ function buildResultPayload(
function failureResult(error: unknown): ReturnType extends Promise ? T : never {
const message = error instanceof Error ? error.message : String(error);
+ if (isQiehaoRealNameAuthError(message)) {
+ return {
+ status: "failed",
+ summary: QIEHAO_REAL_NAME_AUTH_MESSAGE,
+ error: {
+ code: "qiehao_real_name_auth_required",
+ message: QIEHAO_REAL_NAME_AUTH_MESSAGE,
+ detail: message,
+ },
+ };
+ }
+
if (message === "qiehao_not_logged_in") {
return {
status: "failed",
@@ -462,7 +479,7 @@ export const qiehaoAdapter: PublishAdapter = {
throw new Error("qiehao_cookie_missing");
}
- const html = normalizeArticleHtml(context.article);
+ const html = prepareQiehaoArticleHtml(normalizeArticleHtml(context.article));
if (!html) {
throw new Error("article_content_empty");
}
@@ -527,6 +544,9 @@ export const qiehaoAdapter: PublishAdapter = {
if (isQiehaoChallengeMessage(message)) {
throw new Error(`qiehao_challenge_required:${message}`);
}
+ if (isQiehaoRealNameAuthError(message, responseCode)) {
+ throw new Error(`qiehao_real_name_auth_required:${message || responseCode}`);
+ }
throw new Error(message || "qiehao_publish_failed");
}
diff --git a/apps/desktop-client/src/renderer/views/PublishManagementView.vue b/apps/desktop-client/src/renderer/views/PublishManagementView.vue
index 8c35bd5..79cb86d 100644
--- a/apps/desktop-client/src/renderer/views/PublishManagementView.vue
+++ b/apps/desktop-client/src/renderer/views/PublishManagementView.vue
@@ -13,6 +13,7 @@ import StatusBadge from "../components/StatusBadge.vue";
import { useDesktopRuntime } from "../composables/useDesktopRuntime";
import { formatDateTime, formatRelativeTime, titleCaseToken } from "../lib/formatters";
import { desktopPublishMediaCatalog } from "../lib/media-catalog";
+import { normalizePublisherErrorMessage } from "../../shared/publisher-errors";
const PAGE_SIZE = 10;
@@ -125,7 +126,7 @@ function normalizeTaskErrorMessage(message: string | null): string | null {
if (normalized.includes("adapter is not implemented for this platform")) {
return "当前平台的桌面发布适配器未实现,任务已按失败处理。";
}
- return normalized;
+ return normalizePublisherErrorMessage(normalized) ?? normalized;
}
function summaryForTask(status: DesktopTaskInfo["status"]): string {
diff --git a/apps/desktop-client/src/shared/publisher-errors.test.ts b/apps/desktop-client/src/shared/publisher-errors.test.ts
new file mode 100644
index 0000000..f8e8412
--- /dev/null
+++ b/apps/desktop-client/src/shared/publisher-errors.test.ts
@@ -0,0 +1,25 @@
+import { describe, expect, it } from "vitest";
+
+import {
+ QIEHAO_REAL_NAME_AUTH_MESSAGE,
+ isQiehaoRealNameAuthError,
+ normalizePublisherErrorMessage,
+ normalizeQiehaoPublishErrorMessage,
+} from "./publisher-errors";
+
+describe("publisher error normalization", () => {
+ it("normalizes Qiehao real-name authentication failures from raw platform text", () => {
+ expect(
+ normalizePublisherErrorMessage("type:business, code:-5022, msg:user is not real name authentication"),
+ ).toBe(QIEHAO_REAL_NAME_AUTH_MESSAGE);
+ });
+
+ it("recognizes Qiehao real-name authentication failures by business code", () => {
+ expect(isQiehaoRealNameAuthError("publish rejected", -5022)).toBe(true);
+ expect(normalizeQiehaoPublishErrorMessage("publish rejected", -5022)).toBe(QIEHAO_REAL_NAME_AUTH_MESSAGE);
+ });
+
+ it("leaves unknown publisher errors unchanged", () => {
+ expect(normalizePublisherErrorMessage("qiehao_publish_failed")).toBe("qiehao_publish_failed");
+ });
+});
diff --git a/apps/desktop-client/src/shared/publisher-errors.ts b/apps/desktop-client/src/shared/publisher-errors.ts
new file mode 100644
index 0000000..ff03c5d
--- /dev/null
+++ b/apps/desktop-client/src/shared/publisher-errors.ts
@@ -0,0 +1,42 @@
+export const QIEHAO_REAL_NAME_AUTH_MESSAGE = "企鹅号账号未完成实名认证,请先到企鹅号后台完成实名认证后再重试。";
+
+function normalizeErrorText(value: string | null | undefined): string | null {
+ const trimmed = value?.trim();
+ return trimmed ? trimmed : null;
+}
+
+export function isQiehaoRealNameAuthError(
+ message: string | null | undefined,
+ code?: number | string | null,
+): boolean {
+ const codeText = code == null ? "" : String(code).trim();
+ if (codeText === "-5022" || codeText === "5022") {
+ return true;
+ }
+
+ const normalized = normalizeErrorText(message);
+ if (!normalized) {
+ return false;
+ }
+
+ return /^qiehao_real_name_auth_required\b/i.test(normalized)
+ || /\bcode\s*:\s*-?5022\b/i.test(normalized)
+ || /user\s+is\s+not\s+real\s+name\s+authentication/i.test(normalized)
+ || /(?:未|没有|尚未).{0,8}实名/.test(normalized)
+ || /请.{0,8}实名认证/.test(normalized);
+}
+
+export function normalizeQiehaoPublishErrorMessage(
+ message: string | null | undefined,
+ code?: number | string | null,
+): string | null {
+ const normalized = normalizeErrorText(message);
+ if (!normalized) {
+ return null;
+ }
+ return isQiehaoRealNameAuthError(normalized, code) ? QIEHAO_REAL_NAME_AUTH_MESSAGE : normalized;
+}
+
+export function normalizePublisherErrorMessage(message: string | null | undefined): string | null {
+ return normalizeQiehaoPublishErrorMessage(message);
+}
diff --git a/packages/publisher-platforms/src/index.ts b/packages/publisher-platforms/src/index.ts
index 741ab0a..623c957 100644
--- a/packages/publisher-platforms/src/index.ts
+++ b/packages/publisher-platforms/src/index.ts
@@ -1,3 +1,4 @@
export * from "./baijiahao";
export * from "./jianshu";
+export * from "./qiehao";
export * from "./toutiao";
diff --git a/packages/publisher-platforms/src/qiehao.ts b/packages/publisher-platforms/src/qiehao.ts
new file mode 100644
index 0000000..f448ce9
--- /dev/null
+++ b/packages/publisher-platforms/src/qiehao.ts
@@ -0,0 +1,5 @@
+import { renderTablesAsParagraphRows } from "./baijiahao";
+
+export function prepareQiehaoArticleHtml(html: string): string {
+ return renderTablesAsParagraphRows(html).trim();
+}
diff --git a/媒体发布手工自测.md b/媒体发布手工自测.md
index 6702804..9547307 100644
--- a/媒体发布手工自测.md
+++ b/媒体发布手工自测.md
@@ -22,14 +22,14 @@
头条号:toutiaohao.ts
知乎: zhihu.ts
-百家号:baijiao.ts (表格官方渲染成了图片,不可以的,我们需要把表格降级处理)
+百家号:baijiao.ts (表格官方渲染成了图片,表格降级处理)
哔哩哔哩:平台表格就不支持;降级了
掘金:图 表 ok
什么值得买: 图 表 ok
+企鹅号:图片 ok 表格支持,但是会撑出去,目前是降级处理,后续再细调
+简书:jianshu.ts (图片 ok,文字,表格 ok)
-企鹅号:媒体资质未通过,另外要测
-简书:jianshu.ts (图片未测,文字,表格 ok)
网易:媒体资质没下来
搜狐:没注册成功