feat(#142): group GoAuto admin navigation
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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() {
|
||||
_, fileName, _, _ := runtime.Caller(0)
|
||||
migration.Migrate.SetVersion(migration.GetFilename(fileName), migrateGoAutoMenuGroups)
|
||||
}
|
||||
|
||||
func migrateGoAutoMenuGroups(db *gorm.DB, version string) error {
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := ensureGoAutoMenuGroups(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Create(&common.Migration{Version: version}).Error
|
||||
})
|
||||
}
|
||||
|
||||
func ensureGoAutoMenuGroups(db *gorm.DB) error {
|
||||
modules := make(map[string]access.ModuleDefinition)
|
||||
for _, module := range access.GoAutoModules() {
|
||||
modules[module.Key] = module
|
||||
}
|
||||
|
||||
var purchaser migrationmodels.SysRole
|
||||
if err := db.Where("role_key = ?", access.RolePurchaser).First(&purchaser).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allowedPurchaserMenus := make([]migrationmodels.SysMenu, 0, len(access.GoAutoModules())+len(access.GoAutoMenuGroups()))
|
||||
for _, group := range access.GoAutoMenuGroups() {
|
||||
groupMenu, _, err := upsertGoAutoMenu(db, migrationmodels.SysMenu{
|
||||
MenuName: group.MenuName,
|
||||
Title: group.Title,
|
||||
Icon: group.Icon,
|
||||
Path: group.Path,
|
||||
MenuType: "M",
|
||||
Action: "无",
|
||||
ParentId: 0,
|
||||
Component: "Layout",
|
||||
Sort: group.Sort,
|
||||
Visible: "0",
|
||||
IsFrame: "1",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Model(&groupMenu).Update("paths", fmt.Sprintf("/0/%d", groupMenu.MenuId)).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
allowedPurchaserMenus = append(allowedPurchaserMenus, groupMenu)
|
||||
|
||||
for childIndex, moduleKey := range group.ModuleKeys {
|
||||
module, ok := modules[moduleKey]
|
||||
if !ok {
|
||||
return fmt.Errorf("GoAuto menu group %s references unknown module %s", group.Key, moduleKey)
|
||||
}
|
||||
var child migrationmodels.SysMenu
|
||||
if err := db.Unscoped().Where("menu_name = ?", module.RouteName).First(&child).Error; err != nil {
|
||||
return fmt.Errorf("find GoAuto child menu %s: %w", module.RouteName, err)
|
||||
}
|
||||
updates := map[string]any{
|
||||
"parent_id": groupMenu.MenuId,
|
||||
"paths": fmt.Sprintf("/0/%d/%d", groupMenu.MenuId, child.MenuId),
|
||||
"sort": childIndex + 1,
|
||||
"deleted_at": nil,
|
||||
}
|
||||
if err := db.Unscoped().Model(&child).Updates(updates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if module.PurchaserHardHidden {
|
||||
if err := db.Model(&purchaser).Association("SysMenu").Delete(&child); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if module.PurchaserDefault {
|
||||
allowedPurchaserMenus = append(allowedPurchaserMenus, child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := db.Model(&purchaser).Association("SysMenu").Append(allowedPurchaserMenus); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldRootNames := make([]string, 0, len(access.GoAutoModules()))
|
||||
for _, module := range access.GoAutoModules() {
|
||||
oldRootNames = append(oldRootNames, module.RouteName+"Root")
|
||||
}
|
||||
var oldRoots []migrationmodels.SysMenu
|
||||
if err := db.Unscoped().Where("menu_name IN ?", oldRootNames).Find(&oldRoots).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if len(oldRoots) == 0 {
|
||||
return nil
|
||||
}
|
||||
oldRootIDs := make([]int, 0, len(oldRoots))
|
||||
for _, root := range oldRoots {
|
||||
oldRootIDs = append(oldRootIDs, root.MenuId)
|
||||
}
|
||||
if err := db.Table("sys_role_menu").Where("menu_id IN ?", oldRootIDs).Delete(nil).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Table("sys_menu_api_rule").Where("sys_menu_menu_id IN ?", oldRootIDs).Delete(nil).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Unscoped().Where("menu_id IN ?", oldRootIDs).Delete(&migrationmodels.SysMenu{}).Error
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package version_local
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"go-admin/app/goauto/access"
|
||||
migrationmodels "go-admin/cmd/migrate/migration/models"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestEnsureGoAutoMenuGroups(t *testing.T) {
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = db.AutoMigrate(
|
||||
&migrationmodels.SysRole{}, &migrationmodels.SysMenu{}, &migrationmodels.SysApi{},
|
||||
&purchaserCasbinRule{},
|
||||
); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
custom := migrationmodels.SysMenu{MenuName: "UserCustomMenu", Title: "用户自定义菜单", Path: "/custom"}
|
||||
if err = db.Create(&custom).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = ensureGoAutoMenusAndPurchaserBindings(db); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
childIDs := make(map[string]int)
|
||||
for _, module := range access.GoAutoModules() {
|
||||
var child migrationmodels.SysMenu
|
||||
if err = db.Where("menu_name = ?", module.RouteName).First(&child).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
childIDs[module.RouteName] = child.MenuId
|
||||
}
|
||||
|
||||
if err = ensureGoAutoMenuGroups(db); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertGoAutoGroupedMenuState(t, db, childIDs)
|
||||
if err = ensureGoAutoMenuGroups(db); err != nil {
|
||||
t.Fatalf("group reconciliation must be repeatable: %v", err)
|
||||
}
|
||||
assertGoAutoGroupedMenuState(t, db, childIDs)
|
||||
|
||||
var customAfter migrationmodels.SysMenu
|
||||
if err = db.First(&customAfter, custom.MenuId).Error; err != nil {
|
||||
t.Fatal("migration must not remove a user menu:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertGoAutoGroupedMenuState(t *testing.T, db *gorm.DB, childIDs map[string]int) {
|
||||
t.Helper()
|
||||
modules := make(map[string]access.ModuleDefinition)
|
||||
for _, module := range access.GoAutoModules() {
|
||||
modules[module.Key] = module
|
||||
var oldRootCount int64
|
||||
if err := db.Unscoped().Model(&migrationmodels.SysMenu{}).
|
||||
Where("menu_name = ?", module.RouteName+"Root").Count(&oldRootCount).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if oldRootCount != 0 {
|
||||
t.Fatalf("old root %s still exists", module.RouteName+"Root")
|
||||
}
|
||||
}
|
||||
|
||||
var purchaser migrationmodels.SysRole
|
||||
if err := db.Where("role_key = ?", access.RolePurchaser).First(&purchaser).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, group := range access.GoAutoMenuGroups() {
|
||||
var parent migrationmodels.SysMenu
|
||||
if err := db.Where("menu_name = ?", group.MenuName).First(&parent).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if parent.ParentId != 0 || parent.Title != group.Title || parent.Path != group.Path || parent.Paths != fmt.Sprintf("/0/%d", parent.MenuId) {
|
||||
t.Fatalf("unexpected group menu: %#v", parent)
|
||||
}
|
||||
assertRoleHasMenu(t, db, purchaser.RoleId, parent.MenuId, true)
|
||||
|
||||
for index, moduleKey := range group.ModuleKeys {
|
||||
module := modules[moduleKey]
|
||||
var child migrationmodels.SysMenu
|
||||
if err := db.Preload("SysApi").Where("menu_name = ?", module.RouteName).First(&child).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if child.MenuId != childIDs[module.RouteName] {
|
||||
t.Fatalf("child %s ID changed from %d to %d", module.RouteName, childIDs[module.RouteName], child.MenuId)
|
||||
}
|
||||
if child.ParentId != parent.MenuId || child.Paths != fmt.Sprintf("/0/%d/%d", parent.MenuId, child.MenuId) || child.Sort != index+1 {
|
||||
t.Fatalf("unexpected grouped child %s: %#v", module.RouteName, child)
|
||||
}
|
||||
if child.Path != module.Path+"/index" || child.Component != module.Component || len(child.SysApi) != len(module.APIs) {
|
||||
t.Fatalf("route or API association changed for %s: %#v", module.RouteName, child)
|
||||
}
|
||||
assertRoleHasMenu(t, db, purchaser.RoleId, child.MenuId, !module.PurchaserHardHidden)
|
||||
}
|
||||
}
|
||||
|
||||
var goAutoCount int64
|
||||
if err := db.Model(&migrationmodels.SysMenu{}).Where("menu_name LIKE ?", "GoAuto%").Count(&goAutoCount).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want := int64(len(access.GoAutoModules()) + len(access.GoAutoMenuGroups())); goAutoCount != want {
|
||||
t.Fatalf("got %d GoAuto rows, want %d", goAutoCount, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user