package meta import ( "context" "encoding/json" "fmt" "log" ) // BackfillImportResult Z9e 扫库补齐 import/export 的结果。 type BackfillImportResult struct { Scanned int `json:"scanned"` Updated int `json:"updated"` Skipped int `json:"skipped"` DryRun bool `json:"dry_run"` Slugs []string `json:"updated_slugs,omitempty"` Message string `json:"message,omitempty"` } // PublishedLister 列出全部已发布应用(跨租户,供 Z9e 启动扫库)。 type PublishedLister interface { ListPublishedApps(ctx context.Context) ([]*AppRecord, error) } // BackfillDefaultImportExport 遍历已发布蓝图,对可写 resource 补齐 import/export 并落库(Z9e)。 // dryRun=true 只统计不写库。tenantID>0 时仅处理该租户。 func BackfillDefaultImportExport(ctx context.Context, store Store, dryRun bool) (*BackfillImportResult, error) { return BackfillDefaultImportExportForTenant(ctx, store, 0, dryRun) } // BackfillDefaultImportExportForTenant 同 BackfillDefaultImportExport;tenantID=0 表示全库。 func BackfillDefaultImportExportForTenant(ctx context.Context, store Store, tenantID int64, dryRun bool) (*BackfillImportResult, error) { if store == nil { return nil, fmt.Errorf("meta store nil") } lister, ok := store.(PublishedLister) if !ok { return nil, fmt.Errorf("meta store does not support ListPublishedApps") } apps, err := lister.ListPublishedApps(ctx) if err != nil { return nil, err } out := &BackfillImportResult{DryRun: dryRun, Slugs: make([]string, 0)} for _, app := range apps { if app == nil || app.Blueprint == nil { continue } if tenantID > 0 && app.TenantID != tenantID { continue } out.Scanned++ before, _ := json.Marshal(app.Blueprint) app.Blueprint.EnsureDefaultImportExport() after, _ := json.Marshal(app.Blueprint) if string(before) == string(after) { out.Skipped++ continue } out.Updated++ out.Slugs = append(out.Slugs, fmt.Sprintf("%d/%s", app.TenantID, app.Slug)) if dryRun { continue } if err := store.Save(ctx, app); err != nil { return out, fmt.Errorf("save %s: %w", app.Slug, err) } } out.Message = fmt.Sprintf("scanned=%d updated=%d skipped=%d dry_run=%v", out.Scanned, out.Updated, out.Skipped, dryRun) return out, nil } // RunBackfillDefaultImportExportOnBoot 启动时扫库(失败只打日志,不阻断启动)。 func RunBackfillDefaultImportExportOnBoot(ctx context.Context, store Store) { res, err := BackfillDefaultImportExport(ctx, store, false) if err != nil { log.Printf("Z9e backfill import ops: %v", err) return } if res != nil && res.Updated > 0 { log.Printf("Z9e backfill import ops: %s slugs=%v", res.Message, res.Slugs) } else if res != nil { log.Printf("Z9e backfill import ops: %s", res.Message) } }