From c89683862e188b2d1eb76a626c8d634b06b3afa4 Mon Sep 17 00:00:00 2001 From: liangxu Date: Fri, 1 May 2026 16:01:40 +0800 Subject: [PATCH] feat(workspace): replace supported platform count with bound media account count The "platforms" stat tile was a constant (5) that never reflected what the tenant actually had set up. Drop SupportedPlatformCount from the workspace overview API/domain/shared-types and have admin-web compute a bound media account count from the desktop accounts list, filtered through a new isMediaPublishAccount helper that excludes deleted rows and AI-only platforms (yuanbao/kimi/wenxin/deepseek/doubao/qwen). Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/admin-web/src/i18n/messages/en-US.ts | 2 +- apps/admin-web/src/i18n/messages/zh-CN.ts | 2 +- .../src/lib/publish-account-cards.ts | 3 +- apps/admin-web/src/lib/publish-platforms.ts | 12 +++++++- apps/admin-web/src/views/MediaView.vue | 5 ++-- apps/admin-web/src/views/WorkspaceView.vue | 14 +++++++-- packages/shared-types/src/index.ts | 1 - .../internal/tenant/app/workspace_service.go | 30 ++++++++----------- server/internal/tenant/domain/workspace.go | 1 - 9 files changed, 42 insertions(+), 28 deletions(-) diff --git a/apps/admin-web/src/i18n/messages/en-US.ts b/apps/admin-web/src/i18n/messages/en-US.ts index a89e85a..62ea416 100644 --- a/apps/admin-web/src/i18n/messages/en-US.ts +++ b/apps/admin-web/src/i18n/messages/en-US.ts @@ -379,7 +379,7 @@ const enUS = { articles: "Generated", published: "Published", brands: "Brands", - platforms: "Platforms", + mediaAccounts: "Media accounts", }, sections: { generalTemplates: "General-Purpose Templates", diff --git a/apps/admin-web/src/i18n/messages/zh-CN.ts b/apps/admin-web/src/i18n/messages/zh-CN.ts index a4ac578..2b7e229 100644 --- a/apps/admin-web/src/i18n/messages/zh-CN.ts +++ b/apps/admin-web/src/i18n/messages/zh-CN.ts @@ -380,7 +380,7 @@ const zhCN = { articles: "生成数", published: "发布数", brands: "品牌数", - platforms: "媒体平台数", + mediaAccounts: "媒体账号数", }, sections: { generalTemplates: "普通通用模版", diff --git a/apps/admin-web/src/lib/publish-account-cards.ts b/apps/admin-web/src/lib/publish-account-cards.ts index d74eb81..10bf4e4 100644 --- a/apps/admin-web/src/lib/publish-account-cards.ts +++ b/apps/admin-web/src/lib/publish-account-cards.ts @@ -4,6 +4,7 @@ import { resolveApiURL } from "@/lib/api"; import { resolveAccountCheckedAt, resolveAccountHealth } from "@/lib/desktop-account-runtime"; import { getPublishPlatformMeta, + isMediaPublishAccount, isPublishPlatformId, normalizePublishPlatformId, } from "@/lib/publish-platforms"; @@ -65,7 +66,7 @@ export function buildPublishAccountCards( return accounts .filter((account) => { - if (account.deleted_at) { + if (!isMediaPublishAccount(account)) { return false; } const platformId = normalizePublishPlatformId(account.platform); diff --git a/apps/admin-web/src/lib/publish-platforms.ts b/apps/admin-web/src/lib/publish-platforms.ts index aed0179..52573e3 100644 --- a/apps/admin-web/src/lib/publish-platforms.ts +++ b/apps/admin-web/src/lib/publish-platforms.ts @@ -25,9 +25,19 @@ const platformCatalog: Record { diff --git a/apps/admin-web/src/views/MediaView.vue b/apps/admin-web/src/views/MediaView.vue index 4e1d4b8..60517ae 100644 --- a/apps/admin-web/src/views/MediaView.vue +++ b/apps/admin-web/src/views/MediaView.vue @@ -8,7 +8,7 @@ import { useI18n } from "vue-i18n"; import { mediaApi, resolveApiURL, tenantAccountsApi } from "@/lib/api"; import { resolveAccountCheckedAt, resolveAccountHealth } from "@/lib/desktop-account-runtime"; import { formatDateTime } from "@/lib/display"; -import { getPublishPlatformMeta, isPublishPlatformId, normalizePublishPlatformId } from "@/lib/publish-platforms"; +import { getPublishPlatformMeta, isMediaPublishAccount, normalizePublishPlatformId } from "@/lib/publish-platforms"; type AccountHealthFilter = "all" | "live" | "captcha" | "risk" | "expired"; type PublishState = "immediate" | "queued" | "unavailable"; @@ -79,8 +79,7 @@ const platformMap = computed(() => { const mediaAccounts = computed(() => { return [...(accountsQuery.data.value ?? [])] - .filter((account) => !account.deleted_at) - .filter((account) => isPublishPlatformId(account.platform)) + .filter(isMediaPublishAccount) .map((account) => { const platformId = normalizePublishPlatformId(account.platform); const platform = platformMap.value.get(platformId); diff --git a/apps/admin-web/src/views/WorkspaceView.vue b/apps/admin-web/src/views/WorkspaceView.vue index 398cc41..b4d4bb3 100644 --- a/apps/admin-web/src/views/WorkspaceView.vue +++ b/apps/admin-web/src/views/WorkspaceView.vue @@ -26,10 +26,11 @@ import ArticlePublishStatus from "@/components/ArticlePublishStatus.vue"; import ArticleSourceMeta from "@/components/ArticleSourceMeta.vue"; import ArticleDetailDrawer from "@/components/ArticleDetailDrawer.vue"; import PublishArticleModal from "@/components/PublishArticleModal.vue"; -import { articlesApi, workspaceApi } from "@/lib/api"; +import { articlesApi, tenantAccountsApi, workspaceApi } from "@/lib/api"; import { buildArticleClipboardContent, resolveArticleActionState } from "@/lib/article-list-actions"; import { formatDateTime, getTemplateMeta } from "@/lib/display"; import { formatError } from "@/lib/errors"; +import { isMediaPublishAccount } from "@/lib/publish-platforms"; const queryClient = useQueryClient(); const router = useRouter(); @@ -44,6 +45,10 @@ const overviewQuery = useQuery({ queryKey: ["workspace", "overview"], queryFn: () => workspaceApi.overview(), }); +const accountsQuery = useQuery({ + queryKey: ["tenant", "desktop-accounts", "workspace-overview"], + queryFn: () => tenantAccountsApi.list(), +}); const recentArticlesQuery = useQuery({ queryKey: ["workspace", "recent-articles"], queryFn: () => workspaceApi.recentArticles(), @@ -83,13 +88,17 @@ const statIcons = [ PlaySquareOutlined, ]; +const boundMediaAccountCount = computed(() => + (accountsQuery.data.value ?? []).filter(isMediaPublishAccount).length, +); + const statItems = computed(() => { const overview = overviewQuery.data.value; return [ { label: t("workspace.metrics.articles"), value: overview?.article_count ?? 0, icon: statIcons[0] }, { label: t("workspace.metrics.published"), value: overview?.published_count ?? 0, icon: statIcons[1] }, { label: t("workspace.metrics.brands"), value: overview?.brand_count ?? 0, icon: statIcons[2] }, - { label: t("workspace.metrics.platforms"), value: overview?.supported_platform_count ?? 0, icon: statIcons[3] }, + { label: t("workspace.metrics.mediaAccounts"), value: boundMediaAccountCount.value, icon: statIcons[3] }, ]; }); @@ -231,6 +240,7 @@ async function handleDelete(articleId: number): Promise { function refreshDashboard(): void { void Promise.all([ overviewQuery.refetch(), + accountsQuery.refetch(), recentArticlesQuery.refetch(), templateCardsQuery.refetch(), kolCardsQuery.refetch(), diff --git a/packages/shared-types/src/index.ts b/packages/shared-types/src/index.ts index 829911e..b4d7003 100644 --- a/packages/shared-types/src/index.ts +++ b/packages/shared-types/src/index.ts @@ -334,7 +334,6 @@ export interface WorkspaceOverview { article_count: number; published_count: number; brand_count: number; - supported_platform_count: number; } export interface RecentArticle { diff --git a/server/internal/tenant/app/workspace_service.go b/server/internal/tenant/app/workspace_service.go index 18022aa..3007f81 100644 --- a/server/internal/tenant/app/workspace_service.go +++ b/server/internal/tenant/app/workspace_service.go @@ -17,13 +17,12 @@ import ( ) type WorkspaceService struct { - repo repository.WorkspaceRepository - templates repository.TemplateRepository - quota repository.QuotaRepository - aiPointUsage repository.AIPointUsageRepository - supportedPlatformCount int - cache sharedcache.Cache - cacheGroup singleflight.Group + repo repository.WorkspaceRepository + templates repository.TemplateRepository + quota repository.QuotaRepository + aiPointUsage repository.AIPointUsageRepository + cache sharedcache.Cache + cacheGroup singleflight.Group } func NewWorkspaceService( @@ -33,11 +32,10 @@ func NewWorkspaceService( aiPointUsage repository.AIPointUsageRepository, ) *WorkspaceService { return &WorkspaceService{ - repo: repo, - templates: templates, - quota: quota, - aiPointUsage: aiPointUsage, - supportedPlatformCount: 5, + repo: repo, + templates: templates, + quota: quota, + aiPointUsage: aiPointUsage, } } @@ -61,12 +59,10 @@ func (s *WorkspaceService) Overview(ctx context.Context) (*domain.WorkspaceOverv if err != nil { return nil, response.ErrInternal(50010, "query_failed", "failed to count brands") } - return &domain.WorkspaceOverview{ - ArticleCount: articleCount, - PublishedCount: publishedCount, - BrandCount: brandCount, - SupportedPlatformCount: s.supportedPlatformCount, + ArticleCount: articleCount, + PublishedCount: publishedCount, + BrandCount: brandCount, }, nil }) } diff --git a/server/internal/tenant/domain/workspace.go b/server/internal/tenant/domain/workspace.go index d14e4dd..ccf9bfa 100644 --- a/server/internal/tenant/domain/workspace.go +++ b/server/internal/tenant/domain/workspace.go @@ -6,7 +6,6 @@ type WorkspaceOverview struct { ArticleCount int64 `json:"article_count"` PublishedCount int64 `json:"published_count"` BrandCount int64 `json:"brand_count"` - SupportedPlatformCount int `json:"supported_platform_count"` } type RecentArticle struct {