Enable auto default sync channels on agent activate, bind-code/phone confirm flows, publish ALTER, and align admin/yuheng docs with the production bind path.
86 lines
2.7 KiB
Go
86 lines
2.7 KiB
Go
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)
|
||
}
|
||
}
|