|
|
# VBase 开发者使用手册
|
|
|
|
|
|
## 架构概述
|
|
|
|
|
|
```
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
│ vbase/Auth 包 │
|
|
|
│ ├─ New(appKey, config) - 创建权限管理实例 │
|
|
|
│ ├─ Init() - 启动时检查冲突并同步数据库 │
|
|
|
│ └─ Auth 接口 - 权限检查和管理方法 │
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
│
|
|
|
├─────────────────────┼─────────────────────┐
|
|
|
▼ ▼ ▼
|
|
|
注册权限配置 接口权限检查 编程式授权
|
|
|
(代码中声明) (中间件使用) (运行时调用)
|
|
|
```
|
|
|
|
|
|
## 快速开始
|
|
|
|
|
|
```go
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
"github.com/veypi/vbase"
|
|
|
"github.com/veypi/vbase/api"
|
|
|
"github.com/veypi/vigo"
|
|
|
)
|
|
|
|
|
|
// 全局权限管理实例
|
|
|
crmAuth := vbase.Auth.New("crm", vbase.AppConfig{
|
|
|
Name: "客户关系管理",
|
|
|
Description: "CRM系统",
|
|
|
})
|
|
|
|
|
|
func main() {
|
|
|
r := vigo.NewRouter()
|
|
|
|
|
|
// 1. 挂载 VBase 基础设施
|
|
|
r.Extend("/api/vb", api.Router)
|
|
|
|
|
|
// 2. 程序启动时初始化所有权限配置
|
|
|
if err := vbase.Auth.Init(); err != nil {
|
|
|
log.Fatal("权限初始化失败:", err)
|
|
|
}
|
|
|
|
|
|
// 3. 创建业务路由
|
|
|
crm := r.SubRouter("/api/crm")
|
|
|
crm.Use(middleware.AuthRequired())
|
|
|
crm.Use(middleware.OrgContext())
|
|
|
|
|
|
// 4. 使用 crmAuth 配置接口权限
|
|
|
crm.Get("/customers", crmAuth.Perm("customer", "list"), "客户列表", listCustomers)
|
|
|
crm.Post("/customers", crmAuth.Perm("customer", "create"), "创建客户", createCustomer)
|
|
|
crm.Get("/customers/{id}", crmAuth.Perm("customer", "read"), "客户详情", getCustomer)
|
|
|
crm.Patch("/customers/{id}", crmAuth.Perm("customer", "update"), "更新客户", updateCustomer)
|
|
|
crm.Delete("/customers/{id}", crmAuth.Perm("customer", "delete"), "删除客户", deleteCustomer)
|
|
|
|
|
|
// 5. 资源所有者权限
|
|
|
crm.Patch("/customers/{id}/private",
|
|
|
crmAuth.PermWithOwner("customer", "update", "owner_id"),
|
|
|
"仅限所有者",
|
|
|
privateUpdate)
|
|
|
|
|
|
vigo.Run(r)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. 权限管理包 vbase/Auth
|
|
|
|
|
|
### 1.1 包结构
|
|
|
|
|
|
```go
|
|
|
package auth
|
|
|
|
|
|
// Auth 权限管理接口
|
|
|
type Auth interface {
|
|
|
// ========== 中间件生成 ==========
|
|
|
// 基础权限检查
|
|
|
Perm(resource, action string) func(*vigo.X) error
|
|
|
|
|
|
// 资源所有者权限
|
|
|
PermWithOwner(resource, action, ownerKey string) func(*vigo.X) error
|
|
|
|
|
|
// 满足任一权限
|
|
|
PermAny(permissions [][2]string) func(*vigo.X) error
|
|
|
|
|
|
// 满足所有权限
|
|
|
PermAll(permissions [][2]string) func(*vigo.X) error
|
|
|
|
|
|
// ========== 权限管理 ==========
|
|
|
// 授予角色
|
|
|
GrantRole(ctx context.Context, req GrantRoleRequest) error
|
|
|
|
|
|
// 撤销角色
|
|
|
RevokeRole(ctx context.Context, userID, orgID, roleCode string) error
|
|
|
|
|
|
// 授予特定资源权限
|
|
|
GrantResourcePerm(ctx context.Context, req GrantResourcePermRequest) error
|
|
|
|
|
|
// 撤销特定资源权限
|
|
|
RevokeResourcePerm(ctx context.Context, userID, orgID, resource, resourceID string) error
|
|
|
|
|
|
// 撤销用户所有权限
|
|
|
RevokeAll(ctx context.Context, userID, orgID string) error
|
|
|
|
|
|
// ========== 权限查询 ==========
|
|
|
// 检查权限
|
|
|
CheckPermission(ctx context.Context, req CheckPermRequest) (bool, error)
|
|
|
|
|
|
// 列出用户权限
|
|
|
ListUserPermissions(ctx context.Context, userID, orgID string) ([]UserPermission, error)
|
|
|
|
|
|
// 列出资源授权用户
|
|
|
ListResourceUsers(ctx context.Context, orgID, resource, resourceID string) ([]ResourceUser, error)
|
|
|
}
|
|
|
|
|
|
// 全局 Auth 工厂
|
|
|
var Auth = &authFactory{}
|
|
|
|
|
|
type authFactory struct {
|
|
|
apps map[string]*appAuth // appKey -> auth实例
|
|
|
}
|
|
|
|
|
|
// New 创建权限管理实例(注册应用)
|
|
|
func (f *authFactory) New(appKey string, config AppConfig) Auth
|
|
|
|
|
|
// Init 初始化所有注册的权限配置
|
|
|
// - 检查不同 app 之间是否有冲突
|
|
|
// - 同步 Policy 到数据库
|
|
|
// - 建立路由匹配缓存
|
|
|
func (f *authFactory) Init() error
|
|
|
```
|
|
|
|
|
|
### 1.2 使用流程
|
|
|
|
|
|
```go
|
|
|
// 阶段1:程序初始化(全局变量/ init 函数)
|
|
|
package main
|
|
|
|
|
|
// CRM 模块权限
|
|
|
crmAuth := vbase.Auth.New("crm", vbase.AppConfig{
|
|
|
Name: "客户关系管理",
|
|
|
DefaultRoles: []RoleDefinition{
|
|
|
{Code: "admin", Name: "管理员", Policies: []string{"*:*"}},
|
|
|
{Code: "sales", Name: "销售", Policies: []string{"customer:*", "order:read"}},
|
|
|
},
|
|
|
})
|
|
|
|
|
|
// ERP 模块权限
|
|
|
erpAuth := vbase.Auth.New("erp", vbase.AppConfig{
|
|
|
Name: "企业资源计划",
|
|
|
DefaultRoles: []RoleDefinition{
|
|
|
{Code: "admin", Name: "管理员", Policies: []string{"*:*"}},
|
|
|
{Code: "buyer", Name: "采购", Policies: []string{"supplier:read", "purchase:*"}},
|
|
|
},
|
|
|
})
|
|
|
|
|
|
func main() {
|
|
|
// 阶段2:程序启动时初始化
|
|
|
if err := vbase.Auth.Init(); err != nil {
|
|
|
log.Fatal(err)
|
|
|
}
|
|
|
|
|
|
// 阶段3:运行时使用
|
|
|
// crmAuth.Perm(...)
|
|
|
// erpAuth.GrantRole(...)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2. AppConfig 配置
|
|
|
|
|
|
```go
|
|
|
type AppConfig struct {
|
|
|
Name string // 应用名称
|
|
|
Description string // 应用描述
|
|
|
DefaultRoles []RoleDefinition // 预设角色
|
|
|
}
|
|
|
|
|
|
type RoleDefinition struct {
|
|
|
Code string // 角色代码: admin/manager/sales/viewer
|
|
|
Name string // 角色名称
|
|
|
Policies []string // 权限列表: ["customer:read", "customer:create", "*:*"]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 多模块隔离示例
|
|
|
|
|
|
```go
|
|
|
// 模块1:CRM
|
|
|
crmAuth := vbase.Auth.New("crm", vbase.AppConfig{
|
|
|
Name: "客户关系管理",
|
|
|
DefaultRoles: []vbase.RoleDefinition{
|
|
|
{Code: "admin", Name: "管理员", Policies: []string{"customer:*", "contract:*"}},
|
|
|
{Code: "sales", Name: "销售", Policies: []string{"customer:read", "customer:create", "customer:update"}},
|
|
|
},
|
|
|
})
|
|
|
|
|
|
// 模块2:ERP(资源名可以重复,通过 appKey 隔离)
|
|
|
erpAuth := vbase.Auth.New("erp", vbase.AppConfig{
|
|
|
Name: "企业资源计划",
|
|
|
DefaultRoles: []vbase.RoleDefinition{
|
|
|
{Code: "admin", Name: "管理员", Policies: []string{"customer:*", "supplier:*"}},
|
|
|
// erp:customer:read 和 crm:customer:read 是两个不同权限
|
|
|
},
|
|
|
})
|
|
|
|
|
|
// 启动时统一检查
|
|
|
crmAuth, erpAuth 会被 Init() 同时处理
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 3. 接口权限配置
|
|
|
|
|
|
### 3.1 基础权限
|
|
|
|
|
|
```go
|
|
|
func main() {
|
|
|
// ... vbase.Auth.Init() ...
|
|
|
|
|
|
api := r.SubRouter("/api/crm")
|
|
|
api.Use(middleware.AuthRequired())
|
|
|
api.Use(middleware.OrgContext())
|
|
|
|
|
|
// 标准 CRUD
|
|
|
api.Get("/customers", crmAuth.Perm("customer", "list"), "客户列表", listHandler)
|
|
|
api.Post("/customers", crmAuth.Perm("customer", "create"), "创建客户", createHandler)
|
|
|
api.Get("/customers/{id}", crmAuth.Perm("customer", "read"), "客户详情", getHandler)
|
|
|
api.Patch("/customers/{id}", crmAuth.Perm("customer", "update"), "更新客户", updateHandler)
|
|
|
api.Delete("/customers/{id}", crmAuth.Perm("customer", "delete"), "删除客户", deleteHandler)
|
|
|
|
|
|
// 自定义操作
|
|
|
api.Post("/customers/{id}/export", crmAuth.Perm("customer", "export"), "导出客户", exportHandler)
|
|
|
api.Post("/customers/{id}/transfer", crmAuth.Perm("customer", "transfer"), "转交客户", transferHandler)
|
|
|
|
|
|
// 通配权限(拥有 customer 任意操作即可)
|
|
|
api.Get("/customers/stats", crmAuth.Perm("customer", "*"), "客户统计", statsHandler)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 3.2 资源所有者权限
|
|
|
|
|
|
```go
|
|
|
func getCustomer(x *vigo.X, req *GetReq) (*Customer, error) {
|
|
|
customer := query(req.ID)
|
|
|
x.Set("owner_id", customer.OwnerID) // 必须设置 owner_id
|
|
|
return customer, nil
|
|
|
}
|
|
|
|
|
|
// 只有所有者或管理员能更新
|
|
|
api.Patch("/customers/{id}",
|
|
|
crmAuth.PermWithOwner("customer", "update", "owner_id"),
|
|
|
"更新客户",
|
|
|
updateHandler,
|
|
|
)
|
|
|
|
|
|
// 只有所有者能删除(管理员也不行)
|
|
|
api.Delete("/customers/{id}/danger",
|
|
|
crmAuth.PermWithOwner("customer", "delete", "owner_id"),
|
|
|
"危险删除",
|
|
|
dangerDeleteHandler,
|
|
|
)
|
|
|
```
|
|
|
|
|
|
### 3.3 组合权限
|
|
|
|
|
|
```go
|
|
|
// 满足任一权限即可
|
|
|
api.Patch("/customers/{id}",
|
|
|
crmAuth.PermAny([][2]string{
|
|
|
{"customer", "update"},
|
|
|
{"customer", "admin"},
|
|
|
}),
|
|
|
"更新客户",
|
|
|
updateHandler,
|
|
|
)
|
|
|
|
|
|
// 必须同时满足所有权限
|
|
|
api.Post("/customers/batch-import",
|
|
|
crmAuth.PermAll([][2]string{
|
|
|
{"customer", "create"},
|
|
|
{"customer", "batch"},
|
|
|
}),
|
|
|
"批量导入",
|
|
|
batchImportHandler,
|
|
|
)
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 4. 编程式权限管理
|
|
|
|
|
|
开发者在自己的业务逻辑中调用这些方法管理权限。
|
|
|
|
|
|
### 4.1 授予角色
|
|
|
|
|
|
```go
|
|
|
func inviteMember(ctx context.Context, req InviteReq) error {
|
|
|
// 检查当前用户是否有邀请权限
|
|
|
ok, _ := crmAuth.CheckPermission(ctx, CheckPermRequest{
|
|
|
UserID: req.CurrentUserID,
|
|
|
OrgID: req.OrgID,
|
|
|
Resource: "org_member",
|
|
|
Action: "invite",
|
|
|
})
|
|
|
if !ok {
|
|
|
return fmt.Errorf("无权邀请")
|
|
|
}
|
|
|
|
|
|
// 创建用户
|
|
|
user, _ := vbase.User.FindOrCreateByEmail(req.Email)
|
|
|
|
|
|
// 加入组织并授予角色
|
|
|
if err := crmAuth.GrantRole(ctx, GrantRoleRequest{
|
|
|
UserID: user.ID,
|
|
|
OrgID: req.OrgID,
|
|
|
Role: req.Role, // "admin" / "sales" / "viewer"
|
|
|
}); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 4.2 授予特定资源权限
|
|
|
|
|
|
```go
|
|
|
// 场景:将客户转交给销售 B 跟进
|
|
|
func transferCustomer(ctx context.Context, customerID, fromUserID, toUserID, orgID string) error {
|
|
|
// 1. 撤销 A 的权限
|
|
|
if err := crmAuth.RevokeResourcePerm(ctx, fromUserID, orgID, "customer", customerID); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// 2. 授予 B 权限
|
|
|
if err := crmAuth.GrantResourcePerm(ctx, GrantResourcePermRequest{
|
|
|
UserID: toUserID,
|
|
|
OrgID: orgID,
|
|
|
Resource: "customer",
|
|
|
ResourceID: customerID,
|
|
|
Action: "*", // 所有操作
|
|
|
}); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// 场景:临时授权(3天后过期)
|
|
|
func tempGrant(ctx context.Context, userID, orgID string) error {
|
|
|
return crmAuth.GrantResourcePerm(ctx, GrantResourcePermRequest{
|
|
|
UserID: userID,
|
|
|
OrgID: orgID,
|
|
|
Resource: "report",
|
|
|
ResourceID: "*",
|
|
|
Action: "export",
|
|
|
ExpireAt: time.Now().Add(3 * 24 * time.Hour),
|
|
|
})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 4.3 查询权限
|
|
|
|
|
|
```go
|
|
|
// 检查用户是否能操作某客户
|
|
|
func canEditCustomer(ctx context.Context, userID, orgID, customerID string) bool {
|
|
|
ok, _ := crmAuth.CheckPermission(ctx, CheckPermRequest{
|
|
|
UserID: userID,
|
|
|
OrgID: orgID,
|
|
|
Resource: "customer",
|
|
|
ResourceID: customerID,
|
|
|
Action: "update",
|
|
|
})
|
|
|
return ok
|
|
|
}
|
|
|
|
|
|
// 获取用户在组织的所有权限
|
|
|
perms, _ := crmAuth.ListUserPermissions(ctx, "user-123", "org-456")
|
|
|
// 返回:
|
|
|
// [
|
|
|
// {Resource: "customer", ResourceID: "*", Actions: ["read","list"]},
|
|
|
// {Resource: "customer", ResourceID: "c-789", Actions: ["*"]},
|
|
|
// {Resource: "order", ResourceID: "*", Actions: ["read"]}
|
|
|
// ]
|
|
|
|
|
|
// 获取某客户的所有授权用户
|
|
|
users, _ := crmAuth.ListResourceUsers(ctx, "org-456", "customer", "c-789")
|
|
|
// 返回:
|
|
|
// [
|
|
|
// {UserID: "u1", Actions: ["admin"]},
|
|
|
// {UserID: "u2", Actions: ["read","update"]}
|
|
|
// ]
|
|
|
```
|
|
|
|
|
|
### 4.4 撤销权限
|
|
|
|
|
|
```go
|
|
|
// 撤销角色
|
|
|
crmAuth.RevokeRole(ctx, "user-123", "org-456", "sales")
|
|
|
|
|
|
// 撤销特定资源权限
|
|
|
crmAuth.RevokeResourcePerm(ctx, "user-123", "org-456", "customer", "c-789")
|
|
|
|
|
|
// 撤销用户在组织的所有权限(离职)
|
|
|
crmAuth.RevokeAll(ctx, "user-123", "org-456")
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 5. 完整示例:多模块应用
|
|
|
|
|
|
```go
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
"github.com/veypi/vbase"
|
|
|
"github.com/veypi/vbase/api"
|
|
|
"github.com/veypi/vbase/api/middleware"
|
|
|
"github.com/veypi/vigo"
|
|
|
)
|
|
|
|
|
|
// ========== 阶段1:定义权限(全局)==========
|
|
|
|
|
|
// CRM 模块
|
|
|
crmAuth := vbase.Auth.New("crm", vbase.AppConfig{
|
|
|
Name: "客户关系管理",
|
|
|
DefaultRoles: []vbase.RoleDefinition{
|
|
|
{Code: "admin", Name: "管理员", Policies: []string{"*:*"}},
|
|
|
{Code: "manager", Name: "主管", Policies: []string{"customer:*", "contract:*", "report:*"}},
|
|
|
{Code: "sales", Name: "销售", Policies: []string{"customer:read", "customer:create", "customer:update"}},
|
|
|
{Code: "viewer", Name: "查看者", Policies: []string{"customer:read"}},
|
|
|
},
|
|
|
})
|
|
|
|
|
|
// 合同模块(独立,但共用 crm 用户体系)
|
|
|
contractAuth := vbase.Auth.New("contract", vbase.AppConfig{
|
|
|
Name: "合同管理",
|
|
|
DefaultRoles: []vbase.RoleDefinition{
|
|
|
{Code: "admin", Name: "管理员", Policies: []string{"*:*"}},
|
|
|
{Code: "legal", Name: "法务", Policies: []string{"contract:read", "contract:audit", "contract:approve"}},
|
|
|
{Code: "manager", Name: "业务经理", Policies: []string{"contract:read", "contract:create", "contract:sign"}},
|
|
|
},
|
|
|
})
|
|
|
|
|
|
func main() {
|
|
|
// ========== 阶段2:初始化 ==========
|
|
|
if err := vbase.Auth.Init(); err != nil {
|
|
|
log.Fatal("权限初始化失败:", err)
|
|
|
}
|
|
|
|
|
|
r := vigo.NewRouter()
|
|
|
|
|
|
// 挂载 VBase
|
|
|
r.Extend("/api/vb", api.Router)
|
|
|
|
|
|
// ========== 阶段3:配置路由 ==========
|
|
|
|
|
|
// CRM 路由
|
|
|
crm := r.SubRouter("/api/crm")
|
|
|
crm.Use(middleware.AuthRequired())
|
|
|
crm.Use(middleware.OrgContext())
|
|
|
|
|
|
crm.Get("/customers", crmAuth.Perm("customer", "list"), "客户列表", listCustomers)
|
|
|
crm.Post("/customers", crmAuth.Perm("customer", "create"), "创建客户", createCustomer)
|
|
|
crm.Get("/customers/{id}", crmAuth.Perm("customer", "read"), "客户详情", getCustomer)
|
|
|
crm.Patch("/customers/{id}", crmAuth.PermWithOwner("customer", "update", "owner_id"), "更新客户", updateCustomer)
|
|
|
crm.Delete("/customers/{id}", crmAuth.Perm("customer", "delete"), "删除客户", deleteCustomer)
|
|
|
|
|
|
// 合同路由(独立模块)
|
|
|
contract := r.SubRouter("/api/contract")
|
|
|
contract.Use(middleware.AuthRequired())
|
|
|
contract.Use(middleware.OrgContext())
|
|
|
|
|
|
contract.Get("/contracts", contractAuth.Perm("contract", "list"), "合同列表", listContracts)
|
|
|
contract.Post("/contracts", contractAuth.Perm("contract", "create"), "创建合同", createContract)
|
|
|
contract.Post("/contracts/{id}/audit", contractAuth.Perm("contract", "audit"), "审核合同", auditContract)
|
|
|
contract.Post("/contracts/{id}/sign", contractAuth.Perm("contract", "sign"), "签署合同", signContract)
|
|
|
|
|
|
vigo.Run(r)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
## 6. API 参考
|
|
|
|
|
|
### 6.1 全局函数
|
|
|
|
|
|
```go
|
|
|
// 创建权限管理实例
|
|
|
func (f *authFactory) New(appKey string, config AppConfig) Auth
|
|
|
|
|
|
// 初始化所有权限配置
|
|
|
func (f *authFactory) Init() error
|
|
|
```
|
|
|
|
|
|
### 6.2 Auth 接口
|
|
|
|
|
|
```go
|
|
|
// ========== 中间件 ==========
|
|
|
Perm(resource, action string) func(*vigo.X) error
|
|
|
PermWithOwner(resource, action, ownerKey string) func(*vigo.X) error
|
|
|
PermAny(permissions [][2]string) func(*vigo.X) error
|
|
|
PermAll(permissions [][2]string) func(*vigo.X) error
|
|
|
|
|
|
// ========== 授权管理 ==========
|
|
|
GrantRole(ctx context.Context, req GrantRoleRequest) error
|
|
|
RevokeRole(ctx context.Context, userID, orgID, roleCode string) error
|
|
|
GrantResourcePerm(ctx context.Context, req GrantResourcePermRequest) error
|
|
|
RevokeResourcePerm(ctx context.Context, userID, orgID, resource, resourceID string) error
|
|
|
RevokeAll(ctx context.Context, userID, orgID string) error
|
|
|
|
|
|
// ========== 权限查询 ==========
|
|
|
CheckPermission(ctx context.Context, req CheckPermRequest) (bool, error)
|
|
|
ListUserPermissions(ctx context.Context, userID, orgID string) ([]UserPermission, error)
|
|
|
ListResourceUsers(ctx context.Context, orgID, resource, resourceID string) ([]ResourceUser, error)
|
|
|
```
|
|
|
|
|
|
### 6.3 请求/响应结构
|
|
|
|
|
|
```go
|
|
|
type GrantRoleRequest struct {
|
|
|
UserID string // 用户ID
|
|
|
OrgID string // 组织ID
|
|
|
Role string // 角色代码
|
|
|
}
|
|
|
|
|
|
type GrantResourcePermRequest struct {
|
|
|
UserID string // 用户ID
|
|
|
OrgID string // 组织ID
|
|
|
Resource string // 资源类型
|
|
|
ResourceID string // 资源实例ID,"*" 表示所有
|
|
|
Action string // 操作类型,"*" 表示所有
|
|
|
ExpireAt time.Time // 过期时间(可选)
|
|
|
}
|
|
|
|
|
|
type CheckPermRequest struct {
|
|
|
UserID string // 用户ID
|
|
|
OrgID string // 组织ID
|
|
|
Resource string // 资源类型
|
|
|
ResourceID string // 资源实例ID(可选)
|
|
|
Action string // 操作类型
|
|
|
}
|
|
|
|
|
|
type UserPermission struct {
|
|
|
Resource string // 资源类型
|
|
|
ResourceID string // 资源ID
|
|
|
Actions []string // 允许的操作
|
|
|
}
|
|
|
|
|
|
type ResourceUser struct {
|
|
|
UserID string // 用户ID
|
|
|
Actions []string // 允许的操作
|
|
|
}
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
**确认此设计后,我将实现底层代码。**
|