feat(publish): handle qiehao real-name auth via shared publisher errors

- Detect qiehao's real-name auth blocking response (code/message) in the
  desktop adapter, surface a Chinese-friendly summary, and propagate the
  detail through the failure result.
- Extract a shared publisher-errors module so the renderer can normalize
  these messages for the publish task table without duplicating regex.
- Pre-process article HTML for qiehao before upload via a new
  packages/publisher-platforms/src/qiehao.ts entry point.
- Update the manual self-test notes for jianshu and qiehao with the
  latest results.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 11:03:04 +08:00
parent dbce8515e7
commit 7ddd3d0c6c
8 changed files with 129 additions and 5 deletions
@@ -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");
});
});
@@ -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);
}