Compare commits
4 Commits
052299e54f
...
0f4310c345
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f4310c345 | |||
| 764abf32ae | |||
| 7b6ae0f6d0 | |||
| 3436d9536d |
@@ -301,21 +301,23 @@ async function copyPublishLink(record: PublishRecord): Promise<void> {
|
|||||||
:label="t('article.meta.sourceArticle')"
|
:label="t('article.meta.sourceArticle')"
|
||||||
:span="2"
|
:span="2"
|
||||||
>
|
>
|
||||||
<a
|
<div class="article-drawer__source-wrapper">
|
||||||
class="article-drawer__source-link"
|
<a
|
||||||
:href="sourceArticleURL"
|
class="article-drawer__source-link"
|
||||||
target="_blank"
|
:href="sourceArticleURL"
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
:title="sourceArticleURL"
|
rel="noopener noreferrer"
|
||||||
>
|
:title="sourceArticleURL"
|
||||||
<LinkOutlined />
|
>
|
||||||
<span class="article-drawer__source-copy">
|
<LinkOutlined class="article-drawer__source-icon" />
|
||||||
<span v-if="sourceArticleTitle" class="article-drawer__source-title">
|
<span class="article-drawer__source-copy">
|
||||||
{{ sourceArticleTitle }}
|
<span v-if="sourceArticleTitle" class="article-drawer__source-title">
|
||||||
|
{{ sourceArticleTitle }}
|
||||||
|
</span>
|
||||||
|
<span class="article-drawer__source-url">{{ sourceArticleURL }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="article-drawer__source-url">{{ sourceArticleURL }}</span>
|
</a>
|
||||||
</span>
|
</div>
|
||||||
</a>
|
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
</section>
|
</section>
|
||||||
@@ -459,14 +461,24 @@ async function copyPublishLink(record: PublishRecord): Promise<void> {
|
|||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.article-drawer__source-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.article-drawer__source-link {
|
.article-drawer__source-link {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
max-width: 100%;
|
width: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
color: #1677ff;
|
color: #1677ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.article-drawer__source-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.article-drawer__source-copy {
|
.article-drawer__source-copy {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
@@ -1101,7 +1101,7 @@ const enUS = {
|
|||||||
previewHintLocked: "A selected platform requires a fixed cover ratio, so the cover will be exported using the current platform requirement.",
|
previewHintLocked: "A selected platform requires a fixed cover ratio, so the cover will be exported using the current platform requirement.",
|
||||||
previewHintFree: "There is no forced cover ratio right now. You can upload the original image directly or adjust framing before confirming.",
|
previewHintFree: "There is no forced cover ratio right now. You can upload the original image directly or adjust framing before confirming.",
|
||||||
requirementsTitle: "Platform requirements",
|
requirementsTitle: "Platform requirements",
|
||||||
requirementsHint: "Requirements switch automatically based on the selected publish platforms. Baijiahao and Dongchedi require a cover image.",
|
requirementsHint: "Requirements switch automatically based on the selected publish platforms. WeChat Official Account, Baijiahao, and Dongchedi require a cover image.",
|
||||||
recommendedRatio: "Recommended ratio",
|
recommendedRatio: "Recommended ratio",
|
||||||
recommendedSize: "Recommended size",
|
recommendedSize: "Recommended size",
|
||||||
required: "Required",
|
required: "Required",
|
||||||
|
|||||||
@@ -1110,7 +1110,7 @@ const zhCN = {
|
|||||||
previewHintLocked: "已选择需要固定封面比例的平台,确认时会按当前平台要求输出封面。",
|
previewHintLocked: "已选择需要固定封面比例的平台,确认时会按当前平台要求输出封面。",
|
||||||
previewHintFree: "当前不限制封面比例,可直接上传原图;如需调整也可拖拽缩放后再确认。",
|
previewHintFree: "当前不限制封面比例,可直接上传原图;如需调整也可拖拽缩放后再确认。",
|
||||||
requirementsTitle: "平台封面要求",
|
requirementsTitle: "平台封面要求",
|
||||||
requirementsHint: "会根据当前选中的发布平台自动切换要求,百家号和懂车帝为强制上传。",
|
requirementsHint: "会根据当前选中的发布平台自动切换要求,微信公众号、百家号和懂车帝为强制上传。",
|
||||||
recommendedRatio: "推荐比例",
|
recommendedRatio: "推荐比例",
|
||||||
recommendedSize: "推荐尺寸",
|
recommendedSize: "推荐尺寸",
|
||||||
required: "必传",
|
required: "必传",
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ const defaultRequirement = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const requirementCatalog: Record<string, Partial<PlatformCoverRequirement>> = {
|
const requirementCatalog: Record<string, Partial<PlatformCoverRequirement>> = {
|
||||||
|
weixin_gzh: {
|
||||||
|
required: true,
|
||||||
|
supportMode: "native",
|
||||||
|
aspectRatio: 2.35,
|
||||||
|
aspectLabel: "2.35:1",
|
||||||
|
outputWidth: 1200,
|
||||||
|
outputHeight: 511,
|
||||||
|
priority: 110,
|
||||||
|
},
|
||||||
baijiahao: {
|
baijiahao: {
|
||||||
required: true,
|
required: true,
|
||||||
supportMode: "native",
|
supportMode: "native",
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import {
|
|||||||
sessionFetchText,
|
sessionFetchText,
|
||||||
uploadHtmlImages,
|
uploadHtmlImages,
|
||||||
} from "./common";
|
} from "./common";
|
||||||
|
import {
|
||||||
|
WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
|
||||||
|
normalizeWeixinGzhPublishErrorMessage,
|
||||||
|
} from "../../shared/publisher-errors";
|
||||||
import {
|
import {
|
||||||
fetchImageAssetBlob,
|
fetchImageAssetBlob,
|
||||||
getCenteredCropRect,
|
getCenteredCropRect,
|
||||||
@@ -1932,10 +1936,10 @@ function failureResult(error: unknown): ReturnType<PublishAdapter["publish"]> ex
|
|||||||
if (message.startsWith("weixin_gzh_public_url_missing")) {
|
if (message.startsWith("weixin_gzh_public_url_missing")) {
|
||||||
return {
|
return {
|
||||||
status: "failed",
|
status: "failed",
|
||||||
summary: "微信公众号已提交发布,但未能获取外部文章链接,请在公众号后台发表记录确认。",
|
summary: WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
|
||||||
error: {
|
error: {
|
||||||
code: "weixin_gzh_public_url_missing",
|
code: "weixin_gzh_public_url_missing",
|
||||||
message,
|
message: normalizeWeixinGzhPublishErrorMessage(message) ?? WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
|||||||
|
|
||||||
import StatusBadge from "../components/StatusBadge.vue";
|
import StatusBadge from "../components/StatusBadge.vue";
|
||||||
import { useDesktopRuntime } from "../composables/useDesktopRuntime";
|
import { useDesktopRuntime } from "../composables/useDesktopRuntime";
|
||||||
|
import { unwrapClientErrorMessage } from "../lib/client-errors";
|
||||||
import { formatDateTime, formatRelativeTime } from "../lib/formatters";
|
import { formatDateTime, formatRelativeTime } from "../lib/formatters";
|
||||||
import { desktopPlatformShortName, desktopPublishMediaCatalog, translateDesktopPlatform } from "../lib/media-catalog";
|
import { desktopPlatformShortName, desktopPublishMediaCatalog, translateDesktopPlatform } from "../lib/media-catalog";
|
||||||
import { normalizePublisherErrorMessage } from "../../shared/publisher-errors";
|
import { normalizePublisherErrorMessage } from "../../shared/publisher-errors";
|
||||||
@@ -199,7 +200,7 @@ function summaryForTask(
|
|||||||
): string {
|
): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "queued":
|
case "queued":
|
||||||
return "任务已进入队列,等前面的 PublishTasks 翻完后再继续展示历史记录。";
|
return "任务已进入队列,待前面的发布任务完成后继续执行。";
|
||||||
case "in_progress":
|
case "in_progress":
|
||||||
return "当前正在由桌面端执行发送,请避免重复触发。";
|
return "当前正在由桌面端执行发送,请避免重复触发。";
|
||||||
case "succeeded":
|
case "succeeded":
|
||||||
@@ -277,7 +278,7 @@ async function refreshTasks(page = currentPage.value) {
|
|||||||
taskPage.value = response;
|
taskPage.value = response;
|
||||||
currentPage.value = response.page;
|
currentPage.value = response.page;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err instanceof Error ? err.message : "desktop publish tasks unavailable";
|
error.value = normalizeTaskErrorMessage(unwrapClientErrorMessage(err, "发布任务暂时不可用"));
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@@ -291,7 +292,7 @@ async function retryTask(taskId: string) {
|
|||||||
notifySuccess("已重新加入发布队列");
|
notifySuccess("已重新加入发布队列");
|
||||||
await refreshTasks();
|
await refreshTasks();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifyActionError(err instanceof Error ? err.message : "重新发送失败");
|
notifyActionError(normalizeTaskErrorMessage(unwrapClientErrorMessage(err, "重新发送失败")) ?? "重新发送失败");
|
||||||
} finally {
|
} finally {
|
||||||
actionPendingTaskId.value = null;
|
actionPendingTaskId.value = null;
|
||||||
}
|
}
|
||||||
@@ -305,7 +306,7 @@ async function openExternalUrl(url: string | null) {
|
|||||||
try {
|
try {
|
||||||
await window.desktopBridge.app.openExternalUrl(url);
|
await window.desktopBridge.app.openExternalUrl(url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifyActionError(err instanceof Error ? err.message : "打开外链失败");
|
notifyActionError(normalizeTaskErrorMessage(unwrapClientErrorMessage(err, "打开外链失败")) ?? "打开外链失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +322,7 @@ async function openAccountWorkbench(record: PublishTaskItem) {
|
|||||||
displayName: account?.displayName ?? record.accountName,
|
displayName: account?.displayName ?? record.accountName,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifyActionError(err instanceof Error ? err.message : "打开账号工作台失败");
|
notifyActionError(normalizeTaskErrorMessage(unwrapClientErrorMessage(err, "打开账号工作台失败")) ?? "打开账号工作台失败");
|
||||||
} finally {
|
} finally {
|
||||||
consolePendingTaskId.value = null;
|
consolePendingTaskId.value = null;
|
||||||
}
|
}
|
||||||
@@ -359,7 +360,7 @@ async function copyErrorMessage(record: PublishTaskItem) {
|
|||||||
}
|
}
|
||||||
}, 1_500);
|
}, 1_500);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifyActionError(err instanceof Error ? err.message : "复制错误详情失败");
|
notifyActionError(normalizeTaskErrorMessage(unwrapClientErrorMessage(err, "复制错误详情失败")) ?? "复制错误详情失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,7 +471,7 @@ const tableScroll = { x: 1080 } as const;
|
|||||||
<section class="hero-card">
|
<section class="hero-card">
|
||||||
<div class="hero-header">
|
<div class="hero-header">
|
||||||
<div class="hero-title">
|
<div class="hero-title">
|
||||||
<p class="eyebrow">PUBLISH EXECUTION DESK</p>
|
<p class="eyebrow">发布执行台</p>
|
||||||
<h2>发布管理</h2>
|
<h2>发布管理</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-actions">
|
<div class="hero-actions">
|
||||||
@@ -505,7 +506,7 @@ const tableScroll = { x: 1080 } as const;
|
|||||||
<div class="hero-header table-toolbar">
|
<div class="hero-header table-toolbar">
|
||||||
<div class="toolbar-meta">
|
<div class="toolbar-meta">
|
||||||
<h3 class="section-title">待发布与已发送记录</h3>
|
<h3 class="section-title">待发布与已发送记录</h3>
|
||||||
<p class="section-desc">待发布任务始终排在最前,每页 10 条;分页与总数以服务端返回的 PublishTasks 为准。</p>
|
<p class="section-desc">待发布任务始终排在最前,每页 10 条;分页与总数以服务端返回的发布任务为准。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-filters">
|
<div class="toolbar-filters">
|
||||||
<a-input
|
<a-input
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
PUBLISH_ACCOUNT_NOT_LOGGED_IN_MESSAGE,
|
||||||
QIEHAO_REAL_NAME_AUTH_MESSAGE,
|
QIEHAO_REAL_NAME_AUTH_MESSAGE,
|
||||||
WANGYIHAO_DRAFT_SAVE_PARAMETER_MESSAGE,
|
WANGYIHAO_DRAFT_SAVE_PARAMETER_MESSAGE,
|
||||||
WANGYIHAO_PUBLISH_CLICK_MESSAGE,
|
WANGYIHAO_PUBLISH_CLICK_MESSAGE,
|
||||||
WANGYIHAO_TOKEN_MISSING_MESSAGE,
|
WANGYIHAO_TOKEN_MISSING_MESSAGE,
|
||||||
|
WEIXIN_GZH_PUBLIC_URL_MISSING_MESSAGE,
|
||||||
isQiehaoRealNameAuthError,
|
isQiehaoRealNameAuthError,
|
||||||
normalizePublisherErrorMessage,
|
normalizePublisherErrorMessage,
|
||||||
|
normalizePublisherLoginErrorMessage,
|
||||||
normalizeQiehaoPublishErrorMessage,
|
normalizeQiehaoPublishErrorMessage,
|
||||||
normalizeWangyihaoPublishErrorMessage,
|
normalizeWangyihaoPublishErrorMessage,
|
||||||
|
normalizeWeixinGzhPublishErrorMessage,
|
||||||
} from "./publisher-errors";
|
} from "./publisher-errors";
|
||||||
|
|
||||||
describe("publisher error normalization", () => {
|
describe("publisher error normalization", () => {
|
||||||
@@ -23,6 +27,24 @@ describe("publisher error normalization", () => {
|
|||||||
expect(normalizeQiehaoPublishErrorMessage("publish rejected", -5022)).toBe(QIEHAO_REAL_NAME_AUTH_MESSAGE);
|
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", () => {
|
it("normalizes Wangyihao token missing failures", () => {
|
||||||
expect(normalizePublisherErrorMessage("wangyihao_token_missing")).toBe(WANGYIHAO_TOKEN_MISSING_MESSAGE);
|
expect(normalizePublisherErrorMessage("wangyihao_token_missing")).toBe(WANGYIHAO_TOKEN_MISSING_MESSAGE);
|
||||||
expect(normalizeWangyihaoPublishErrorMessage("Error: wangyihao_token_missing")).toBe(
|
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", () => {
|
it("leaves unknown publisher errors unchanged", () => {
|
||||||
expect(normalizePublisherErrorMessage("qiehao_publish_failed")).toBe("qiehao_publish_failed");
|
expect(normalizePublisherErrorMessage("qiehao_publish_failed")).toBe("qiehao_publish_failed");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,28 @@
|
|||||||
export const QIEHAO_REAL_NAME_AUTH_MESSAGE = "企鹅号账号未完成实名认证,请先到企鹅号后台完成实名认证后再重试。";
|
export const QIEHAO_REAL_NAME_AUTH_MESSAGE = "企鹅号账号未完成实名认证,请先到企鹅号后台完成实名认证后再重试。";
|
||||||
|
export const PUBLISH_ACCOUNT_NOT_LOGGED_IN_MESSAGE = "发布账号登录态已失效,请重新打开平台后台完成登录后再重试。";
|
||||||
export const WANGYIHAO_TOKEN_MISSING_MESSAGE = "网易号发布凭证获取失败,请重新打开网易号后台确认登录状态。";
|
export const WANGYIHAO_TOKEN_MISSING_MESSAGE = "网易号发布凭证获取失败,请重新打开网易号后台确认登录状态。";
|
||||||
export const WANGYIHAO_DRAFT_SAVE_PARAMETER_MESSAGE =
|
export const WANGYIHAO_DRAFT_SAVE_PARAMETER_MESSAGE =
|
||||||
"网易号草稿保存失败:参数错误。请检查标题、正文、封面或账号状态后重试。";
|
"网易号草稿保存失败:参数错误。请检查标题、正文、封面或账号状态后重试。";
|
||||||
export const WANGYIHAO_PUBLISH_CLICK_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 {
|
function normalizeErrorText(value: string | null | undefined): string | null {
|
||||||
const trimmed = value?.trim();
|
const trimmed = value?.trim();
|
||||||
@@ -42,6 +61,23 @@ export function normalizeQiehaoPublishErrorMessage(
|
|||||||
return isQiehaoRealNameAuthError(normalized, code) ? QIEHAO_REAL_NAME_AUTH_MESSAGE : normalized;
|
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 {
|
export function normalizeWangyihaoPublishErrorMessage(message: string | null | undefined): string | null {
|
||||||
const normalized = normalizeErrorText(message);
|
const normalized = normalizeErrorText(message);
|
||||||
if (!normalized) {
|
if (!normalized) {
|
||||||
@@ -63,7 +99,21 @@ export function normalizeWangyihaoPublishErrorMessage(message: string | null | u
|
|||||||
return normalized;
|
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 {
|
export function normalizePublisherErrorMessage(message: string | null | undefined): string | null {
|
||||||
const qiehaoMessage = normalizeQiehaoPublishErrorMessage(message);
|
const qiehaoMessage = normalizeQiehaoPublishErrorMessage(message);
|
||||||
return normalizeWangyihaoPublishErrorMessage(qiehaoMessage);
|
const wangyihaoMessage = normalizeWangyihaoPublishErrorMessage(qiehaoMessage);
|
||||||
|
const loginMessage = normalizePublisherLoginErrorMessage(wangyihaoMessage);
|
||||||
|
return normalizeWeixinGzhPublishErrorMessage(loginMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
@@ -136,13 +137,22 @@ func (s *MediaService) listArticlePublishRecords(ctx context.Context, tenantID,
|
|||||||
|
|
||||||
func publishBatchRequiresCover(accounts []platformAccountSeed) bool {
|
func publishBatchRequiresCover(accounts []platformAccountSeed) bool {
|
||||||
for _, account := range accounts {
|
for _, account := range accounts {
|
||||||
if account.PlatformID == "baijiahao" || account.PlatformID == "dongchedi" {
|
if publishPlatformRequiresCover(account.PlatformID) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func publishPlatformRequiresCover(platformID string) bool {
|
||||||
|
switch strings.TrimSpace(platformID) {
|
||||||
|
case "weixin_gzh", "baijiahao", "dongchedi":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func pointerStringValue(value *string) string {
|
func pointerStringValue(value *string) string {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ func (s *PublishJobService) Create(ctx context.Context, actor auth.Actor, req Cr
|
|||||||
accountSeeds = append(accountSeeds, *target.accountSeed)
|
accountSeeds = append(accountSeeds, *target.accountSeed)
|
||||||
}
|
}
|
||||||
if publishBatchRequiresCover(accountSeeds) && strings.TrimSpace(pointerStringValue(articleMeta.CoverAssetURL)) == "" {
|
if publishBatchRequiresCover(accountSeeds) && strings.TrimSpace(pointerStringValue(articleMeta.CoverAssetURL)) == "" {
|
||||||
return nil, response.ErrBadRequest(40044, "publish_cover_required", "baijiahao publishing requires a cover image")
|
return nil, response.ErrBadRequest(40044, "publish_cover_required", "selected publishing platform requires a cover image")
|
||||||
}
|
}
|
||||||
|
|
||||||
publishBatchID, publishRecordIDs, err := createPublishBatchRecords(
|
publishBatchID, publishRecordIDs, err := createPublishBatchRecords(
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPublishPlatformRequiresCover(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, platformID := range []string{"weixin_gzh", "baijiahao", "dongchedi"} {
|
||||||
|
if !publishPlatformRequiresCover(platformID) {
|
||||||
|
t.Fatalf("publishPlatformRequiresCover(%q) = false, want true", platformID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if publishPlatformRequiresCover("zhihu") {
|
||||||
|
t.Fatal("publishPlatformRequiresCover(\"zhihu\") = true, want false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPublishCoverRequirementCallersIncludeWeixinGzh(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
accounts := []platformAccountSeed{{PlatformID: "weixin_gzh"}}
|
||||||
|
if !publishBatchRequiresCover(accounts) {
|
||||||
|
t.Fatal("publishBatchRequiresCover() = false for weixin_gzh, want true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !schedulePublishPlatformsRequireCover([]string{"weixin_gzh"}) {
|
||||||
|
t.Fatal("schedulePublishPlatformsRequireCover() = false for weixin_gzh, want true")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -564,8 +564,7 @@ func (s *ScheduleTaskService) loadPublishAccountPlatformIDs(ctx context.Context,
|
|||||||
|
|
||||||
func schedulePublishPlatformsRequireCover(platformIDs []string) bool {
|
func schedulePublishPlatformsRequireCover(platformIDs []string) bool {
|
||||||
for _, platformID := range platformIDs {
|
for _, platformID := range platformIDs {
|
||||||
switch strings.TrimSpace(platformID) {
|
if publishPlatformRequiresCover(platformID) {
|
||||||
case "baijiahao", "dongchedi":
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user