From 800d7fd4fd09381fe0095cf02c16e1fe1f41ffcb Mon Sep 17 00:00:00 2001 From: veypi Date: Sun, 15 Feb 2026 04:49:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5UI=E5=92=8C=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agents.md | 2 +- auth/auth.go | 89 ++++-- models/auth.go | 16 +- models/oauth.go | 18 +- ui/page/sys/org/detail.html | 574 +++++++++++++++++++++++++++--------- ui/page/sys/org/index.html | 507 ++++++++++++++++++++++--------- 6 files changed, 884 insertions(+), 322 deletions(-) diff --git a/agents.md b/agents.md index 1165196..401da70 100644 --- a/agents.md +++ b/agents.md @@ -12,9 +12,9 @@ ```bash //重置数据库 go run cli/main.go db drop && go run cli/main.go db migrate -// 运行, 可以通过 http://localhost:4000/_api.json 查看接口列表 go run cli/main.go -p 4000 ``` + 可以通过 http://localhost:4000/_api.json 查看接口列表 ## UI 界面开发指南 diff --git a/auth/auth.go b/auth/auth.go index ec0c1b8..0a3975d 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -214,11 +214,11 @@ func (a *appAuth) extractPermissions() []models.Permission { func (a *appAuth) initRole(roleDef models.RoleDefinition) error { // 查找或创建系统角色 var role models.Role - err := cfg.DB().Where("code = ? AND org_id = ''", roleDef.Code).First(&role).Error + err := cfg.DB().Where("code = ? AND org_id IS NULL", roleDef.Code).First(&role).Error if err != nil { // 创建新角色 role = models.Role{ - OrgID: "", + OrgID: nil, Code: roleDef.Code, Name: roleDef.Name, Description: roleDef.Description, @@ -426,13 +426,13 @@ func (a *appAuth) GrantRole(ctx context.Context, userID, orgID, roleCode string) if orgID != "" { query = query.Where("org_id = ?", orgID) } else { - query = query.Where("org_id = ''") + query = query.Where("org_id IS NULL") } if err := query.First(&role).Error; err != nil { // 如果指定了 OrgID 但没找到,尝试查找全局角色 if orgID != "" { - query = cfg.DB().Where("code = ? AND org_id = ''", roleCode) + query = cfg.DB().Where("code = ? AND org_id IS NULL", roleCode) if err := query.First(&role).Error; err != nil { return fmt.Errorf("role not found: %s", roleCode) } @@ -443,17 +443,26 @@ func (a *appAuth) GrantRole(ctx context.Context, userID, orgID, roleCode string) // 检查是否已存在 var count int64 - cfg.DB().Model(&models.UserRole{}). - Where("user_id = ? AND org_id = ? AND role_id = ?", userID, orgID, role.ID). - Count(&count) + roleQuery := cfg.DB().Model(&models.UserRole{}). + Where("user_id = ? AND role_id = ?", userID, role.ID) + if orgID != "" { + roleQuery = roleQuery.Where("org_id = ?", orgID) + } else { + roleQuery = roleQuery.Where("org_id IS NULL") + } + roleQuery.Count(&count) if count > 0 { return nil // 已存在 } + var orgIDPtr *string + if orgID != "" { + orgIDPtr = &orgID + } userRole := models.UserRole{ UserID: userID, - OrgID: orgID, + OrgID: orgIDPtr, RoleID: role.ID, ExpireAt: nil, // 默认不过期 } @@ -472,13 +481,13 @@ func (a *appAuth) RevokeRole(ctx context.Context, userID, orgID, roleCode string if orgID != "" { query = query.Where("org_id = ?", orgID) } else { - query = query.Where("org_id = ''") + query = query.Where("org_id IS NULL") } if err := query.First(&role).Error; err != nil { // 如果没找到,尝试查找全局角色 if orgID != "" { - if err := cfg.DB().Where("code = ? AND org_id = ''", roleCode).First(&role).Error; err != nil { + if err := cfg.DB().Where("code = ? AND org_id IS NULL", roleCode).First(&role).Error; err != nil { return nil // 角色不存在,无需撤销 } } else { @@ -486,8 +495,14 @@ func (a *appAuth) RevokeRole(ctx context.Context, userID, orgID, roleCode string } } - if err := cfg.DB().Where("user_id = ? AND org_id = ? AND role_id = ?", userID, orgID, role.ID). - Delete(&models.UserRole{}).Error; err != nil { + // 构建删除条件 + deleteQuery := cfg.DB().Where("user_id = ? AND role_id = ?", userID, role.ID) + if orgID != "" { + deleteQuery = deleteQuery.Where("org_id = ?", orgID) + } else { + deleteQuery = deleteQuery.Where("org_id IS NULL") + } + if err := deleteQuery.Delete(&models.UserRole{}).Error; err != nil { return err } incUserPermVersion(userID) @@ -506,18 +521,27 @@ func (a *appAuth) GrantResourcePerm(ctx context.Context, userID, orgID, permissi // 检查是否已存在 var existing models.UserPermission - err := cfg.DB().Where("user_id = ? AND org_id = ? AND permission_id = ? AND resource_id = ?", - userID, orgID, permissionID, resourceID). - First(&existing).Error + query := cfg.DB().Where("user_id = ? AND permission_id = ? AND resource_id = ?", + userID, permissionID, resourceID) + if orgID != "" { + query = query.Where("org_id = ?", orgID) + } else { + query = query.Where("org_id IS NULL") + } + err := query.First(&existing).Error if err == nil { // 已存在 return nil } + var orgIDPtr *string + if orgID != "" { + orgIDPtr = &orgID + } userPerm := models.UserPermission{ UserID: userID, - OrgID: orgID, + OrgID: orgIDPtr, PermissionID: permissionID, ResourceID: resourceID, ExpireAt: nil, // 默认不过期 @@ -535,9 +559,14 @@ func (a *appAuth) RevokeResourcePerm(ctx context.Context, userID, orgID, permiss if strings.Count(permissionID, ":") == 1 { permissionID = fmt.Sprintf("%s:%s", a.appKey, permissionID) } - if err := cfg.DB().Where("user_id = ? AND org_id = ? AND permission_id = ? AND resource_id = ?", - userID, orgID, permissionID, resourceID). - Delete(&models.UserPermission{}).Error; err != nil { + query := cfg.DB().Where("user_id = ? AND permission_id = ? AND resource_id = ?", + userID, permissionID, resourceID) + if orgID != "" { + query = query.Where("org_id = ?", orgID) + } else { + query = query.Where("org_id IS NULL") + } + if err := query.Delete(&models.UserPermission{}).Error; err != nil { return err } incUserPermVersion(userID) @@ -546,14 +575,24 @@ func (a *appAuth) RevokeResourcePerm(ctx context.Context, userID, orgID, permiss func (a *appAuth) RevokeAll(ctx context.Context, userID, orgID string) error { // 删除用户角色 - if err := cfg.DB().Where("user_id = ? AND org_id = ?", userID, orgID). - Delete(&models.UserRole{}).Error; err != nil { + roleQuery := cfg.DB().Where("user_id = ?", userID) + if orgID != "" { + roleQuery = roleQuery.Where("org_id = ?", orgID) + } else { + roleQuery = roleQuery.Where("org_id IS NULL") + } + if err := roleQuery.Delete(&models.UserRole{}).Error; err != nil { return err } // 删除用户特定权限 - if err := cfg.DB().Where("user_id = ? AND org_id = ?", userID, orgID). - Delete(&models.UserPermission{}).Error; err != nil { + permQuery := cfg.DB().Where("user_id = ?", userID) + if orgID != "" { + permQuery = permQuery.Where("org_id = ?", orgID) + } else { + permQuery = permQuery.Where("org_id IS NULL") + } + if err := permQuery.Delete(&models.UserPermission{}).Error; err != nil { return err } @@ -602,9 +641,9 @@ func (a *appAuth) checkPermissionDB(ctx context.Context, userID, orgID, permissi Where("user_id = ? AND (expire_at IS NULL OR expire_at > ?)", userID, time.Now()) if orgID != "" { - roleQuery = roleQuery.Where("org_id = ? OR org_id = ''", orgID) + roleQuery = roleQuery.Where("org_id = ? OR org_id IS NULL", orgID) } else { - roleQuery = roleQuery.Where("org_id = ''") + roleQuery = roleQuery.Where("org_id IS NULL") } if err := roleQuery.Pluck("role_id", &roleIDs).Error; err != nil { diff --git a/models/auth.go b/models/auth.go index 8ba7b65..c8d08fc 100644 --- a/models/auth.go +++ b/models/auth.go @@ -38,12 +38,12 @@ func (Permission) TableName() string { // Role 角色表(不关联 app,可跨应用) type Role struct { vigo.Model - OrgID string `json:"org_id" gorm:"index;size:36" desc:"组织ID,空=系统预设"` - Code string `json:"code" gorm:"index;size:50" desc:"角色代码"` - Name string `json:"name" desc:"角色名称"` - Description string `json:"description" desc:"角色描述"` - IsSystem bool `json:"is_system" desc:"是否系统预设角色"` - Status int `json:"status" gorm:"default:1" desc:"状态: 1=启用, 0=禁用"` + OrgID *string `json:"org_id" gorm:"index;size:36" desc:"组织ID,空=系统预设"` + Code string `json:"code" gorm:"index;size:50" desc:"角色代码"` + Name string `json:"name" desc:"角色名称"` + Description string `json:"description" desc:"角色描述"` + IsSystem bool `json:"is_system" desc:"是否系统预设角色"` + Status int `json:"status" gorm:"default:1" desc:"状态: 1=启用, 0=禁用"` // 外键关联 Org *Org `json:"org,omitempty" gorm:"foreignKey:OrgID;references:ID"` @@ -73,7 +73,7 @@ func (RolePermission) TableName() string { type UserRole struct { vigo.Model UserID string `json:"user_id" gorm:"index;size:36" desc:"用户ID"` - OrgID string `json:"org_id" gorm:"index;size:36" desc:"组织ID"` + OrgID *string `json:"org_id" gorm:"index;size:36" desc:"组织ID"` RoleID string `json:"role_id" gorm:"index;size:36" desc:"角色ID"` ExpireAt *time.Time `json:"expire_at" desc:"过期时间(可选)"` @@ -91,7 +91,7 @@ func (UserRole) TableName() string { type UserPermission struct { vigo.Model UserID string `json:"user_id" gorm:"index;size:36" desc:"用户ID"` - OrgID string `json:"org_id" gorm:"index;size:36" desc:"组织ID"` + OrgID *string `json:"org_id" gorm:"index;size:36" desc:"组织ID"` PermissionID string `json:"permission_id" gorm:"index;size:100" desc:"权限ID"` ResourceID string `json:"resource_id" gorm:"index;size:100" desc:"具体资源ID,* 表示所有"` ExpireAt *time.Time `json:"expire_at" desc:"过期时间(可选)"` diff --git a/models/oauth.go b/models/oauth.go index a3ca2a9..ce2570c 100644 --- a/models/oauth.go +++ b/models/oauth.go @@ -21,9 +21,9 @@ type OAuthClient struct { Description string `json:"description" gorm:"size:500"` RedirectURIs string `json:"redirect_uris" gorm:"type:text"` // JSON数组 AllowedScopes string `json:"allowed_scopes" gorm:"size:500"` // 空格分隔 - OwnerID string `json:"owner_id" gorm:"not null"` - OrgID string `json:"org_id" gorm:"index"` - Status int `json:"status" gorm:"default:1"` + OwnerID string `json:"owner_id" gorm:"not null"` + OrgID *string `json:"org_id" gorm:"index"` + Status int `json:"status" gorm:"default:1"` // 外键关联 Owner User `json:"owner,omitempty" gorm:"foreignKey:OwnerID;references:ID"` @@ -38,9 +38,9 @@ func (OAuthClient) TableName() string { type OAuthAuthorizationCode struct { vigo.Model Code string `json:"code" gorm:"uniqueIndex;size:100;not null"` - ClientID string `json:"client_id" gorm:"index;not null"` - UserID string `json:"user_id" gorm:"index;not null"` - OrgID string `json:"org_id" gorm:"index"` + ClientID string `json:"client_id" gorm:"index;not null"` + UserID string `json:"user_id" gorm:"index;not null"` + OrgID *string `json:"org_id" gorm:"index"` RedirectURI string `json:"redirect_uri" gorm:"size:500"` Scope string `json:"scope" gorm:"size:200"` CodeChallenge string `json:"-" gorm:"size:128"` @@ -61,9 +61,9 @@ func (OAuthAuthorizationCode) TableName() string { // OAuthToken OAuth2.0 令牌 type OAuthToken struct { vigo.Model - ClientID string `json:"client_id" gorm:"index;not null"` - UserID string `json:"user_id" gorm:"index;not null"` - OrgID string `json:"org_id" gorm:"index"` + ClientID string `json:"client_id" gorm:"index;not null"` + UserID string `json:"user_id" gorm:"index;not null"` + OrgID *string `json:"org_id" gorm:"index"` AccessToken string `json:"-" gorm:"uniqueIndex;size:255;not null"` RefreshToken string `json:"-" gorm:"uniqueIndex;size:255"` TokenType string `json:"token_type" gorm:"size:20;default:'Bearer'"` diff --git a/ui/page/sys/org/detail.html b/ui/page/sys/org/detail.html index c25bcba..4ead286 100644 --- a/ui/page/sys/org/detail.html +++ b/ui/page/sys/org/detail.html @@ -1,165 +1,447 @@ + - - {{ $t('org.detail') }} - + + {{ org ? org.name : ($t('org.detail') || 'Organization Detail') }} + + + +
+
+ {{ $t('common.loading') || 'Loading...' }} +
+ + + + + + + + + +
+ {{ $t('common.cancel') || 'Cancel' }} + + {{ $t('common.save') || 'Save' }} + +
+
+ diff --git a/ui/page/sys/org/index.html b/ui/page/sys/org/index.html index c7116f5..20550ee 100644 --- a/ui/page/sys/org/index.html +++ b/ui/page/sys/org/index.html @@ -1,148 +1,389 @@ + - - {{ $t('nav.org') }} - + + {{ $t('nav.org') }} + + -