package main import ( "context" "fmt" "log" "os" "github.com/jackc/pgx/v5/pgxpool" "golang.org/x/crypto/bcrypt" "github.com/geo-platform/tenant-api/internal/shared/config" ) func main() { configPath := "configs/config.yaml" if p := os.Getenv("CONFIG_PATH"); p != "" { configPath = p } cfg, err := config.Load(configPath) if err != nil { log.Fatalf("load config: %v", err) } ctx := context.Background() pool, err := pgxpool.New(ctx, cfg.Database.DSN()) if err != nil { log.Fatalf("connect db: %v", err) } defer pool.Close() hash, err := bcrypt.GenerateFromPassword([]byte("Admin@123"), bcrypt.DefaultCost) if err != nil { log.Fatalf("hash password: %v", err) } tx, err := pool.Begin(ctx) if err != nil { log.Fatalf("begin tx: %v", err) } defer func() { _ = tx.Rollback(ctx) }() // Tenant var tenantID int64 err = tx.QueryRow(ctx, ` INSERT INTO tenants (name, status) VALUES ('GEO Demo', 'active') ON CONFLICT DO NOTHING RETURNING id `).Scan(&tenantID) if err != nil { log.Fatalf("insert tenant: %v", err) } // User var userID int64 err = tx.QueryRow(ctx, ` INSERT INTO users (email, password_hash, name, status) VALUES ('admin@geo.local', $1, 'Admin', 'active') ON CONFLICT DO NOTHING RETURNING id `, string(hash)).Scan(&userID) if err != nil { log.Fatalf("insert user: %v", err) } // Membership _, err = tx.Exec(ctx, ` INSERT INTO tenant_memberships (tenant_id, user_id, tenant_role) VALUES ($1, $2, 'tenant_admin') ON CONFLICT DO NOTHING `, tenantID, userID) if err != nil { log.Fatalf("insert membership: %v", err) } // Plan var planID int64 err = tx.QueryRow(ctx, ` INSERT INTO plans (plan_code, name, quota_policy_json, status) VALUES ('free', 'Free Plan', '{"article_generation": 100}', 'active') ON CONFLICT DO NOTHING RETURNING id `).Scan(&planID) if err != nil { log.Fatalf("insert plan: %v", err) } // Subscription _, err = tx.Exec(ctx, ` INSERT INTO tenant_plan_subscriptions (tenant_id, plan_id, start_at, end_at, status) VALUES ($1, $2, NOW(), NOW() + INTERVAL '1 year', 'active') ON CONFLICT DO NOTHING `, tenantID, planID) if err != nil { log.Fatalf("insert subscription: %v", err) } // Quota ledger _, err = tx.Exec(ctx, ` INSERT INTO tenant_quota_ledgers (tenant_id, quota_type, delta, balance_after, reason) VALUES ($1, 'article_generation', 100, 100, 'initial_allocation') `, tenantID) if err != nil { log.Fatalf("insert quota: %v", err) } // Platform templates templates := []struct { key, name, schema, prompt string }{ { "top_x_article", "Top X 文章", `{"fields":[{"name":"topic","type":"text","label":"话题","required":true},{"name":"count","type":"number","label":"数量","required":true,"default":10}]}`, "Write a Top {{count}} article about {{topic}}. Include brief descriptions for each item.", }, { "product_review", "产品评测文章", `{"fields":[{"name":"product_name","type":"text","label":"产品名称","required":true},{"name":"category","type":"text","label":"品类","required":true}]}`, "Write a comprehensive product review for {{product_name}} in the {{category}} category.", }, { "research_report", "研究报告文章", `{"fields":[{"name":"subject","type":"text","label":"研究主题","required":true},{"name":"depth","type":"select","label":"深度","options":["overview","detailed"],"default":"overview"}]}`, "Write a {{depth}} research report about {{subject}}.", }, { "brand_search_expansion", "品牌词搜索扩写", `{"fields":[{"name":"brand","type":"text","label":"品牌名","required":true},{"name":"keyword","type":"text","label":"关键词","required":true}]}`, "Expand on the search query '{{brand}} {{keyword}}' with a comprehensive article.", }, } for _, t := range templates { _, err = tx.Exec(ctx, ` INSERT INTO article_templates (scope, origin_type, template_key, template_name, schema_json, prompt_template, prompt_visibility, status) VALUES ('platform', 'platform', $1, $2, $3, $4, 'visible', 'active') ON CONFLICT DO NOTHING `, t.key, t.name, t.schema, t.prompt) if err != nil { log.Fatalf("insert template %s: %v", t.key, err) } } if err := tx.Commit(ctx); err != nil { log.Fatalf("commit: %v", err) } fmt.Println("Seed data inserted successfully.") fmt.Printf(" Tenant ID: %d\n", tenantID) fmt.Printf(" User ID: %d (admin@geo.local / Admin@123)\n", userID) fmt.Printf(" Plan ID: %d (free)\n", planID) fmt.Println(" Templates: 4 platform presets") }