package repository import ( "hyapp-admin-server/internal/config" "hyapp-admin-server/internal/model" "hyapp-admin-server/internal/security" "gorm.io/gorm" ) var defaultPermissions = []model.Permission{ {Name: "总览查看", Code: "overview:view", Kind: "menu"}, {Name: "用户查看", Code: "user:view", Kind: "menu"}, {Name: "用户创建", Code: "user:create", Kind: "button"}, {Name: "用户更新", Code: "user:update", Kind: "button"}, {Name: "用户状态", Code: "user:status", Kind: "button"}, {Name: "用户重置密码", Code: "user:reset-password", Kind: "button"}, {Name: "用户导出", Code: "user:export", Kind: "button"}, {Name: "App 用户查看", Code: "app-user:view", Kind: "menu"}, {Name: "App 用户更新", Code: "app-user:update", Kind: "button"}, {Name: "App 用户状态", Code: "app-user:status", Kind: "button"}, {Name: "App 用户设置密码", Code: "app-user:password", Kind: "button"}, {Name: "等级配置查看", Code: "level-config:view", Kind: "menu"}, {Name: "等级配置更新", Code: "level-config:update", Kind: "button"}, {Name: "地区屏蔽查看", Code: "region-block:view", Kind: "menu"}, {Name: "地区屏蔽更新", Code: "region-block:update", Kind: "button"}, {Name: "房间查看", Code: "room:view", Kind: "menu"}, {Name: "房间更新", Code: "room:update", Kind: "button"}, {Name: "房间删除", Code: "room:delete", Kind: "button"}, {Name: "房间置顶查看", Code: "room-pin:view", Kind: "menu"}, {Name: "房间置顶创建", Code: "room-pin:create", Kind: "button"}, {Name: "房间置顶取消", Code: "room-pin:cancel", Kind: "button"}, {Name: "房间配置查看", Code: "room-config:view", Kind: "menu"}, {Name: "房间配置更新", Code: "room-config:update", Kind: "button"}, {Name: "APP 配置查看", Code: "app-config:view", Kind: "menu"}, {Name: "APP 配置更新", Code: "app-config:update", Kind: "button"}, {Name: "版本管理查看", Code: "app-version:view", Kind: "menu"}, {Name: "版本管理创建", Code: "app-version:create", Kind: "button"}, {Name: "版本管理更新", Code: "app-version:update", Kind: "button"}, {Name: "版本管理删除", Code: "app-version:delete", Kind: "button"}, {Name: "资源查看", Code: "resource:view", Kind: "menu"}, {Name: "资源创建", Code: "resource:create", Kind: "button"}, {Name: "资源更新", Code: "resource:update", Kind: "button"}, {Name: "资源组查看", Code: "resource-group:view", Kind: "menu"}, {Name: "资源组创建", Code: "resource-group:create", Kind: "button"}, {Name: "资源组更新", Code: "resource-group:update", Kind: "button"}, {Name: "资源赠送查看", Code: "resource-grant:view", Kind: "menu"}, {Name: "资源赠送创建", Code: "resource-grant:create", Kind: "button"}, {Name: "礼物查看", Code: "gift:view", Kind: "menu"}, {Name: "礼物创建", Code: "gift:create", Kind: "button"}, {Name: "礼物更新", Code: "gift:update", Kind: "button"}, {Name: "礼物状态", Code: "gift:status", Kind: "button"}, {Name: "表情包查看", Code: "emoji-pack:view", Kind: "menu"}, {Name: "表情包创建", Code: "emoji-pack:create", Kind: "button"}, {Name: "国家查看", Code: "country:view", Kind: "menu"}, {Name: "国家创建", Code: "country:create", Kind: "button"}, {Name: "国家更新", Code: "country:update", Kind: "button"}, {Name: "国家状态", Code: "country:status", Kind: "button"}, {Name: "区域查看", Code: "region:view", Kind: "menu"}, {Name: "区域创建", Code: "region:create", Kind: "button"}, {Name: "区域更新", Code: "region:update", Kind: "button"}, {Name: "区域状态", Code: "region:status", Kind: "button"}, {Name: "主播查看", Code: "host:view", Kind: "menu"}, {Name: "Agency 查看", Code: "agency:view", Kind: "menu"}, {Name: "Agency 创建", Code: "agency:create", Kind: "button"}, {Name: "Agency 状态", Code: "agency:status", Kind: "button"}, {Name: "BD 查看", Code: "bd:view", Kind: "menu"}, {Name: "BD 创建", Code: "bd:create", Kind: "button"}, {Name: "BD 更新", Code: "bd:update", Kind: "button"}, {Name: "币商查看", Code: "coin-seller:view", Kind: "menu"}, {Name: "币商创建", Code: "coin-seller:create", Kind: "button"}, {Name: "币商更新", Code: "coin-seller:update", Kind: "button"}, {Name: "币商进货", Code: "coin-seller:stock-credit", Kind: "button"}, {Name: "金币流水查看", Code: "coin-ledger:view", Kind: "menu"}, {Name: "金币增减查看", Code: "coin-adjustment:view", Kind: "menu"}, {Name: "金币增减创建", Code: "coin-adjustment:create", Kind: "button"}, {Name: "支付账单查看", Code: "payment-bill:view", Kind: "menu"}, {Name: "内购配置查看", Code: "payment-product:view", Kind: "menu"}, {Name: "内购配置创建", Code: "payment-product:create", Kind: "button"}, {Name: "内购配置更新", Code: "payment-product:update", Kind: "button"}, {Name: "内购配置删除", Code: "payment-product:delete", Kind: "button"}, {Name: "游戏查看", Code: "game:view", Kind: "menu"}, {Name: "游戏创建", Code: "game:create", Kind: "button"}, {Name: "游戏更新", Code: "game:update", Kind: "button"}, {Name: "游戏状态", Code: "game:status", Kind: "button"}, {Name: "游戏删除", Code: "game:delete", Kind: "button"}, {Name: "每日任务查看", Code: "daily-task:view", Kind: "menu"}, {Name: "每日任务创建", Code: "daily-task:create", Kind: "button"}, {Name: "每日任务更新", Code: "daily-task:update", Kind: "button"}, {Name: "每日任务状态", Code: "daily-task:status", Kind: "button"}, {Name: "成就配置查看", Code: "achievement:view", Kind: "menu"}, {Name: "成就配置创建", Code: "achievement:create", Kind: "button"}, {Name: "成就配置更新", Code: "achievement:update", Kind: "button"}, {Name: "注册奖励查看", Code: "registration-reward:view", Kind: "menu"}, {Name: "注册奖励更新", Code: "registration-reward:update", Kind: "button"}, {Name: "七日签到查看", Code: "seven-day-checkin:view", Kind: "menu"}, {Name: "七日签到更新", Code: "seven-day-checkin:update", Kind: "button"}, {Name: "幸运礼物查看", Code: "lucky-gift:view", Kind: "menu"}, {Name: "幸运礼物更新", Code: "lucky-gift:update", Kind: "button"}, {Name: "房间宝箱查看", Code: "room-treasure:view", Kind: "menu"}, {Name: "房间宝箱更新", Code: "room-treasure:update", Kind: "button"}, {Name: "用户榜单查看", Code: "user-leaderboard:view", Kind: "menu"}, {Name: "红包配置查看", Code: "red-packet:view", Kind: "menu"}, {Name: "红包配置更新", Code: "red-packet:update", Kind: "button"}, {Name: "VIP 配置查看", Code: "vip-config:view", Kind: "menu"}, {Name: "VIP 配置更新", Code: "vip-config:update", Kind: "button"}, {Name: "VIP 赠送", Code: "vip-config:grant", Kind: "button"}, {Name: "角色查看", Code: "role:view", Kind: "menu"}, {Name: "角色创建", Code: "role:create", Kind: "button"}, {Name: "角色更新", Code: "role:update", Kind: "button"}, {Name: "角色删除", Code: "role:delete", Kind: "button"}, {Name: "角色授权", Code: "role:permission", Kind: "button"}, {Name: "角色数据权限", Code: "role:data-scope", Kind: "button"}, {Name: "角色管理", Code: "role:manage", Kind: "button", Description: "兼容旧版本的角色管理权限"}, {Name: "权限查看", Code: "permission:view", Kind: "menu"}, {Name: "权限创建", Code: "permission:create", Kind: "button"}, {Name: "权限更新", Code: "permission:update", Kind: "button"}, {Name: "权限删除", Code: "permission:delete", Kind: "button"}, {Name: "权限同步", Code: "permission:sync", Kind: "button"}, {Name: "菜单查看", Code: "menu:view", Kind: "menu"}, {Name: "菜单创建", Code: "menu:create", Kind: "button"}, {Name: "菜单更新", Code: "menu:update", Kind: "button"}, {Name: "菜单删除", Code: "menu:delete", Kind: "button"}, {Name: "菜单排序", Code: "menu:sort", Kind: "button"}, {Name: "菜单显隐", Code: "menu:visible", Kind: "button"}, {Name: "日志查看", Code: "log:view", Kind: "menu"}, {Name: "日志导出", Code: "log:export", Kind: "button"}, {Name: "任务查看", Code: "job:view", Kind: "menu"}, {Name: "任务取消", Code: "job:cancel", Kind: "button"}, {Name: "导出任务创建", Code: "export:create", Kind: "button"}, {Name: "文件上传", Code: "upload:create", Kind: "button"}, } var deprecatedPermissionCodes = []string{ "service:view", "service:create", "service:update", "service:operate", "notification:view", "notification:read", "notification:read-all", "notification:delete", } var deprecatedMenuCodes = []string{"services", "notifications", "jobs"} func (s *Store) seedPermissions() error { return s.syncDefaultPermissions(s.db) } func (s *Store) seedMenus() error { systemID := uint(0) geoID := uint(0) hostOrgID := uint(0) appUsersID := uint(0) appConfigID := uint(0) resourceID := uint(0) operationsID := uint(0) roomsID := uint(0) paymentID := uint(0) activityID := uint(0) gameID := uint(0) menus := []model.Menu{ {Title: "总览", Code: "overview", Path: "/overview", Icon: "dashboard", PermissionCode: "overview:view", Sort: 10, Visible: true}, {Title: "后台设置", Code: "system", Path: "", Icon: "settings", PermissionCode: "", Sort: 30, Visible: true}, {Title: "日志审计", Code: "logs", Path: "", Icon: "history", PermissionCode: "", Sort: 40, Visible: false}, {Title: "用户管理", Code: "app-users", Path: "", Icon: "users", PermissionCode: "", Sort: 60, Visible: true}, {Title: "房间管理", Code: "rooms", Path: "", Icon: "room", PermissionCode: "", Sort: 65, Visible: true}, {Title: "APP配置", Code: "app-config", Path: "", Icon: "settings", PermissionCode: "", Sort: 66, Visible: true}, {Title: "资源管理", Code: "resources", Path: "", Icon: "inventory", PermissionCode: "", Sort: 67, Visible: true}, {Title: "运营管理", Code: "operations", Path: "", Icon: "operations", PermissionCode: "", Sort: 68, Visible: true}, {Title: "支付管理", Code: "payment", Path: "", Icon: "wallet", PermissionCode: "", Sort: 69, Visible: true}, {Title: "活动管理", Code: "activities", Path: "", Icon: "campaign", PermissionCode: "", Sort: 70, Visible: true}, {Title: "游戏管理", Code: "games", Path: "", Icon: "sports_esports", PermissionCode: "", Sort: 71, Visible: true}, {Title: "地区管理", Code: "geo", Path: "", Icon: "map", PermissionCode: "", Sort: 72, Visible: true}, {Title: "团队管理", Code: "host-org", Path: "", Icon: "network", PermissionCode: "", Sort: 80, Visible: true}, } for _, menu := range menus { syncedMenu, err := s.syncDefaultMenu(s.db, menu) if err != nil { return err } if syncedMenu.Code == "system" { systemID = syncedMenu.ID } if syncedMenu.Code == "geo" { geoID = syncedMenu.ID } if syncedMenu.Code == "host-org" { hostOrgID = syncedMenu.ID } if syncedMenu.Code == "app-users" { appUsersID = syncedMenu.ID } if syncedMenu.Code == "app-config" { appConfigID = syncedMenu.ID } if syncedMenu.Code == "resources" { resourceID = syncedMenu.ID } if syncedMenu.Code == "operations" { operationsID = syncedMenu.ID } if syncedMenu.Code == "rooms" { roomsID = syncedMenu.ID } if syncedMenu.Code == "payment" { paymentID = syncedMenu.ID } if syncedMenu.Code == "activities" { activityID = syncedMenu.ID } if syncedMenu.Code == "games" { gameID = syncedMenu.ID } } children := []model.Menu{ {ParentID: &systemID, Title: "后台用户", Code: "system-users", Path: "/system/users", Icon: "users", PermissionCode: "user:view", Sort: 31, Visible: true}, {ParentID: &systemID, Title: "角色配置", Code: "system-roles", Path: "/system/roles", Icon: "shield", PermissionCode: "role:view", Sort: 32, Visible: true}, {ParentID: &systemID, Title: "菜单权限", Code: "system-menus", Path: "/system/menus", Icon: "menu", PermissionCode: "menu:view", Sort: 33, Visible: true}, {ParentID: &systemID, Title: "登录日志", Code: "logs-login", Path: "/logs/login", Icon: "login", PermissionCode: "log:view", Sort: 34, Visible: true}, {ParentID: &systemID, Title: "操作日志", Code: "logs-operations", Path: "/logs/operations", Icon: "receipt", PermissionCode: "log:view", Sort: 35, Visible: true}, {ParentID: &appUsersID, Title: "用户列表", Code: "app-user-list", Path: "/app/users", Icon: "users", PermissionCode: "app-user:view", Sort: 60, Visible: true}, {ParentID: &appUsersID, Title: "封禁列表", Code: "app-user-bans", Path: "/app/users/bans", Icon: "block", PermissionCode: "app-user:view", Sort: 61, Visible: true}, {ParentID: &appUsersID, Title: "登录日志", Code: "app-user-login-logs", Path: "/app/users/login-logs", Icon: "login", PermissionCode: "app-user:view", Sort: 62, Visible: true}, {ParentID: &appUsersID, Title: "等级配置", Code: "app-user-level-config", Path: "/app/users/level-config", Icon: "military_tech", PermissionCode: "level-config:view", Sort: 63, Visible: true}, {ParentID: &appUsersID, Title: "地区屏蔽", Code: "app-user-region-blocks", Path: "/app/users/region-blocks", Icon: "public", PermissionCode: "region-block:view", Sort: 64, Visible: true}, {ParentID: &roomsID, Title: "房间列表", Code: "room-list", Path: "/rooms", Icon: "room", PermissionCode: "room:view", Sort: 65, Visible: true}, {ParentID: &roomsID, Title: "房间置顶", Code: "room-pins", Path: "/rooms/pins", Icon: "push_pin", PermissionCode: "room-pin:view", Sort: 66, Visible: true}, {ParentID: &roomsID, Title: "房间配置", Code: "room-config", Path: "/rooms/config", Icon: "settings", PermissionCode: "room-config:view", Sort: 67, Visible: true}, {ParentID: &appConfigID, Title: "H5配置", Code: "app-config-h5", Path: "/app-config/h5", Icon: "settings", PermissionCode: "app-config:view", Sort: 66, Visible: true}, {ParentID: &appConfigID, Title: "BANNER配置", Code: "app-config-banners", Path: "/app-config/banners", Icon: "image", PermissionCode: "app-config:view", Sort: 67, Visible: true}, {ParentID: &appConfigID, Title: "Explore配置", Code: "app-config-explore", Path: "/app-config/explore", Icon: "explore", PermissionCode: "app-config:view", Sort: 68, Visible: true}, {ParentID: &appConfigID, Title: "内购配置", Code: "payment-recharge-products", Path: "/app-config/recharge-products", Icon: "wallet", PermissionCode: "payment-product:view", Sort: 69, Visible: true}, {ParentID: &appConfigID, Title: "版本管理", Code: "app-config-versions", Path: "/app-config/versions", Icon: "settings", PermissionCode: "app-version:view", Sort: 70, Visible: true}, {ParentID: &resourceID, Title: "资源列表", Code: "resource-list", Path: "/resources", Icon: "inventory", PermissionCode: "resource:view", Sort: 67, Visible: true}, {ParentID: &resourceID, Title: "资源组列表", Code: "resource-group-list", Path: "/resource-groups", Icon: "category", PermissionCode: "resource-group:view", Sort: 68, Visible: true}, {ParentID: &resourceID, Title: "礼物列表", Code: "gift-list", Path: "/gifts", Icon: "gift", PermissionCode: "gift:view", Sort: 69, Visible: true}, {ParentID: &resourceID, Title: "资源赠送", Code: "resource-grant-list", Path: "/resource-grants", Icon: "send", PermissionCode: "resource-grant:view", Sort: 70, Visible: true}, {ParentID: &resourceID, Title: "表情包列表", Code: "emoji-pack-list", Path: "/emoji-packs", Icon: "image", PermissionCode: "emoji-pack:view", Sort: 71, Visible: true}, {ParentID: &operationsID, Title: "金币流水", Code: "operation-coin-ledger", Path: "/operations/coin-ledger", Icon: "receipt", PermissionCode: "coin-ledger:view", Sort: 68, Visible: true}, {ParentID: &operationsID, Title: "金币增减", Code: "operation-coin-adjustment", Path: "/operations/coin-adjustments", Icon: "wallet", PermissionCode: "coin-adjustment:view", Sort: 69, Visible: true}, {ParentID: &operationsID, Title: "幸运礼物", Code: "lucky-gift", Path: "/operations/lucky-gift", Icon: "redeem", PermissionCode: "lucky-gift:view", Sort: 70, Visible: true}, {ParentID: &paymentID, Title: "账单列表", Code: "payment-bill-list", Path: "/payment/bills", Icon: "receipt", PermissionCode: "payment-bill:view", Sort: 68, Visible: true}, {ParentID: &activityID, Title: "每日任务", Code: "daily-task-list", Path: "/activities/daily-tasks", Icon: "task", PermissionCode: "daily-task:view", Sort: 69, Visible: true}, {ParentID: &activityID, Title: "注册奖励", Code: "registration-reward", Path: "/activities/registration-reward", Icon: "gift", PermissionCode: "registration-reward:view", Sort: 70, Visible: true}, {ParentID: &activityID, Title: "成就配置", Code: "achievement-config", Path: "/activities/achievements", Icon: "military_tech", PermissionCode: "achievement:view", Sort: 71, Visible: true}, {ParentID: &activityID, Title: "七日签到", Code: "seven-day-checkin", Path: "/activities/seven-day-checkin", Icon: "event_available", PermissionCode: "seven-day-checkin:view", Sort: 72, Visible: true}, {ParentID: &activityID, Title: "房间宝箱", Code: "room-treasure", Path: "/activities/room-treasure", Icon: "redeem", PermissionCode: "room-treasure:view", Sort: 73, Visible: true}, {ParentID: &activityID, Title: "用户榜单", Code: "user-leaderboard", Path: "/activities/user-leaderboards", Icon: "leaderboard", PermissionCode: "user-leaderboard:view", Sort: 74, Visible: true}, {ParentID: &activityID, Title: "红包配置", Code: "red-packet", Path: "/activities/red-packets", Icon: "redeem", PermissionCode: "red-packet:view", Sort: 75, Visible: true}, {ParentID: &activityID, Title: "VIP配置", Code: "vip-config", Path: "/activities/vip-config", Icon: "workspace_premium", PermissionCode: "vip-config:view", Sort: 76, Visible: true}, {ParentID: &gameID, Title: "游戏列表", Code: "game-list", Path: "/games", Icon: "sports_esports", PermissionCode: "game:view", Sort: 70, Visible: true}, {ParentID: &geoID, Title: "国家管理", Code: "host-org-countries", Path: "/host/countries", Icon: "public", PermissionCode: "country:view", Sort: 70, Visible: true}, {ParentID: &geoID, Title: "区域管理", Code: "host-org-regions", Path: "/host/regions", Icon: "map", PermissionCode: "region:view", Sort: 71, Visible: true}, {ParentID: &hostOrgID, Title: "Manager列表", Code: "host-org-managers", Path: "/host/managers", Icon: "users", PermissionCode: "agency:view", Sort: 80, Visible: true}, {ParentID: &hostOrgID, Title: "Agency 列表", Code: "host-org-agencies", Path: "/host/agencies", Icon: "apartment", PermissionCode: "agency:view", Sort: 81, Visible: true}, {ParentID: &hostOrgID, Title: "BD Leader 列表", Code: "host-org-bd-leaders", Path: "/host/bd-leaders", Icon: "shield", PermissionCode: "bd:view", Sort: 82, Visible: true}, {ParentID: &hostOrgID, Title: "BD 列表", Code: "host-org-bds", Path: "/host/bds", Icon: "team", PermissionCode: "bd:view", Sort: 83, Visible: true}, {ParentID: &hostOrgID, Title: "Host 列表", Code: "host-org-hosts", Path: "/host/hosts", Icon: "users", PermissionCode: "host:view", Sort: 84, Visible: true}, {ParentID: &hostOrgID, Title: "Coin Saller列表", Code: "host-org-coin-sellers", Path: "/host/coin-sellers", Icon: "wallet", PermissionCode: "coin-seller:view", Sort: 85, Visible: true}, } for _, menu := range children { if _, err := s.syncDefaultMenu(s.db, menu); err != nil { return err } } return nil } func (s *Store) seedRoles() error { var permissions []model.Permission if err := s.db.Find(&permissions).Error; err != nil { return err } roles := []model.Role{ {Name: "平台管理员", Code: "platform-admin", Description: "拥有管理后台全部权限"}, {Name: "运维管理员", Code: "ops-admin", Description: "管理用户状态和后台操作"}, {Name: "审计员", Code: "auditor", Description: "查看日志和审计记录"}, {Name: "只读用户", Code: "readonly", Description: "只读查看后台数据"}, } for _, role := range roles { if err := s.db.Where("code = ?", role.Code).FirstOrCreate(&role).Error; err != nil { return err } if role.Code == "platform-admin" { if err := s.db.Model(&role).Association("Permissions").Replace(permissions); err != nil { return err } continue } // 预置角色只在没有任何权限时写入默认模板,避免后续人工调整被启动种子覆盖。 if s.db.Model(&role).Association("Permissions").Count() == 0 { if err := s.replaceRolePermissionsByCode(&role, defaultRolePermissionCodes(role.Code)); err != nil { return err } continue } if err := s.appendRolePermissionsByCode(s.db, &role, defaultRolePermissionMigrationCodes(role.Code)); err != nil { return err } } return nil } func (s *Store) seedUsers(cfg config.Config) error { var count int64 if err := s.db.Model(&model.User{}).Count(&count).Error; err != nil { return err } if count > 0 { return nil } if cfg.Bootstrap.Password == "" { return nil } hash, err := security.HashPassword(cfg.Bootstrap.Password) if err != nil { return err } var role model.Role if err := s.db.Where("code = ?", "platform-admin").First(&role).Error; err != nil { return err } user := model.User{ Username: cfg.Bootstrap.Username, Name: cfg.Bootstrap.Name, PasswordHash: hash, Team: cfg.Bootstrap.Team, Status: model.UserStatusActive, MFAEnabled: false, } if err := s.db.Create(&user).Error; err != nil { return err } return s.db.Model(&user).Association("Roles").Replace([]model.Role{role}) } func (s *Store) pruneDeprecatedDefaults() error { return s.db.Transaction(func(tx *gorm.DB) error { if err := tx.Where("code IN ? OR permission_code IN ?", deprecatedMenuCodes, deprecatedPermissionCodes).Delete(&model.Menu{}).Error; err != nil { return err } var permissions []model.Permission if err := tx.Where("code IN ?", deprecatedPermissionCodes).Find(&permissions).Error; err != nil { return err } if len(permissions) == 0 { return nil } permissionIDs := make([]uint, 0, len(permissions)) for _, permission := range permissions { permissionIDs = append(permissionIDs, permission.ID) } if err := tx.Table("admin_role_permissions").Where("permission_id IN ?", permissionIDs).Delete(nil).Error; err != nil { return err } return tx.Where("id IN ?", permissionIDs).Delete(&model.Permission{}).Error }) } func (s *Store) syncDefaultPermissions(tx *gorm.DB) error { for _, definition := range defaultPermissions { permission := model.Permission{} // 权限种子是前后端契约来源;已存在时同步名称、类型和说明,避免按钮文案和授权页长期漂移。 result := tx.Where("code = ?", definition.Code).Limit(1).Find(&permission) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { if err := tx.Create(&definition).Error; err != nil { return err } continue } if err := tx.Model(&permission).Updates(map[string]any{ "name": definition.Name, "kind": definition.Kind, "description": definition.Description, }).Error; err != nil { return err } } return nil } func (s *Store) syncDefaultMenu(tx *gorm.DB, definition model.Menu) (model.Menu, error) { menu := model.Menu{} // 菜单种子同样是前后端契约来源;已存在时同步标题、路径和权限,避免后台菜单文案长期漂移。 result := tx.Where("code = ?", definition.Code).Limit(1).Find(&menu) if result.Error != nil { return model.Menu{}, result.Error } if result.RowsAffected == 0 { if err := tx.Create(&definition).Error; err != nil { return model.Menu{}, err } return definition, nil } if err := tx.Model(&menu).Updates(map[string]any{ "parent_id": definition.ParentID, "title": definition.Title, "path": definition.Path, "icon": definition.Icon, "permission_code": definition.PermissionCode, "sort": definition.Sort, "visible": definition.Visible, }).Error; err != nil { return model.Menu{}, err } return menu, nil } func (s *Store) replaceRolePermissionsByCode(role *model.Role, codes []string) error { var permissions []model.Permission if len(codes) > 0 { if err := s.db.Where("code IN ?", codes).Find(&permissions).Error; err != nil { return err } } return s.db.Model(role).Association("Permissions").Replace(permissions) } func (s *Store) appendRolePermissionsByCode(tx *gorm.DB, role *model.Role, codes []string) error { if len(codes) == 0 { return nil } var current model.Role if err := tx.Preload("Permissions").First(¤t, role.ID).Error; err != nil { return err } existing := make(map[string]struct{}, len(current.Permissions)) for _, permission := range current.Permissions { existing[permission.Code] = struct{}{} } missingCodes := make([]string, 0, len(codes)) for _, code := range codes { if _, ok := existing[code]; !ok { missingCodes = append(missingCodes, code) } } if len(missingCodes) == 0 { return nil } var permissions []model.Permission if err := tx.Where("code IN ?", missingCodes).Find(&permissions).Error; err != nil { return err } if len(permissions) == 0 { return nil } return tx.Model(role).Association("Permissions").Append(permissions) } func defaultRolePermissionCodes(code string) []string { switch code { case "ops-admin": return []string{ "overview:view", "user:view", "user:status", "app-user:view", "app-user:update", "app-user:status", "app-user:password", "level-config:view", "level-config:update", "region-block:view", "region-block:update", "room:view", "room:update", "room:delete", "room-pin:view", "room-pin:create", "room-pin:cancel", "room-config:view", "room-config:update", "app-config:view", "app-config:update", "app-version:view", "app-version:create", "app-version:update", "app-version:delete", "resource:view", "resource:create", "resource:update", "resource-group:view", "resource-group:create", "resource-group:update", "resource-grant:view", "resource-grant:create", "gift:view", "gift:create", "gift:update", "gift:status", "emoji-pack:view", "emoji-pack:create", "country:view", "country:create", "country:update", "country:status", "region:view", "region:create", "region:update", "region:status", "host:view", "agency:view", "agency:create", "agency:status", "bd:view", "bd:create", "bd:update", "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "lucky-gift:view", "lucky-gift:update", "game:view", "game:create", "game:update", "game:status", "game:delete", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", "achievement:view", "achievement:create", "achievement:update", "seven-day-checkin:view", "seven-day-checkin:update", "room-treasure:view", "room-treasure:update", "red-packet:view", "red-packet:update", "vip-config:view", "vip-config:update", "vip-config:grant", "log:view", "job:view", "job:cancel", "export:create", "upload:create", } case "auditor": return []string{"overview:view", "log:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-ledger:view", "coin-adjustment:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view", "role:view", "permission:view", "job:view"} case "readonly": return []string{ "overview:view", "user:view", "app-user:view", "level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "country:view", "region:view", "host:view", "agency:view", "bd:view", "coin-seller:view", "coin-ledger:view", "coin-adjustment:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view", "role:view", "permission:view", "menu:view", "log:view", "job:view", } default: return nil } } func defaultRolePermissionMigrationCodes(code string) []string { switch code { case "ops-admin": return []string{ "app-user:update", "app-user:status", "app-user:password", "room:view", "room:update", "room:delete", "room-pin:view", "room-pin:create", "room-pin:cancel", "room-config:view", "room-config:update", "app-config:view", "app-config:update", "app-version:view", "app-version:create", "app-version:update", "app-version:delete", "level-config:view", "level-config:update", "region-block:view", "region-block:update", "resource:view", "resource:create", "resource:update", "resource-group:view", "resource-group:create", "resource-group:update", "resource-grant:view", "resource-grant:create", "gift:view", "gift:create", "gift:update", "gift:status", "emoji-pack:view", "emoji-pack:create", "upload:create", "country:view", "country:create", "country:update", "country:status", "region:view", "region:create", "region:update", "region:status", "coin-seller:view", "coin-seller:create", "coin-seller:update", "coin-seller:stock-credit", "coin-ledger:view", "coin-adjustment:view", "coin-adjustment:create", "payment-bill:view", "payment-product:view", "payment-product:create", "payment-product:update", "payment-product:delete", "lucky-gift:view", "lucky-gift:update", "game:view", "game:create", "game:update", "game:status", "game:delete", "daily-task:view", "daily-task:create", "daily-task:update", "daily-task:status", "achievement:view", "achievement:create", "achievement:update", "seven-day-checkin:view", "seven-day-checkin:update", "room-treasure:view", "room-treasure:update", "red-packet:view", "red-packet:update", "vip-config:view", "vip-config:update", "vip-config:grant", } case "auditor", "readonly": return []string{"level-config:view", "region-block:view", "room:view", "room-pin:view", "room-config:view", "app-config:view", "app-version:view", "resource:view", "resource-group:view", "resource-grant:view", "gift:view", "emoji-pack:view", "coin-seller:view", "coin-ledger:view", "coin-adjustment:view", "lucky-gift:view", "payment-bill:view", "payment-product:view", "game:view", "daily-task:view", "achievement:view", "seven-day-checkin:view", "room-treasure:view", "red-packet:view", "vip-config:view"} default: return nil } }