diff --git a/server/internal/ops/app/kol_subscription.go b/server/internal/ops/app/kol_subscription.go index b5a3eef..7174b02 100644 --- a/server/internal/ops/app/kol_subscription.go +++ b/server/internal/ops/app/kol_subscription.go @@ -241,6 +241,7 @@ func (s *KolSubscriptionService) invalidateAccessCaches(ctx context.Context, ten _ = s.cache.Delete(ctx, fmt.Sprintf("workspace:kol_cards:%d", tenantID)) _ = s.cache.DeletePrefix(ctx, fmt.Sprintf("kol:subscription_prompt:list:%d:", tenantID)) _ = s.cache.DeletePrefix(ctx, fmt.Sprintf("kol:subscription_prompt:schema:%d:", tenantID)) + _ = s.cache.DeletePrefix(ctx, "kol:marketplace:") } func toKolSubscriptionView(item repository.KolSubscriptionRecord) KolSubscriptionView { diff --git a/server/internal/ops/app/kol_subscription_cache_test.go b/server/internal/ops/app/kol_subscription_cache_test.go index e9e910f..0496e23 100644 --- a/server/internal/ops/app/kol_subscription_cache_test.go +++ b/server/internal/ops/app/kol_subscription_cache_test.go @@ -58,6 +58,7 @@ func TestKolSubscriptionInvalidateAccessCaches(t *testing.T) { wantPrefixes := []string{ "kol:subscription_prompt:list:42:", "kol:subscription_prompt:schema:42:", + "kol:marketplace:", } if !reflect.DeepEqual(cache.deletedPrefixes, wantPrefixes) { t.Fatalf("deleted prefixes = %v, want %v", cache.deletedPrefixes, wantPrefixes) diff --git a/server/internal/tenant/app/cache_support.go b/server/internal/tenant/app/cache_support.go index f6440f1..75acda6 100644 --- a/server/internal/tenant/app/cache_support.go +++ b/server/internal/tenant/app/cache_support.go @@ -285,14 +285,49 @@ func kolPromptListCachePrefix(tenantID int64) string { return fmt.Sprintf("kol:prompt:list:%d:", tenantID) } +func kolMarketplaceCachePrefix() string { + return "kol:marketplace:" +} + +func kolMarketplacePackageListCachePrefix() string { + return kolMarketplaceCachePrefix() + "packages:list:" +} + +func kolMarketplacePackageListCacheKey(viewerTenantID int64, params interface{}) string { + return fmt.Sprintf("%s%d:%s", kolMarketplacePackageListCachePrefix(), viewerTenantID, digestCacheKey(params)) +} + +func kolMarketplacePackageDetailCachePrefix() string { + return kolMarketplaceCachePrefix() + "packages:detail:" +} + +func kolMarketplacePackageDetailCacheKey(viewerTenantID, packageID int64) string { + return fmt.Sprintf("%s%d:%d", kolMarketplacePackageDetailCachePrefix(), viewerTenantID, packageID) +} + func kolSubscriptionPromptListCachePrefix(tenantID int64) string { return fmt.Sprintf("kol:subscription_prompt:list:%d:", tenantID) } +func kolSubscriptionPromptListCacheKey(tenantID int64) string { + return kolSubscriptionPromptListCachePrefix(tenantID) + "all" +} + func kolSubscriptionPromptSchemaCachePrefix(tenantID int64) string { return fmt.Sprintf("kol:subscription_prompt:schema:%d:", tenantID) } +func kolSubscriptionPromptSchemaCacheKey(tenantID, subscriptionPromptID int64) string { + return fmt.Sprintf("%s%d", kolSubscriptionPromptSchemaCachePrefix(tenantID), subscriptionPromptID) +} + +func InvalidateKolPublicCaches(ctx context.Context, c sharedcache.Cache) { + deleteCachePrefix(ctx, c, kolMarketplaceCachePrefix()) + deleteCachePrefix(ctx, c, "kol:subscription_prompt:list:") + deleteCachePrefix(ctx, c, "kol:subscription_prompt:schema:") + deleteCachePrefix(ctx, c, "workspace:kol_cards:") +} + func invalidateKolPromptCaches(ctx context.Context, c sharedcache.Cache, tenantID int64) { deleteCachePrefix(ctx, c, kolPromptDetailCachePrefix(tenantID)) deleteCachePrefix(ctx, c, fmt.Sprintf("kol:prompt:content:%d:", tenantID)) @@ -306,7 +341,5 @@ func InvalidateKolPromptCaches(ctx context.Context, c sharedcache.Cache) { deleteCachePrefix(ctx, c, "kol:prompt:detail:") deleteCachePrefix(ctx, c, "kol:prompt:content:") deleteCachePrefix(ctx, c, "kol:prompt:list:") - deleteCachePrefix(ctx, c, "kol:subscription_prompt:list:") - deleteCachePrefix(ctx, c, "kol:subscription_prompt:schema:") - deleteCachePrefix(ctx, c, "workspace:kol_cards:") + InvalidateKolPublicCaches(ctx, c) } diff --git a/server/internal/tenant/app/kol_generation_service.go b/server/internal/tenant/app/kol_generation_service.go index ba658bb..44feb3e 100644 --- a/server/internal/tenant/app/kol_generation_service.go +++ b/server/internal/tenant/app/kol_generation_service.go @@ -129,30 +129,32 @@ func (s *KolGenerationService) WithCache(c sharedcache.Cache) *KolGenerationServ } func (s *KolGenerationService) GetSchema(ctx context.Context, actor auth.Actor, subPromptID int64) (*KolSubscriptionPromptSchemaResponse, error) { - row, prompt, err := s.loadAuthorizedPrompt(ctx, actor, subPromptID) - if err != nil { - return nil, err - } + return sharedcache.LoadJSON(ctx, s.cache, &s.cacheGroup, kolSubscriptionPromptSchemaCacheKey(actor.TenantID, subPromptID), defaultCacheTTL(), func(loadCtx context.Context) (*KolSubscriptionPromptSchemaResponse, error) { + row, prompt, err := s.loadAuthorizedPrompt(loadCtx, actor, subPromptID) + if err != nil { + return nil, err + } - schema, err := decodeKolSchema(prompt.SchemaJSON) - if err != nil { - return nil, response.ErrInternal(50096, "kol_generation_schema_decode_failed", err.Error()) - } - cardConfig, err := decodeKolCardConfig(prompt.CardConfigJSON) - if err != nil { - return nil, response.ErrInternal(50097, "kol_generation_card_config_decode_failed", err.Error()) - } + schema, err := decodeKolSchema(prompt.SchemaJSON) + if err != nil { + return nil, response.ErrInternal(50096, "kol_generation_schema_decode_failed", err.Error()) + } + cardConfig, err := decodeKolCardConfig(prompt.CardConfigJSON) + if err != nil { + return nil, response.ErrInternal(50097, "kol_generation_card_config_decode_failed", err.Error()) + } - return &KolSubscriptionPromptSchemaResponse{ - SubscriptionPromptID: row.ID, - PackageID: row.PackageID, - PackageName: row.PackageName, - PromptID: row.PromptID, - PromptName: row.PromptName, - PlatformHint: row.PlatformHint, - SchemaJSON: schema, - CardConfigJSON: cardConfig, - }, nil + return &KolSubscriptionPromptSchemaResponse{ + SubscriptionPromptID: row.ID, + PackageID: row.PackageID, + PackageName: row.PackageName, + PromptID: row.PromptID, + PromptName: row.PromptName, + PlatformHint: row.PlatformHint, + SchemaJSON: schema, + CardConfigJSON: cardConfig, + }, nil + }) } func (s *KolGenerationService) Submit(ctx context.Context, actor auth.Actor, subPromptID int64, req KolGenerationSubmitRequest) (*KolGenerationSubmitResponse, error) { diff --git a/server/internal/tenant/app/kol_marketplace_cache_test.go b/server/internal/tenant/app/kol_marketplace_cache_test.go new file mode 100644 index 0000000..4e06ec2 --- /dev/null +++ b/server/internal/tenant/app/kol_marketplace_cache_test.go @@ -0,0 +1,83 @@ +package app + +import ( + "context" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/geo-platform/tenant-api/internal/shared/auth" + sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache" + "github.com/geo-platform/tenant-api/internal/tenant/repository" +) + +type stubKolMarketplaceRepository struct { + listCalls atomic.Int64 +} + +func (r *stubKolMarketplaceRepository) ListPublished(ctx context.Context, filter repository.ListPublishedFilter) ([]repository.PublishedKolPackage, error) { + r.listCalls.Add(1) + time.Sleep(25 * time.Millisecond) + return []repository.PublishedKolPackage{ + { + ID: 7, + TenantID: 11, + KolProfileID: 13, + Name: "pkg", + Tags: []byte("[]"), + Status: "published", + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + KolDisplayName: "KOL", + }, + }, nil +} + +func (r *stubKolMarketplaceRepository) GetPublished(ctx context.Context, id int64) (*repository.PublishedKolPackage, error) { + return nil, nil +} + +func (r *stubKolMarketplaceRepository) ListPublicPrompts(ctx context.Context, packageID int64) ([]repository.PublicKolPrompt, error) { + return nil, nil +} + +func TestKolMarketplaceListUsesCacheAsideAndSingleflight(t *testing.T) { + t.Parallel() + + ctx := context.Background() + cache := sharedcache.New("memory", nil, sharedcache.WithNamespace("test"), sharedcache.WithMetrics(false), sharedcache.WithJitterRatio(0)) + repo := &stubKolMarketplaceRepository{} + svc := NewKolMarketplaceService(repo, nil).WithCache(cache) + actor := auth.Actor{TenantID: 42} + filter := MarketFilter{Limit: 20} + + const callers = 8 + var wg sync.WaitGroup + errs := make(chan error, callers) + for i := 0; i < callers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + _, err := svc.ListPackages(ctx, actor, filter) + errs <- err + }() + } + wg.Wait() + close(errs) + for err := range errs { + if err != nil { + t.Fatalf("ListPackages() error = %v", err) + } + } + if calls := repo.listCalls.Load(); calls != 1 { + t.Fatalf("ListPublished calls after concurrent cold-cache load = %d, want 1", calls) + } + + if _, err := svc.ListPackages(ctx, actor, filter); err != nil { + t.Fatalf("ListPackages() cached error = %v", err) + } + if calls := repo.listCalls.Load(); calls != 1 { + t.Fatalf("ListPublished calls after cache hit = %d, want 1", calls) + } +} diff --git a/server/internal/tenant/app/kol_marketplace_service.go b/server/internal/tenant/app/kol_marketplace_service.go index ca31b53..3204862 100644 --- a/server/internal/tenant/app/kol_marketplace_service.go +++ b/server/internal/tenant/app/kol_marketplace_service.go @@ -5,8 +5,10 @@ import ( "time" "github.com/geo-platform/tenant-api/internal/shared/auth" + sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache" "github.com/geo-platform/tenant-api/internal/shared/response" "github.com/geo-platform/tenant-api/internal/tenant/repository" + "golang.org/x/sync/singleflight" ) var ( @@ -82,6 +84,8 @@ type KolSubscriptionPromptCardResponse struct { type KolMarketplaceService struct { marketplaceRepo repository.KolMarketplaceRepository subRepo repository.KolSubscriptionRepository + cache sharedcache.Cache + cacheGroup singleflight.Group } func NewKolMarketplaceService( @@ -94,71 +98,87 @@ func NewKolMarketplaceService( } } +func (s *KolMarketplaceService) WithCache(c sharedcache.Cache) *KolMarketplaceService { + s.cache = c + return s +} + func (s *KolMarketplaceService) ListPackages(ctx context.Context, actor auth.Actor, filter MarketFilter) ([]KolMarketplacePackageResponse, error) { filter = normalizeMarketFilter(filter) - packages, err := s.marketplaceRepo.ListPublished(ctx, repository.ListPublishedFilter{ - Industry: filter.Industry, - Keyword: filter.Keyword, - Offset: filter.Offset, - Limit: filter.Limit, - }) - if err != nil { - return nil, response.ErrInternal(50090, "kol_marketplace_query_failed", err.Error()) - } - - result := make([]KolMarketplacePackageResponse, 0, len(packages)) - for _, pkg := range packages { - item, err := newKolMarketplacePackageResponse(pkg, actor.TenantID) + return sharedcache.LoadJSON(ctx, s.cache, &s.cacheGroup, kolMarketplacePackageListCacheKey(actor.TenantID, filter), defaultCacheTTL(), func(loadCtx context.Context) ([]KolMarketplacePackageResponse, error) { + packages, err := s.marketplaceRepo.ListPublished(loadCtx, repository.ListPublishedFilter{ + Industry: filter.Industry, + Keyword: filter.Keyword, + Offset: filter.Offset, + Limit: filter.Limit, + }) if err != nil { - return nil, response.ErrInternal(50091, "kol_marketplace_decode_failed", err.Error()) + return nil, response.ErrInternal(50090, "kol_marketplace_query_failed", err.Error()) } - result = append(result, *item) - } - return result, nil + + result := make([]KolMarketplacePackageResponse, 0, len(packages)) + for _, pkg := range packages { + item, err := newKolMarketplacePackageResponse(pkg, actor.TenantID) + if err != nil { + return nil, response.ErrInternal(50091, "kol_marketplace_decode_failed", err.Error()) + } + result = append(result, *item) + } + return result, nil + }) } func (s *KolMarketplaceService) GetPackage(ctx context.Context, actor auth.Actor, id int64) (*KolMarketplacePackageDetailResponse, error) { - pkg, err := s.marketplaceRepo.GetPublished(ctx, id) + record, found, err := sharedcache.LoadJSONWithEmpty(ctx, s.cache, &s.cacheGroup, kolMarketplacePackageDetailCacheKey(actor.TenantID, id), defaultCacheTTL(), defaultCacheEmptyTTL(), func(loadCtx context.Context) (*KolMarketplacePackageDetailResponse, bool, error) { + pkg, err := s.marketplaceRepo.GetPublished(loadCtx, id) + if err != nil { + return nil, false, response.ErrInternal(50090, "kol_marketplace_query_failed", err.Error()) + } + if pkg == nil { + return nil, false, nil + } + + prompts, err := s.marketplaceRepo.ListPublicPrompts(loadCtx, id) + if err != nil { + return nil, false, response.ErrInternal(50092, "kol_marketplace_prompt_query_failed", err.Error()) + } + sub, err := s.subRepo.GetActiveForTenant(loadCtx, actor.TenantID, id) + if err != nil { + return nil, false, response.ErrInternal(50093, "kol_subscription_query_failed", err.Error()) + } + + pkgResp, err := newKolMarketplacePackageResponse(*pkg, actor.TenantID) + if err != nil { + return nil, false, response.ErrInternal(50091, "kol_marketplace_decode_failed", err.Error()) + } + + resp := &KolMarketplacePackageDetailResponse{ + KolMarketplacePackageResponse: *pkgResp, + KolBio: pkg.KolBio, + Prompts: make([]KolMarketplacePromptResponse, 0, len(prompts)), + Subscription: newKolSubscriptionResponse(sub), + } + for _, prompt := range prompts { + resp.Prompts = append(resp.Prompts, KolMarketplacePromptResponse{ + ID: prompt.ID, + Name: prompt.Name, + PlatformHint: prompt.PlatformHint, + Status: prompt.Status, + PublishedRevisionNo: prompt.PublishedRevisionNo, + CreatedAt: prompt.CreatedAt.Format(time.RFC3339), + }) + } + + return resp, true, nil + }) if err != nil { - return nil, response.ErrInternal(50090, "kol_marketplace_query_failed", err.Error()) + return nil, err } - if pkg == nil { + if !found || record == nil { return nil, errKolMarketplacePackageNotFound } - - prompts, err := s.marketplaceRepo.ListPublicPrompts(ctx, id) - if err != nil { - return nil, response.ErrInternal(50092, "kol_marketplace_prompt_query_failed", err.Error()) - } - sub, err := s.subRepo.GetActiveForTenant(ctx, actor.TenantID, id) - if err != nil { - return nil, response.ErrInternal(50093, "kol_subscription_query_failed", err.Error()) - } - - pkgResp, err := newKolMarketplacePackageResponse(*pkg, actor.TenantID) - if err != nil { - return nil, response.ErrInternal(50091, "kol_marketplace_decode_failed", err.Error()) - } - - resp := &KolMarketplacePackageDetailResponse{ - KolMarketplacePackageResponse: *pkgResp, - KolBio: pkg.KolBio, - Prompts: make([]KolMarketplacePromptResponse, 0, len(prompts)), - Subscription: newKolSubscriptionResponse(sub), - } - for _, prompt := range prompts { - resp.Prompts = append(resp.Prompts, KolMarketplacePromptResponse{ - ID: prompt.ID, - Name: prompt.Name, - PlatformHint: prompt.PlatformHint, - Status: prompt.Status, - PublishedRevisionNo: prompt.PublishedRevisionNo, - CreatedAt: prompt.CreatedAt.Format(time.RFC3339), - }) - } - - return resp, nil + return record, nil } func (s *KolMarketplaceService) Subscribe(ctx context.Context, actor auth.Actor, packageID int64) (*KolSubscriptionResponse, error) { @@ -188,6 +208,7 @@ func (s *KolMarketplaceService) Subscribe(ctx context.Context, actor auth.Actor, } return nil, response.ErrInternal(50094, "kol_subscription_create_failed", err.Error()) } + InvalidateKolPublicCaches(ctx, s.cache) return newKolSubscriptionResponse(sub), nil } @@ -207,29 +228,31 @@ func (s *KolMarketplaceService) ListMySubscriptions(ctx context.Context, actor a } func (s *KolMarketplaceService) ListMySubscriptionPrompts(ctx context.Context, actor auth.Actor) ([]KolSubscriptionPromptCardResponse, error) { - rows, err := s.subRepo.ListSubscriptionPromptsByTenant(ctx, actor.TenantID) - if err != nil { - return nil, response.ErrInternal(50095, "kol_subscription_prompt_query_failed", err.Error()) - } - - result := make([]KolSubscriptionPromptCardResponse, 0, len(rows)) - for _, row := range rows { - grantedAt := "" - if row.GrantedAt != nil { - grantedAt = row.GrantedAt.Format(time.RFC3339) + return sharedcache.LoadJSON(ctx, s.cache, &s.cacheGroup, kolSubscriptionPromptListCacheKey(actor.TenantID), defaultCacheTTL(), func(loadCtx context.Context) ([]KolSubscriptionPromptCardResponse, error) { + rows, err := s.subRepo.ListSubscriptionPromptsByTenant(loadCtx, actor.TenantID) + if err != nil { + return nil, response.ErrInternal(50095, "kol_subscription_prompt_query_failed", err.Error()) } - result = append(result, KolSubscriptionPromptCardResponse{ - ID: row.ID, - PackageID: row.PackageID, - PackageName: row.PackageName, - PromptID: row.PromptID, - PromptName: row.PromptName, - PlatformHint: row.PlatformHint, - KolDisplayName: row.KolDisplayName, - GrantedAt: grantedAt, - }) - } - return result, nil + + result := make([]KolSubscriptionPromptCardResponse, 0, len(rows)) + for _, row := range rows { + grantedAt := "" + if row.GrantedAt != nil { + grantedAt = row.GrantedAt.Format(time.RFC3339) + } + result = append(result, KolSubscriptionPromptCardResponse{ + ID: row.ID, + PackageID: row.PackageID, + PackageName: row.PackageName, + PromptID: row.PromptID, + PromptName: row.PromptName, + PlatformHint: row.PlatformHint, + KolDisplayName: row.KolDisplayName, + GrantedAt: grantedAt, + }) + } + return result, nil + }) } func newKolMarketplacePackageResponse(pkg repository.PublishedKolPackage, viewerTenantID int64) (*KolMarketplacePackageResponse, error) { diff --git a/server/internal/tenant/app/kol_profile_cache_test.go b/server/internal/tenant/app/kol_profile_cache_test.go new file mode 100644 index 0000000..d22d000 --- /dev/null +++ b/server/internal/tenant/app/kol_profile_cache_test.go @@ -0,0 +1,86 @@ +package app + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/geo-platform/tenant-api/internal/shared/auth" + sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache" + "github.com/geo-platform/tenant-api/internal/tenant/repository" +) + +type stubKolProfileRepository struct { + profile repository.KolProfile +} + +func (r *stubKolProfileRepository) GetByUser(ctx context.Context, tenantID, userID int64) (*repository.KolProfile, error) { + if r.profile.TenantID != tenantID || r.profile.UserID != userID { + return nil, nil + } + profile := r.profile + return &profile, nil +} + +func (r *stubKolProfileRepository) GetByID(ctx context.Context, id int64) (*repository.KolProfile, error) { + if r.profile.ID != id { + return nil, nil + } + profile := r.profile + return &profile, nil +} + +func (r *stubKolProfileRepository) Update(ctx context.Context, tenantID int64, input repository.UpdateKolProfileInput) error { + if r.profile.TenantID != tenantID || r.profile.ID != input.ID { + return errors.New("unexpected profile update target") + } + r.profile.DisplayName = input.DisplayName + r.profile.Bio = input.Bio + r.profile.AvatarURL = input.AvatarURL + r.profile.UpdatedAt = time.Now() + return nil +} + +func TestUpdateKolProfileInvalidatesPublicCaches(t *testing.T) { + t.Parallel() + + ctx := context.Background() + cache := sharedcache.New("memory", nil, sharedcache.WithNamespace("test"), sharedcache.WithMetrics(false), sharedcache.WithJitterRatio(0)) + tenantID := int64(42) + + keys := []string{ + workspaceKolCardsCacheKey(tenantID), + kolMarketplacePackageListCacheKey(tenantID, MarketFilter{Limit: 20}), + kolMarketplacePackageDetailCacheKey(tenantID, 7), + kolSubscriptionPromptListCacheKey(tenantID), + kolSubscriptionPromptSchemaCacheKey(tenantID, 9), + } + for _, key := range keys { + if err := cache.Set(ctx, key, []byte(`{"value":"stale"}`), time.Minute); err != nil { + t.Fatalf("seed cache %s: %v", key, err) + } + } + + repo := &stubKolProfileRepository{profile: repository.KolProfile{ + ID: 3, + TenantID: tenantID, + UserID: 5, + DisplayName: "old name", + MarketEnabled: true, + Status: "active", + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }} + svc := NewKolProfileService(repo).WithCache(cache) + + if _, err := svc.UpdateProfile(ctx, auth.Actor{TenantID: tenantID, UserID: 5}, UpdateKolProfileServiceInput{DisplayName: "new name"}); err != nil { + t.Fatalf("UpdateProfile() error = %v", err) + } + + for _, key := range keys { + if _, err := cache.Get(ctx, key); !errors.Is(err, sharedcache.ErrNotFound) { + t.Fatalf("cache key %s should be invalidated, got err %v", key, err) + } + } +} diff --git a/server/internal/tenant/app/kol_profile_service.go b/server/internal/tenant/app/kol_profile_service.go index 911cdef..5f62cf9 100644 --- a/server/internal/tenant/app/kol_profile_service.go +++ b/server/internal/tenant/app/kol_profile_service.go @@ -5,6 +5,7 @@ import ( "time" "github.com/geo-platform/tenant-api/internal/shared/auth" + sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache" "github.com/geo-platform/tenant-api/internal/shared/objectstorage" "github.com/geo-platform/tenant-api/internal/shared/response" "github.com/geo-platform/tenant-api/internal/tenant/repository" @@ -15,6 +16,7 @@ var ErrNotActiveKol = response.ErrForbidden(40341, "kol_not_active", "user is no type KolProfileService struct { repo repository.KolProfileRepository objectStorage objectstorage.Client + cache sharedcache.Cache } type UpdateKolProfileServiceInput struct { @@ -45,6 +47,11 @@ func (s *KolProfileService) WithObjectStorage(client objectstorage.Client) *KolP return s } +func (s *KolProfileService) WithCache(c sharedcache.Cache) *KolProfileService { + s.cache = c + return s +} + func (s *KolProfileService) RequireActiveKol(ctx context.Context, actor auth.Actor) (*repository.KolProfile, error) { profile, err := s.repo.GetByUser(ctx, actor.TenantID, actor.UserID) if err != nil { @@ -91,6 +98,7 @@ func (s *KolProfileService) UpdateProfile(ctx context.Context, actor auth.Actor, if updated == nil { return nil, ErrNotActiveKol } + InvalidateKolPublicCaches(ctx, s.cache) return updated, nil } diff --git a/server/internal/tenant/transport/kol_manage_handler.go b/server/internal/tenant/transport/kol_manage_handler.go index b1b33bf..8f62bc2 100644 --- a/server/internal/tenant/transport/kol_manage_handler.go +++ b/server/internal/tenant/transport/kol_manage_handler.go @@ -36,7 +36,7 @@ type kolAssistStreamEvent struct { } func NewKolManageHandler(a *bootstrap.App, assets *AssetHandler) *KolManageHandler { - profileSvc := app.NewKolProfileService(a.KolProfiles).WithObjectStorage(a.ObjectStorage) + profileSvc := app.NewKolProfileService(a.KolProfiles).WithObjectStorage(a.ObjectStorage).WithCache(a.Cache) return &KolManageHandler{ profileSvc: profileSvc, diff --git a/server/internal/tenant/transport/kol_marketplace_handler.go b/server/internal/tenant/transport/kol_marketplace_handler.go index 08a306a..e9f17f5 100644 --- a/server/internal/tenant/transport/kol_marketplace_handler.go +++ b/server/internal/tenant/transport/kol_marketplace_handler.go @@ -18,7 +18,7 @@ type KolMarketplaceHandler struct { func NewKolMarketplaceHandler(a *bootstrap.App) *KolMarketplaceHandler { return &KolMarketplaceHandler{ - svc: app.NewKolMarketplaceService(a.KolMarketplace, a.KolSubscriptions), + svc: app.NewKolMarketplaceService(a.KolMarketplace, a.KolSubscriptions).WithCache(a.Cache), genSvc: app.NewKolGenerationService( a.DB, a.KolSubscriptions,