package app import ( "context" "crypto/sha1" "encoding/hex" "encoding/json" "fmt" "time" sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache" ) const ( defaultReadCacheTTL = 5 * time.Minute defaultReadCacheEmptyTTL = 1 * time.Minute ) func defaultCacheTTL() time.Duration { return defaultReadCacheTTL } func defaultCacheEmptyTTL() time.Duration { return defaultReadCacheEmptyTTL } func digestCacheKey(value interface{}) string { raw, err := json.Marshal(value) if err != nil { return "fallback" } sum := sha1.Sum(raw) return hex.EncodeToString(sum[:8]) } func deleteCacheKey(ctx context.Context, c sharedcache.Cache, key string) { if c == nil || key == "" { return } _ = c.Delete(ctx, key) } func deleteCachePrefix(ctx context.Context, c sharedcache.Cache, prefix string) { if c == nil || prefix == "" { return } _ = c.DeletePrefix(ctx, prefix) } func workspaceOverviewCacheKey(tenantID int64) string { return fmt.Sprintf("workspace:overview:%d", tenantID) } func workspaceRecentArticlesCacheKey(tenantID int64) string { return fmt.Sprintf("workspace:recent_articles:%d", tenantID) } func workspaceQuotaSummaryCacheKey(tenantID int64) string { return fmt.Sprintf("workspace:quota_summary:%d", tenantID) } func workspaceTemplateCardsCacheKey(tenantID int64) string { return fmt.Sprintf("workspace:template_cards:%d", tenantID) } func invalidateWorkspaceCaches(ctx context.Context, c sharedcache.Cache, tenantID int64) { deleteCacheKey(ctx, c, workspaceOverviewCacheKey(tenantID)) deleteCacheKey(ctx, c, workspaceRecentArticlesCacheKey(tenantID)) deleteCacheKey(ctx, c, workspaceQuotaSummaryCacheKey(tenantID)) deleteCacheKey(ctx, c, workspaceTemplateCardsCacheKey(tenantID)) } func brandListCacheKey(tenantID int64) string { return fmt.Sprintf("brand:list:%d", tenantID) } func brandLibrarySummaryCacheKey(tenantID int64) string { return fmt.Sprintf("brand:library_summary:%d", tenantID) } func brandDetailCacheKey(tenantID, brandID int64) string { return fmt.Sprintf("brand:detail:%d:%d", tenantID, brandID) } func brandKeywordsCachePrefix(tenantID, brandID int64) string { return fmt.Sprintf("brand:keywords:%d:%d:", tenantID, brandID) } func brandKeywordsCacheKey(tenantID, brandID int64) string { return brandKeywordsCachePrefix(tenantID, brandID) + "all" } func brandQuestionsCachePrefix(tenantID, brandID int64) string { return fmt.Sprintf("brand:questions:%d:%d:", tenantID, brandID) } func brandQuestionsCacheKey(tenantID, brandID int64, keywordID *int64) string { return fmt.Sprintf("%s%s", brandQuestionsCachePrefix(tenantID, brandID), digestCacheKey(struct { KeywordID *int64 `json:"keyword_id,omitempty"` }{ KeywordID: keywordID, })) } func brandCompetitorsCachePrefix(tenantID, brandID int64) string { return fmt.Sprintf("brand:competitors:%d:%d:", tenantID, brandID) } func brandCompetitorsCacheKey(tenantID, brandID int64) string { return brandCompetitorsCachePrefix(tenantID, brandID) + "all" } func scheduleTaskListCachePrefix(tenantID int64) string { return fmt.Sprintf("schedule_task:list:%d:", tenantID) } func scheduleTaskDetailCachePrefix(tenantID int64) string { return fmt.Sprintf("schedule_task:detail:%d:", tenantID) } func invalidateBrandCaches(ctx context.Context, c sharedcache.Cache, tenantID, brandID int64) { deleteCacheKey(ctx, c, brandDetailCacheKey(tenantID, brandID)) deleteCachePrefix(ctx, c, brandKeywordsCachePrefix(tenantID, brandID)) deleteCachePrefix(ctx, c, brandQuestionsCachePrefix(tenantID, brandID)) deleteCachePrefix(ctx, c, brandCompetitorsCachePrefix(tenantID, brandID)) deleteCacheKey(ctx, c, brandListCacheKey(tenantID)) deleteCacheKey(ctx, c, brandLibrarySummaryCacheKey(tenantID)) deleteCachePrefix(ctx, c, scheduleTaskListCachePrefix(tenantID)) deleteCachePrefix(ctx, c, scheduleTaskDetailCachePrefix(tenantID)) invalidateWorkspaceCaches(ctx, c, tenantID) } func promptRuleGroupsCacheKey(tenantID int64) string { return fmt.Sprintf("prompt_rule:groups:%d", tenantID) } func promptRuleSimpleCacheKey(tenantID int64) string { return fmt.Sprintf("prompt_rule:simple:%d", tenantID) } func promptRuleDetailCachePrefix(tenantID int64) string { return fmt.Sprintf("prompt_rule:detail:%d:", tenantID) } func promptRuleDetailCacheKey(tenantID, ruleID int64) string { return fmt.Sprintf("%s%d", promptRuleDetailCachePrefix(tenantID), ruleID) } func promptRuleListCachePrefix(tenantID int64) string { return fmt.Sprintf("prompt_rule:list:%d:", tenantID) } func promptRuleListCacheKey(tenantID int64, params interface{}) string { return promptRuleListCachePrefix(tenantID) + digestCacheKey(params) } func articleListCachePrefix(tenantID int64) string { return fmt.Sprintf("article:list:%d:", tenantID) } func invalidatePromptRuleCaches(ctx context.Context, c sharedcache.Cache, tenantID int64, ruleID *int64) { deleteCacheKey(ctx, c, promptRuleGroupsCacheKey(tenantID)) deleteCacheKey(ctx, c, promptRuleSimpleCacheKey(tenantID)) deleteCachePrefix(ctx, c, promptRuleListCachePrefix(tenantID)) if ruleID != nil && *ruleID > 0 { deleteCacheKey(ctx, c, promptRuleDetailCacheKey(tenantID, *ruleID)) } else { deleteCachePrefix(ctx, c, promptRuleDetailCachePrefix(tenantID)) } deleteCachePrefix(ctx, c, scheduleTaskListCachePrefix(tenantID)) deleteCachePrefix(ctx, c, scheduleTaskDetailCachePrefix(tenantID)) deleteCachePrefix(ctx, c, articleListCachePrefix(tenantID)) } func scheduleTaskDetailCacheKey(tenantID, taskID int64) string { return fmt.Sprintf("%s%d", scheduleTaskDetailCachePrefix(tenantID), taskID) } func scheduleTaskListCacheKey(tenantID int64, params interface{}) string { return scheduleTaskListCachePrefix(tenantID) + digestCacheKey(params) } func invalidateScheduleTaskCaches(ctx context.Context, c sharedcache.Cache, tenantID int64, taskID *int64) { deleteCachePrefix(ctx, c, scheduleTaskListCachePrefix(tenantID)) if taskID != nil && *taskID > 0 { deleteCacheKey(ctx, c, scheduleTaskDetailCacheKey(tenantID, *taskID)) } else { deleteCachePrefix(ctx, c, scheduleTaskDetailCachePrefix(tenantID)) } } func InvalidateScheduleTaskCaches(ctx context.Context, c sharedcache.Cache, tenantID int64, taskID *int64) { invalidateScheduleTaskCaches(ctx, c, tenantID, taskID) } func articleDetailCachePrefix(tenantID int64) string { return fmt.Sprintf("article:detail:%d:", tenantID) } func articleDetailCacheKey(tenantID, articleID int64) string { return fmt.Sprintf("%s%d", articleDetailCachePrefix(tenantID), articleID) } func articleListCacheKey(tenantID int64, params interface{}) string { return articleListCachePrefix(tenantID) + digestCacheKey(params) } func invalidateArticleCaches(ctx context.Context, c sharedcache.Cache, tenantID int64, articleID *int64) { deleteCachePrefix(ctx, c, articleListCachePrefix(tenantID)) if articleID != nil && *articleID > 0 { deleteCacheKey(ctx, c, articleDetailCacheKey(tenantID, *articleID)) } else { deleteCachePrefix(ctx, c, articleDetailCachePrefix(tenantID)) } invalidateWorkspaceCaches(ctx, c, tenantID) }