feat(desktop): drop parked review flow, add publish management

SaaS 侧人工审核后才创建 publish job,desktop 不再做二次审核:移除
manual/waiting_user/parked/from_parked 状态机与 LeaseFromParked 查询,
desktop client 只执行发布并新增"发布管理"页(待发布队列 / 历史 / 再次
发送)。同时抽离 @geo/publisher-platforms 共享适配器包、新增 Redis-based
desktop_presence 与 publish_record_support,刷新 admin-web 发布弹窗与
媒体库;plan A / spec 文档同步口径。
This commit is contained in:
2026-04-20 09:52:48 +08:00
parent b16e9f0bd1
commit a617d39a4a
93 changed files with 5519 additions and 3594 deletions
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { QuestionCircleOutlined, UserOutlined } from "@ant-design/icons-vue";
import { useQuery } from "@tanstack/vue-query";
import type { MediaPlatform, PlatformAccount } from "@geo/shared-types";
import type { MediaPlatform } from "@geo/shared-types";
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";
import { articlesApi, mediaApi, resolveApiURL } from "@/lib/api";
import { articlesApi, mediaApi } from "@/lib/api";
import { getPublishStatusMeta } from "@/lib/display";
import { getPublishPlatformMeta, normalizePublishPlatformId } from "@/lib/publish-platforms";
@@ -49,13 +49,6 @@ const platformsQuery = useQuery({
staleTime: 5 * 60 * 1000,
});
const accountsQuery = useQuery({
queryKey: ["media", "platform-accounts", "publish-status"],
enabled: computed(() => popoverOpen.value && shouldLoadPlatforms.value),
queryFn: () => mediaApi.accounts(),
staleTime: 5 * 60 * 1000,
});
const publishRecordsQuery = useQuery({
queryKey: computed(() => ["articles", "publish-records", props.articleId, "publish-status"]),
enabled: computed(() => popoverOpen.value && shouldLoadPlatforms.value),
@@ -73,12 +66,6 @@ const platformMap = computed(() => {
);
});
const accountMap = computed(() => {
return new Map(
(accountsQuery.data.value ?? []).map((account: PlatformAccount) => [account.id, account]),
);
});
const platformEntries = computed<PublishPlatformEntry[]>(() => {
const seen = new Set<string>();
const entries: PublishPlatformEntry[] = [];
@@ -92,7 +79,6 @@ const platformEntries = computed<PublishPlatformEntry[]>(() => {
const normalizedId = normalizePublishPlatformId(record.platform_id);
const platform = platformMap.value.get(normalizedId);
const account = accountMap.value.get(record.platform_account_id);
const fallback = getPublishPlatformMeta(normalizedId);
entries.push({
@@ -100,8 +86,8 @@ const platformEntries = computed<PublishPlatformEntry[]>(() => {
name: record.platform_name || platform?.name || fallback.name,
shortName: platform?.short_name || fallback.shortName,
accent: platform?.accent_color || fallback.accent,
accountName: account?.nickname || record.platform_nickname || "--",
avatarUrl: resolveApiURL(account?.avatar_url),
accountName: record.platform_nickname || "--",
avatarUrl: "",
status: normalizePublishRecordStatus(record.status),
});
}
@@ -251,7 +237,7 @@ function getAccountInitial(accountName: string): string {
<template #content>
<div class="article-publish-status__popover">
<div
v-if="publishRecordsQuery.isPending.value || platformsQuery.isPending.value || accountsQuery.isPending.value"
v-if="publishRecordsQuery.isPending.value || platformsQuery.isPending.value"
class="article-publish-status__loading"
>
<a-spin size="small" />
@@ -8,7 +8,7 @@ import { useI18n } from "vue-i18n";
import PromptRuleModal from "@/components/PromptRuleModal.vue";
import PublishPlatformSelector from "@/components/PublishPlatformSelector.vue";
import { generateApi, mediaApi, promptRulesApi, schedulesApi } from "@/lib/api";
import { generateApi, mediaApi, promptRulesApi, schedulesApi, tenantAccountsApi } from "@/lib/api";
import { formatError } from "@/lib/errors";
import { IMAGE_UPLOAD_ACCEPT, useImageUploadProgress, validateImageUploadFile } from "@/lib/image-webp";
import {
@@ -55,9 +55,9 @@ const platformsQuery = useQuery({
});
const accountsQuery = useQuery({
queryKey: ["media", "platform-accounts", "task-drawer"],
queryKey: ["tenant", "desktop-accounts", "task-drawer"],
enabled: computed(() => props.open),
queryFn: () => mediaApi.accounts(),
queryFn: () => tenantAccountsApi.list(),
});
const allPlatforms = computed(() =>
File diff suppressed because it is too large Load Diff