Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

migrate.md

maoxiaoyue edited this page May 14, 2026 · 2 revisions

pkg/migrate — Migration Diff 自動生成

掃描 Go Model struct 的 bun tag,比對快照檔案,自動產生 up/down SQL migration。

設計理念

AI 修改 Model struct 後,手動寫 SQL migration 容易遺漏欄位。Migration Diff 讓框架自動偵測變更並產生正確的 SQL:

Model struct 變更 → 反射掃描 bun tag → 比對上次快照 → 產生 SQL

快速上手

1. 註冊 Model

import "github.com/maoxiaoyue/hypgo/pkg/migrate"
type User struct {
 bun.BaseModel `bun:"table:users"`
 ID int64 `bun:"id,pk,autoincrement"`
 Name string `bun:"name,notnull"`
 Email string `bun:"email,notnull,unique"`
 Bio string `bun:"bio,type:text"`
 CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp"`
}
registry := migrate.NewRegistry()
registry.Register((*User)(nil))

2. 掃描 & Diff

// 掃描 Model → 提取 TableSchema
tables := migrate.ScanModels(registry)
// 載入上次快照
snapshot, _ := migrate.LoadSnapshot(".hyp/schema_snapshot.json")
// 比對 → 產生 ChangeSet
changes := migrate.Diff(tables, snapshot)
// 產生 SQL
up, down := migrate.GenerateSQL(changes, "postgres")
fmt.Println(up)
// → ALTER TABLE "users" ADD COLUMN "bio" TEXT;

3. 儲存

// 儲存新快照
migrate.SaveSnapshot(".hyp/schema_snapshot.json", tables)
// 取得 migration 檔名
upFile, downFile := migrate.MigrationFiles("migrations/")
// → migrations/20260326_200000_auto.up.sql
// → migrations/20260326_200000_auto.down.sql

完整工作流程

func generateMigration() {
 // 1. 註冊所有 Model
 registry := migrate.NewRegistry()
 registry.Register(
 (*models.User)(nil),
 (*models.Post)(nil),
 (*models.Comment)(nil),
 )
 // 2. 掃描
 tables := migrate.ScanModels(registry)
 // 3. Diff
 snapshot, _ := migrate.LoadSnapshot(".hyp/schema_snapshot.json")
 changes := migrate.Diff(tables, snapshot)
 if len(changes) == 0 {
 fmt.Println("No changes detected")
 return
 }
 // 4. 顯示變更
 for _, c := range changes {
 fmt.Println(c.String())
 }
 // 5. 產生 SQL
 up, down := migrate.GenerateSQL(changes, "postgres")
 // 6. 儲存 migration 檔案
 upFile, downFile := migrate.MigrationFiles("migrations/")
 os.WriteFile(upFile, []byte(up), 0644)
 os.WriteFile(downFile, []byte(down), 0644)
 // 7. 更新快照
 migrate.SaveSnapshot(".hyp/schema_snapshot.json", tables)
}

Scanner — bun tag 解析

Scanner 從 struct field 的 bun:"..." tag 提取欄位資訊:

bun tag 解析結果
bun:"id,pk,autoincrement" PrimaryKey=true, AutoIncrement=true
bun:"name,notnull" NotNull=true
bun:"email,notnull,unique" NotNull=true, Unique=true
bun:"bio,type:text" SQLType="text"
bun:"score,default:0" Default="0"
bun:"table:users,alias:u" 表名="users"(BaseModel tag)
bun:"-" 跳過此欄位

Diff — 變更偵測

變更類型 說明
AddTable 快照中不存在的新表
DropTable Model 中已移除的表
AddColumn 表中新增的欄位
DropColumn 表中已移除的欄位
AlterColumn 欄位屬性變更(型別、NotNull、Default 等)

SQL Generator — 多 Dialect 支援

特性 PostgreSQL MySQL
識別符引號 "users" `users`
自增主鍵 int64 BIGSERIAL BIGINT AUTO_INCREMENT
自增主鍵 int SERIAL INT AUTO_INCREMENT
浮點數 DOUBLE PRECISION DOUBLE
時間戳 TIMESTAMPTZ DATETIME
ALTER NOT NULL SET/DROP NOT NULL MODIFY COLUMN

Go → SQL 型別對映

Go Type SQL Type
int64 BIGINT
int, int32 INTEGER
int16 SMALLINT
float64 DOUBLE PRECISION / DOUBLE
float32 REAL
bool BOOLEAN
string TEXT
time.Time TIMESTAMPTZ / DATETIME

若有 type:xxx tag,優先使用指定的 SQL 型別。

快照格式

{
 "tables": {
 "users": {
 "name": "users",
 "columns": [
 {"name": "id", "go_type": "int64", "primary_key": true, "auto_increment": true},
 {"name": "name", "go_type": "string", "not_null": true},
 {"name": "email", "go_type": "string", "not_null": true, "unique": true}
 ]
 }
 }
}

架構

pkg/migrate/
├── registry.go ModelRegistry — Model 註冊
├── scanner.go ScanModel() — 反射提取 bun tag → TableSchema
├── snapshot.go 快照讀寫(JSON)
├── diff.go Diff() — 比對新舊 schema → ChangeSet
├── generator.go GenerateSQL() — ChangeSet → SQL(pg/mysql)
└── migrate_test.go 22 個單元測試

HypGo

繁體中文 | English


中文文件

設計文件

套件

AI 協作工具鏈

CLI 命令


English Docs

Design Docs

Packages

AI Collaboration Toolchain

CLI Commands

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /