From 4dd8a1bb0e0e80fa2b6a4ace3af100df6cc3e604 Mon Sep 17 00:00:00 2001 From: liangxu Date: Wed, 29 Apr 2026 22:06:57 +0800 Subject: [PATCH] feat(tenant/kol): let KOLs self-subscribe to their own published packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /kol/manage/packages/:id/self-subscription POST/DELETE on the tenant side so a KOL can grant themselves access to their own published package without going through the marketplace approval flow. Reject self-subscribe through the marketplace with a clear error, expose owned_by_current_tenant on package responses, and surface a self_subscription block on KolPackage Response. The admin-web KOL workspace gets 订阅自己 / 取消订阅 entries with matching messaging in the marketplace detail view. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/admin-web/src/lib/api.ts | 11 + apps/admin-web/src/views/KolManageView.vue | 52 +++++ .../src/views/KolPackageDetailView.vue | 22 +- packages/shared-types/src/index.ts | 2 + .../tenant/app/kol_marketplace_service.go | 72 +++--- .../tenant/app/kol_package_service.go | 118 +++++++--- .../repository/kol_subscription_repo.go | 220 +++++++++++++++++- .../tenant/transport/kol_manage_handler.go | 42 +++- server/internal/tenant/transport/router.go | 10 +- 9 files changed, 472 insertions(+), 77 deletions(-) diff --git a/apps/admin-web/src/lib/api.ts b/apps/admin-web/src/lib/api.ts index 42edfd6..4558676 100644 --- a/apps/admin-web/src/lib/api.ts +++ b/apps/admin-web/src/lib/api.ts @@ -449,6 +449,17 @@ export const kolManageApi = { {}, ); }, + selfSubscribePackage(id: number) { + return apiClient.post>( + `/api/tenant/kol/manage/packages/${id}/self-subscription`, + {}, + ); + }, + cancelSelfSubscription(id: number) { + return apiClient.remove( + `/api/tenant/kol/manage/packages/${id}/self-subscription`, + ); + }, deletePackage(id: number) { return apiClient.remove(`/api/tenant/kol/manage/packages/${id}`); }, diff --git a/apps/admin-web/src/views/KolManageView.vue b/apps/admin-web/src/views/KolManageView.vue index 2646698..5966a57 100644 --- a/apps/admin-web/src/views/KolManageView.vue +++ b/apps/admin-web/src/views/KolManageView.vue @@ -11,6 +11,8 @@ import { CloudUploadOutlined, InboxOutlined, DeleteOutlined, + CheckCircleOutlined, + StopOutlined, } from "@ant-design/icons-vue"; import KolPackageFormModal from "@/components/kol/KolPackageFormModal.vue"; @@ -79,6 +81,24 @@ const archivePackageMutation = useMutation({ onError: (error) => message.error(formatError(error)), }); +const selfSubscribePackageMutation = useMutation({ + mutationFn: (id: number) => kolManageApi.selfSubscribePackage(id), + onSuccess: () => { + message.success("已订阅自己的订阅包"); + invalidateKolPackageAccess(); + }, + onError: (error) => message.error(formatError(error)), +}); + +const cancelSelfSubscriptionMutation = useMutation({ + mutationFn: (id: number) => kolManageApi.cancelSelfSubscription(id), + onSuccess: () => { + message.success("已取消订阅自己的订阅包"); + invalidateKolPackageAccess(); + }, + onError: (error) => message.error(formatError(error)), +}); + const deletePackageMutation = useMutation({ mutationFn: (id: number) => kolManageApi.deletePackage(id), onSuccess: (_, id) => { @@ -150,6 +170,12 @@ const deletePromptMutation = useMutation({ }, }); +function invalidateKolPackageAccess() { + void queryClient.invalidateQueries({ queryKey: ["kol", "packages"] }); + void queryClient.invalidateQueries({ queryKey: ["kol", "my-subscription-prompts"] }); + void queryClient.invalidateQueries({ queryKey: ["workspace"] }); +} + const packageInitialValue = computed | undefined>(() => { if (!editingPackage.value) return undefined; return { @@ -220,6 +246,18 @@ function packageStatusLabel(status: string) { if (status === "archived") return t("kol.manage.packageStatus.archived"); return t("kol.manage.packageStatus.draft"); } + +function isSelfSubscribed(pkg: KolPackageSummary) { + return pkg.self_subscription?.status === "active"; +} + +function canSelfSubscribe(pkg: KolPackageSummary) { + return pkg.status === "published" && !isSelfSubscribed(pkg); +} + +function canCancelSelfSubscription(pkg: KolPackageSummary) { + return pkg.self_subscription?.status === "active"; +}