From 5a9b5f1d8ea4e8d22b37a868f31d276e3596e69b Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Fri, 10 Jul 2026 13:55:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(server):=20cmd/migrate=20up=20=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=20codes=20=E5=BA=93=E8=87=AA=E5=BB=BA=E8=BF=81?= =?UTF-8?q?=E7=A7=BB(ApplyCodesLibMigrations)(#codes-lib)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 顺带修 000020 sqlite 索引名冲突:ALTER TABLE RENAME 不会带着重命名索引, legacy_codes 上遗留的 idx_codes_status 与 codes 库自己迁移里同名索引撞名, 一并改成 idx_legacy_codes_status(up/down 对称)。 --- server/cmd/migrate/main.go | 4 ++ server/go.mod | 1 + .../internal/store/codes_lib_migrate_test.go | 41 +++++++++++++++++++ server/internal/store/migrate.go | 14 +++++++ .../000020_codes_lib_legacy_rename.down.sql | 2 + .../000020_codes_lib_legacy_rename.up.sql | 5 +++ 6 files changed, 67 insertions(+) create mode 100644 server/internal/store/codes_lib_migrate_test.go diff --git a/server/cmd/migrate/main.go b/server/cmd/migrate/main.go index dfac877..ae96592 100644 --- a/server/cmd/migrate/main.go +++ b/server/cmd/migrate/main.go @@ -12,6 +12,7 @@ package main import ( + "context" "fmt" "log" "os" @@ -44,6 +45,9 @@ func main() { if err := store.MigrateUp(db, driver); err != nil { log.Fatalf("migrate up: %v", err) } + if err := store.ApplyCodesLibMigrations(context.Background(), db, driver); err != nil { + log.Fatalf("migrate up (codes lib): %v", err) + } log.Println("migrate: up — done") case "down": diff --git a/server/go.mod b/server/go.mod index 275c355..72a2448 100644 --- a/server/go.mod +++ b/server/go.mod @@ -17,6 +17,7 @@ require ( github.com/testcontainers/testcontainers-go v0.43.0 github.com/testcontainers/testcontainers-go/modules/mysql v0.43.0 github.com/testcontainers/testcontainers-go/modules/redis v0.43.0 + github.com/wangjia/codes v0.0.0-00010101000000-000000000000 golang.org/x/crypto v0.52.0 golang.org/x/term v0.43.0 google.golang.org/grpc v1.81.1 diff --git a/server/internal/store/codes_lib_migrate_test.go b/server/internal/store/codes_lib_migrate_test.go new file mode 100644 index 0000000..1a6e786 --- /dev/null +++ b/server/internal/store/codes_lib_migrate_test.go @@ -0,0 +1,41 @@ +package store_test + +import ( + "context" + "testing" + + "github.com/wangjia/pangolin/server/internal/config" + "github.com/wangjia/pangolin/server/internal/store" +) + +// TestApplyCodesLibMigrations verifies the shared codes library's tables are +// created on top of a fully-migrated pangolin sqlite DB (post-000020, the +// legacy tables are renamed away so the lib's `codes` table name is free). +func TestApplyCodesLibMigrations(t *testing.T) { + ctx := context.Background() + db, err := store.Open(&config.Config{Driver: "sqlite", DSN: ":memory:"}) + if err != nil { + t.Fatalf("open: %v", err) + } + defer db.Close() + if err := store.MigrateUp(db, "sqlite"); err != nil { + t.Fatalf("MigrateUp: %v", err) + } + + if err := store.ApplyCodesLibMigrations(ctx, db, "sqlite"); err != nil { + t.Fatalf("ApplyCodesLibMigrations: %v", err) + } + // 幂等:重复调用不报错(库自带 codes_schema_migrations 追踪表)。 + if err := store.ApplyCodesLibMigrations(ctx, db, "sqlite"); err != nil { + t.Fatalf("ApplyCodesLibMigrations 2nd: %v", err) + } + + for _, tbl := range []string{"codes", "codes_batches", "codes_audit_log", "codes_schema_migrations"} { + var name string + if err := db.QueryRow( + `SELECT name FROM sqlite_master WHERE type='table' AND name=?`, tbl, + ).Scan(&name); err != nil { + t.Errorf("lib table %q missing: %v", tbl, err) + } + } +} diff --git a/server/internal/store/migrate.go b/server/internal/store/migrate.go index 3d8d428..a30fa3e 100644 --- a/server/internal/store/migrate.go +++ b/server/internal/store/migrate.go @@ -1,6 +1,7 @@ package store import ( + "context" "database/sql" "errors" "fmt" @@ -11,6 +12,7 @@ import ( migratemysql "github.com/golang-migrate/migrate/v4/database/mysql" migratesqlite "github.com/golang-migrate/migrate/v4/database/sqlite" "github.com/golang-migrate/migrate/v4/source/iofs" + libcodes "github.com/wangjia/codes" "github.com/wangjia/pangolin/server/internal/db" "github.com/wangjia/pangolin/server/migrations" @@ -24,6 +26,18 @@ func MigrateUp(database *sql.DB, driver string) error { }) } +// ApplyCodesLibMigrations creates/updates the shared codes library's own +// tables (codes / codes_batches / codes_audit_log, tracked by the lib's +// codes_schema_migrations). Runs AFTER pangolin's golang-migrate set — 000020 +// must have renamed the legacy `codes` table away first. Idempotent. +func ApplyCodesLibMigrations(ctx context.Context, database *sql.DB, driver string) error { + d := libcodes.DialectMySQL + if db.Normalize(driver) == "sqlite" { + d = libcodes.DialectSQLite + } + return libcodes.ApplyMigrations(ctx, database, d) +} + // MigrateDown rolls back all applied migrations. ErrNoChange = success. func MigrateDown(database *sql.DB, driver string) error { return runMigration(database, driver, func(m *migrate.Migrate) error { diff --git a/server/migrations/sqlite/000020_codes_lib_legacy_rename.down.sql b/server/migrations/sqlite/000020_codes_lib_legacy_rename.down.sql index 32cdbcf..cc0b779 100644 --- a/server/migrations/sqlite/000020_codes_lib_legacy_rename.down.sql +++ b/server/migrations/sqlite/000020_codes_lib_legacy_rename.down.sql @@ -1,2 +1,4 @@ +DROP INDEX idx_legacy_codes_status; ALTER TABLE legacy_code_batches RENAME TO code_batches; ALTER TABLE legacy_codes RENAME TO codes; +CREATE INDEX idx_codes_status ON codes (status); diff --git a/server/migrations/sqlite/000020_codes_lib_legacy_rename.up.sql b/server/migrations/sqlite/000020_codes_lib_legacy_rename.up.sql index 99d5069..2d60a57 100644 --- a/server/migrations/sqlite/000020_codes_lib_legacy_rename.up.sql +++ b/server/migrations/sqlite/000020_codes_lib_legacy_rename.up.sql @@ -1,3 +1,8 @@ -- 同 mysql 版注释;sqlite ≥3.25 的 RENAME 会自动更新其他表 FK 引用。 ALTER TABLE codes RENAME TO legacy_codes; ALTER TABLE code_batches RENAME TO legacy_code_batches; +-- sqlite 的索引名是库级全局命名空间,ALTER TABLE RENAME 不会连带重命名索引: +-- idx_codes_status 仍挂在 legacy_codes 上但保留旧名,会跟 codes 库自己迁移里 +-- 同名的 idx_codes_status(建在它的新 codes 表上)冲突。这里随表改名一并重建索引名。 +DROP INDEX idx_codes_status; +CREATE INDEX idx_legacy_codes_status ON legacy_codes (status);