From cad9f6d1858227abc89fec39c1569e4e48b39641 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Fri, 11 Sep 2026 16:49:56 +0800 Subject: [PATCH] fix(goauto): add the missing menu migration for SYB product filters #269 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registering a module in access.GoAutoModules() does not make its page reachable on an existing database. sys_menu rows are written by the versioned migration that first created them, and that version is already recorded in sys_migration everywhere — it never runs again. Startup only reconciles the casbin API matrix, not menus. So on a fresh database the page appeared and on every real one it did not: the table, the API and the route were all present with no way to navigate there. This adds a migration that upserts the entry under 采采管理 beside 「SYB 店铺」 and binds it to both 管理员 and 采购员. Verified by deleting the row from an already-migrated database and re-running migrate: 新执行 1 个,跳过 42 个, row restored with both roles. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F --- .../1789113000000_syb_product_filter_menu.go | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 server/cmd/migrate/migration/version-local/1789113000000_syb_product_filter_menu.go diff --git a/server/cmd/migrate/migration/version-local/1789113000000_syb_product_filter_menu.go b/server/cmd/migrate/migration/version-local/1789113000000_syb_product_filter_menu.go new file mode 100644 index 0000000..1cb17dc --- /dev/null +++ b/server/cmd/migrate/migration/version-local/1789113000000_syb_product_filter_menu.go @@ -0,0 +1,91 @@ +package version_local + +import ( + "fmt" + "runtime" + + "go-admin/app/goauto/access" + "go-admin/cmd/migrate/migration" + migrationmodels "go-admin/cmd/migrate/migration/models" + common "go-admin/common/models" + + "gorm.io/gorm" +) + +func init() { + _, file, _, _ := runtime.Caller(0) + migration.Migrate.SetVersion(migration.GetFilename(file), migrateSYBProductFilterMenu) +} + +// migrateSYBProductFilterMenu adds the 「SYB 商品过滤」 menu entry for #269. +// +// `[必须]` Adding a module to access.GoAutoModules() is not enough to make its +// page reachable. Menus are written by the versioned migration that first +// created them, and that version is already recorded in sys_migration on every +// existing database — it never runs again. Startup only reconciles the casbin +// API matrix (access.ReconcilePurchaserPermissions), not sys_menu. Without this +// migration the page exists, the API answers and the route resolves, yet nobody +// can navigate to it on an already-migrated database. +func migrateSYBProductFilterMenu(db *gorm.DB, version string) error { + return db.Transaction(func(tx *gorm.DB) error { + module, ok := goAutoModuleByKey(access.ModuleSYBProductFilters) + if !ok { + return fmt.Errorf("module %q is not registered in access.GoAutoModules()", access.ModuleSYBProductFilters) + } + + // The page belongs beside 「SYB 店铺」 under 采采管理, which is where the + // admin already manages which SYB data enters the system. + var parent migrationmodels.SysMenu + if err := tx.Where("menu_name = ?", "GoAutoCollectionManagement").First(&parent).Error; err != nil { + return err + } + + child, _, err := upsertGoAutoMenu(tx, migrationmodels.SysMenu{ + MenuName: module.RouteName, + Title: module.Title, + Icon: module.Icon, + Path: module.Path + "/index", + MenuType: "C", + Action: "无", + ParentId: parent.MenuId, + Component: module.Component, + Sort: module.Sort, + Visible: "0", + IsFrame: "1", + }) + if err != nil { + return err + } + if err = tx.Model(&child).Update("paths", fmt.Sprintf("/0/%d/%d", parent.MenuId, child.MenuId)).Error; err != nil { + return err + } + + // Bind to both roles the module declares. 采购员 needs it because the + // module is PurchaserDefault; 管理员 needs it because only an admin may + // change the rules (the write routes are RequireRoleKey("admin")). + roleKeys := []string{"admin"} + if module.PurchaserDefault { + roleKeys = append(roleKeys, access.RolePurchaser) + } + for _, key := range roleKeys { + var role migrationmodels.SysRole + if err = tx.Where("role_key = ?", key).First(&role).Error; err != nil { + return err + } + if err = tx.Model(&role).Association("SysMenu").Append(&child); err != nil { + return err + } + } + + return tx.Create(&common.Migration{Version: version}).Error + }) +} + +func goAutoModuleByKey(key string) (access.ModuleDefinition, bool) { + for _, module := range access.GoAutoModules() { + if module.Key == key { + return module, true + } + } + return access.ModuleDefinition{}, false +}