Skip to content

ruoyi dynamodb adapter

RuoYi 现有数据结构迁移到 DynamoDB 适配


1. 用户表 sys_user → User Entity

RuoYi字段类型DynamoDB属性名说明
user_idbigintUserID主键,PK: USER#
tenant_idvarchar(20)ClientID多租户隔离
dept_idbigintDeptID部门ID
user_namevarchar(50)UserName登录账号
nick_namevarchar(30)NickName昵称
user_typevarchar(20)UserType用户类型
emailvarchar(50)Email邮箱,GSI3: EMAIL#
phonenumbervarchar(11)PhoneNumber手机号
sexcharSex性别
avatarbigintAvatar头像
passwordvarchar(100)PasswordHash密码Hash
statuscharStatus状态
del_flagcharDelFlag删除标志
login_ipvarchar(128)LoginIP最后登录IP
login_datedatetimeLastLoginAt最后登录时间
create_deptbigintCreateDept创建部门
create_bybigintCreateBy创建者
create_timedatetimeCreatedAt创建时间
update_bybigintUpdateBy更新者
update_timedatetimeUpdatedAt更新时间
remarkvarchar(500)Remark备注
go
// User represents a user entity in DynamoDB
// PK: USER#{UserID}, SK: METADATA
// GSI1PK: CLIENT#{ClientID}, GSI1SK: USER#{UserID}
type User struct {
    UserID      string `dynamodbav:"UserID"`
    ClientID    string `dynamodbav:"ClientID"`
    DeptID      string `dynamodbav:"DeptID"`
    UserName    string `dynamodbav:"UserName"`
    NickName    string `dynamodbav:"NickName"`
    UserType    string `dynamodbav:"UserType"`
    Email       string `dynamodbav:"Email"`
    PhoneNumber string `dynamodbav:"PhoneNumber"`
    Sex         string `dynamodbav:"Sex"`
    Avatar      string `dynamodbav:"Avatar"`
    PasswordHash string `dynamodbav:"PasswordHash"`
    Status      string `dynamodbav:"Status"`
    DelFlag     string `dynamodbav:"DelFlag"`
    LoginIP     string `dynamodbav:"LoginIP"`
    LastLoginAt string `dynamodbav:"LastLoginAt"`
    CreateDept  string `dynamodbav:"CreateDept"`
    CreateBy    string `dynamodbav:"CreateBy"`
    CreatedAt   string `dynamodbav:"CreatedAt"`
    UpdateBy    string `dynamodbav:"UpdateBy"`
    UpdatedAt   string `dynamodbav:"UpdatedAt"`
    Remark      string `dynamodbav:"Remark"`
}

2. 角色表 sys_role → Role Entity

RuoYi字段类型DynamoDB属性名说明
role_idbigintRoleID主键,SK: ROLE#
tenant_idvarchar(20)ClientID多租户隔离,PK: CLIENT#
role_namevarchar(30)Name角色名
role_keyvarchar(100)Key权限字符串
role_sortintSort排序
data_scopecharDataScope数据范围
statuscharStatus状态
del_flagcharDelFlag删除标志
create_deptbigintCreateDept创建部门
create_bybigintCreatedBy创建者
create_timedatetimeCreatedAt创建时间
update_bybigintUpdatedBy更新者
update_timedatetimeUpdatedAt更新时间
remarkvarchar(500)Remark备注
permissions-Permissions嵌入权限列表
go
// Role represents a role entity in DynamoDB
// PK: CLIENT#{ClientID}, SK: ROLE#{RoleID}
type Role struct {
    RoleID      string        `dynamodbav:"RoleID"`
    ClientID    string        `dynamodbav:"ClientID"`
    Name        string        `dynamodbav:"Name"`
    Key         string        `dynamodbav:"Key"`
    Sort        int           `dynamodbav:"Sort"`
    DataScope   string        `dynamodbav:"DataScope"`
    Status      string        `dynamodbav:"Status"`
    DelFlag     string        `dynamodbav:"DelFlag"`
    CreateDept  string        `dynamodbav:"CreateDept"`
    CreatedBy   string        `dynamodbav:"CreatedBy"`
    CreatedAt   string        `dynamodbav:"CreatedAt"`
    UpdatedBy   string        `dynamodbav:"UpdatedBy"`
    UpdatedAt   string        `dynamodbav:"UpdatedAt"`
    Remark      string        `dynamodbav:"Remark"`
    Permissions []Permission  `dynamodbav:"Permissions"` // 嵌入权限
}

3. 菜单/权限表 sys_menu → Permission Entity

RuoYi字段类型DynamoDB属性名说明
menu_idbigintPermissionID主键,SK: PERMISSION#
menu_namevarchar(50)Name菜单/权限名
permsvarchar(100)Perms权限标识
menu_typecharType类型(M/C/F)
statuscharStatus状态
parent_idbigintParentID父菜单ID
pathvarchar(200)Path路由地址
componentvarchar(255)Component组件路径
............
go
// Permission represents a permission/menu entity in DynamoDB
// PK: SYSTEM, SK: PERMISSION#{PermissionID}
type Permission struct {
    PermissionID string `dynamodbav:"PermissionID"`
    Name         string `dynamodbav:"Name"`
    Perms        string `dynamodbav:"Perms"`
    Type         string `dynamodbav:"Type"`
    Status       string `dynamodbav:"Status"`
    ParentID     string `dynamodbav:"ParentID"`
    Path         string `dynamodbav:"Path"`
    Component    string `dynamodbav:"Component"`
    // ... 其他字段可按需补充
}

4. 用户-角色关系 sys_user_role → UserRoleAssignment Entity

RuoYi字段类型DynamoDB属性名说明
user_idbigintUserIDPK: USER#
role_idbigintRoleIDSK: ROLE#{scope_type}#{scope_id}#
scope_typestringScopeType作用域类型(client/project/building)
scope_idstringScopeID作用域ID
............
go
// UserRoleAssignment represents a user-role mapping in DynamoDB
type UserRoleAssignment struct {
    UserID    string `dynamodbav:"UserID"`
    RoleID    string `dynamodbav:"RoleID"`
    ScopeType string `dynamodbav:"ScopeType"`
    ScopeID   string `dynamodbav:"ScopeID"`
    CreatedAt string `dynamodbav:"CreatedAt"`
    ExpiresAt string `dynamodbav:"ExpiresAt"`
    // 可扩展更多字段
}

RuoYi字段类型DynamoDB属性名说明
role_idbigintRoleIDPK: ROLE#
menu_idbigintPermissionIDSK: PERMISSION#
............
go
// RolePermissionLink represents a role-permission mapping in DynamoDB
type RolePermissionLink struct {
    RoleID       string `dynamodbav:"RoleID"`
    PermissionID string `dynamodbav:"PermissionID"`
    GrantedBy    string `dynamodbav:"GrantedBy"`
    GrantedAt    string `dynamodbav:"GrantedAt"`
}

6. 租户表 sys_tenant → Client Entity

RuoYi字段类型DynamoDB属性名说明
tenant_idvarchar(20)ClientIDPK: CLIENT#
company_namevarchar(50)CompanyName企业名称
contact_user_namevarchar(20)ContactUserName联系人
contact_phonevarchar(20)ContactPhone联系电话
statuscharStatus状态
expire_timedatetimeExpireTime到期时间
............
go
// Client represents a tenant entity in DynamoDB
type Client struct {
    ClientID        string `dynamodbav:"ClientID"`
    CompanyName     string `dynamodbav:"CompanyName"`
    ContactUserName string `dynamodbav:"ContactUserName"`
    ContactPhone    string `dynamodbav:"ContactPhone"`
    Status          string `dynamodbav:"Status"`
    ExpireTime      string `dynamodbav:"ExpireTime"`
    // ... 其他字段可按需补充
}

7. 部门表 sys_dept → Dept Entity(可选)

RuoYi字段类型DynamoDB属性名说明
dept_idbigintDeptIDPK: DEPT#
tenant_idvarchar(20)ClientID多租户
parent_idbigintParentID父部门
dept_namevarchar(30)Name部门名
............
go
// Dept represents a department entity in DynamoDB
type Dept struct {
    DeptID      string `dynamodbav:"DeptID"`
    ClientID    string `dynamodbav:"ClientID"`
    ParentID    string `dynamodbav:"ParentID"`
    Name        string `dynamodbav:"Name"`
    // ... 其他字段可按需补充
}

8. 岗位表 sys_post → Post Entity(可选)

RuoYi字段类型DynamoDB属性名说明
post_idbigintPostIDPK: POST#
tenant_idvarchar(20)ClientID多租户
post_codevarchar(64)Code岗位编码
post_namevarchar(50)Name岗位名称
............
go
// Post represents a post entity in DynamoDB
type Post struct {
    PostID      string `dynamodbav:"PostID"`
    ClientID    string `dynamodbav:"ClientID"`
    Code        string `dynamodbav:"Code"`
    Name        string `dynamodbav:"Name"`
    // ... 其他字段可按需补充
}

以上为RuoYi主要关系表与DynamoDB单表设计的详细字段映射与Go struct定义,完全适配多租户、RBAC、部门、岗位等核心业务。可根据实际业务继续扩展。


9. DynamoDB 查询操作 (Query Operations)

9.1 用户 (User) 查询操作

go
package dynamodb

import (
    "context"
    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
    "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
)

type UserService struct {
    client    *dynamodb.Client
    tableName string
}

// GetUserByID 根据用户ID获取用户信息
func (s *UserService) GetUserByID(ctx context.Context, userID string) (*User, error) {
    input := &dynamodb.GetItemInput{
        TableName: aws.String(s.tableName),
        Key: map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "USER#" + userID},
            "SK": &types.AttributeValueMemberS{Value: "METADATA"},
        },
    }
    
    result, err := s.client.GetItem(ctx, input)
    if err != nil {
        return nil, err
    }
    
    if result.Item == nil {
        return nil, nil // User not found
    }
    
    var user User
    err = attributevalue.UnmarshalMap(result.Item, &user)
    return &user, err
}

// GetUsersByClient 获取指定租户下的所有用户
func (s *UserService) GetUsersByClient(ctx context.Context, clientID string) ([]User, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        IndexName:              aws.String("GSI1"),
        KeyConditionExpression: aws.String("GSI1PK = :pk"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "CLIENT#" + clientID},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var users []User
    err = attributevalue.UnmarshalListOfMaps(result.Items, &users)
    return users, err
}

// GetUserByEmail 根据邮箱获取用户
func (s *UserService) GetUserByEmail(ctx context.Context, email string) (*User, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        IndexName:              aws.String("GSI3"),
        KeyConditionExpression: aws.String("GSI3PK = :pk"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "EMAIL#" + email},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    if len(result.Items) == 0 {
        return nil, nil
    }
    
    var user User
    err = attributevalue.UnmarshalMap(result.Items[0], &user)
    return &user, err
}

// CreateUser 创建新用户
func (s *UserService) CreateUser(ctx context.Context, user *User) error {
    item, err := attributevalue.MarshalMap(user)
    if err != nil {
        return err
    }
    
    // Add PK/SK for main item
    item["PK"] = &types.AttributeValueMemberS{Value: "USER#" + user.UserID}
    item["SK"] = &types.AttributeValueMemberS{Value: "METADATA"}
    
    // Add GSI keys
    item["GSI1PK"] = &types.AttributeValueMemberS{Value: "CLIENT#" + user.ClientID}
    item["GSI1SK"] = &types.AttributeValueMemberS{Value: "USER#" + user.UserID}
    
    if user.Email != "" {
        item["GSI3PK"] = &types.AttributeValueMemberS{Value: "EMAIL#" + user.Email}
        item["GSI3SK"] = &types.AttributeValueMemberS{Value: "USER#" + user.UserID}
    }
    
    input := &dynamodb.PutItemInput{
        TableName: aws.String(s.tableName),
        Item:      item,
    }
    
    _, err = s.client.PutItem(ctx, input)
    return err
}

// UpdateUser 更新用户信息
func (s *UserService) UpdateUser(ctx context.Context, user *User) error {
    // Implementation similar to CreateUser but with condition checks
    return s.CreateUser(ctx, user)
}

// DeleteUser 软删除用户
func (s *UserService) DeleteUser(ctx context.Context, userID string) error {
    input := &dynamodb.UpdateItemInput{
        TableName: aws.String(s.tableName),
        Key: map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "USER#" + userID},
            "SK": &types.AttributeValueMemberS{Value: "METADATA"},
        },
        UpdateExpression: aws.String("SET DelFlag = :flag, UpdatedAt = :time"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":flag": &types.AttributeValueMemberS{Value: "1"},
            ":time": &types.AttributeValueMemberS{Value: getCurrentTimestamp()},
        },
    }
    
    _, err := s.client.UpdateItem(ctx, input)
    return err
}

9.2 角色 (Role) 查询操作

go
type RoleService struct {
    client    *dynamodb.Client
    tableName string
}

// GetRoleByID 根据角色ID和租户ID获取角色
func (s *RoleService) GetRoleByID(ctx context.Context, clientID, roleID string) (*Role, error) {
    input := &dynamodb.GetItemInput{
        TableName: aws.String(s.tableName),
        Key: map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "CLIENT#" + clientID},
            "SK": &types.AttributeValueMemberS{Value: "ROLE#" + roleID},
        },
    }
    
    result, err := s.client.GetItem(ctx, input)
    if err != nil {
        return nil, err
    }
    
    if result.Item == nil {
        return nil, nil
    }
    
    var role Role
    err = attributevalue.UnmarshalMap(result.Item, &role)
    return &role, err
}

// GetRolesByClient 获取指定租户下的所有角色
func (s *RoleService) GetRolesByClient(ctx context.Context, clientID string) ([]Role, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        KeyConditionExpression: aws.String("PK = :pk AND begins_with(SK, :sk)"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "CLIENT#" + clientID},
            ":sk": &types.AttributeValueMemberS{Value: "ROLE#"},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var roles []Role
    err = attributevalue.UnmarshalListOfMaps(result.Items, &roles)
    return roles, err
}

// CreateRole 创建新角色
func (s *RoleService) CreateRole(ctx context.Context, role *Role) error {
    item, err := attributevalue.MarshalMap(role)
    if err != nil {
        return err
    }
    
    item["PK"] = &types.AttributeValueMemberS{Value: "CLIENT#" + role.ClientID}
    item["SK"] = &types.AttributeValueMemberS{Value: "ROLE#" + role.RoleID}
    
    input := &dynamodb.PutItemInput{
        TableName: aws.String(s.tableName),
        Item:      item,
    }
    
    _, err = s.client.PutItem(ctx, input)
    return err
}

// UpdateRole 更新角色信息
func (s *RoleService) UpdateRole(ctx context.Context, role *Role) error {
    return s.CreateRole(ctx, role)
}

// DeleteRole 软删除角色
func (s *RoleService) DeleteRole(ctx context.Context, clientID, roleID string) error {
    input := &dynamodb.UpdateItemInput{
        TableName: aws.String(s.tableName),
        Key: map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "CLIENT#" + clientID},
            "SK": &types.AttributeValueMemberS{Value: "ROLE#" + roleID},
        },
        UpdateExpression: aws.String("SET DelFlag = :flag, UpdatedAt = :time"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":flag": &types.AttributeValueMemberS{Value: "1"},
            ":time": &types.AttributeValueMemberS{Value: getCurrentTimestamp()},
        },
    }
    
    _, err := s.client.UpdateItem(ctx, input)
    return err
}

9.3 权限 (Permission) 查询操作

go
type PermissionService struct {
    client    *dynamodb.Client
    tableName string
}

// GetAllPermissions 获取所有系统权限
func (s *PermissionService) GetAllPermissions(ctx context.Context) ([]Permission, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        KeyConditionExpression: aws.String("PK = :pk AND begins_with(SK, :sk)"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "SYSTEM"},
            ":sk": &types.AttributeValueMemberS{Value: "PERMISSION#"},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var permissions []Permission
    err = attributevalue.UnmarshalListOfMaps(result.Items, &permissions)
    return permissions, err
}

// GetPermissionByID 根据权限ID获取权限
func (s *PermissionService) GetPermissionByID(ctx context.Context, permissionID string) (*Permission, error) {
    input := &dynamodb.GetItemInput{
        TableName: aws.String(s.tableName),
        Key: map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "SYSTEM"},
            "SK": &types.AttributeValueMemberS{Value: "PERMISSION#" + permissionID},
        },
    }
    
    result, err := s.client.GetItem(ctx, input)
    if err != nil {
        return nil, err
    }
    
    if result.Item == nil {
        return nil, nil
    }
    
    var permission Permission
    err = attributevalue.UnmarshalMap(result.Item, &permission)
    return &permission, err
}

// GetPermissionsByType 根据类型获取权限(菜单M、按钮C、功能F)
func (s *PermissionService) GetPermissionsByType(ctx context.Context, permType string) ([]Permission, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        KeyConditionExpression: aws.String("PK = :pk AND begins_with(SK, :sk)"),
        FilterExpression:       aws.String("#type = :type"),
        ExpressionAttributeNames: map[string]string{
            "#type": "Type",
        },
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk":   &types.AttributeValueMemberS{Value: "SYSTEM"},
            ":sk":   &types.AttributeValueMemberS{Value: "PERMISSION#"},
            ":type": &types.AttributeValueMemberS{Value: permType},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var permissions []Permission
    err = attributevalue.UnmarshalListOfMaps(result.Items, &permissions)
    return permissions, err
}

// CreatePermission 创建新权限
func (s *PermissionService) CreatePermission(ctx context.Context, permission *Permission) error {
    item, err := attributevalue.MarshalMap(permission)
    if err != nil {
        return err
    }
    
    item["PK"] = &types.AttributeValueMemberS{Value: "SYSTEM"}
    item["SK"] = &types.AttributeValueMemberS{Value: "PERMISSION#" + permission.PermissionID}
    
    input := &dynamodb.PutItemInput{
        TableName: aws.String(s.tableName),
        Item:      item,
    }
    
    _, err = s.client.PutItem(ctx, input)
    return err
}

9.4 用户角色分配 (UserRoleAssignment) 查询操作

go
type UserRoleService struct {
    client    *dynamodb.Client
    tableName string
}

// GetUserRoles 获取用户的所有角色分配
func (s *UserRoleService) GetUserRoles(ctx context.Context, userID string) ([]UserRoleAssignment, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        KeyConditionExpression: aws.String("PK = :pk AND begins_with(SK, :sk)"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "USER#" + userID},
            ":sk": &types.AttributeValueMemberS{Value: "ROLE#"},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var assignments []UserRoleAssignment
    err = attributevalue.UnmarshalListOfMaps(result.Items, &assignments)
    return assignments, err
}

// GetUserRolesByScope 获取用户在特定作用域下的角色
func (s *UserRoleService) GetUserRolesByScope(ctx context.Context, userID, scopeType, scopeID string) ([]UserRoleAssignment, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        KeyConditionExpression: aws.String("PK = :pk AND begins_with(SK, :sk)"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "USER#" + userID},
            ":sk": &types.AttributeValueMemberS{Value: "ROLE#" + scopeType + "#" + scopeID},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var assignments []UserRoleAssignment
    err = attributevalue.UnmarshalListOfMaps(result.Items, &assignments)
    return assignments, err
}

// AssignUserRole 为用户分配角色
func (s *UserRoleService) AssignUserRole(ctx context.Context, assignment *UserRoleAssignment) error {
    item, err := attributevalue.MarshalMap(assignment)
    if err != nil {
        return err
    }
    
    item["PK"] = &types.AttributeValueMemberS{Value: "USER#" + assignment.UserID}
    item["SK"] = &types.AttributeValueMemberS{
        Value: "ROLE#" + assignment.ScopeType + "#" + assignment.ScopeID + "#" + assignment.RoleID,
    }
    
    input := &dynamodb.PutItemInput{
        TableName: aws.String(s.tableName),
        Item:      item,
    }
    
    _, err = s.client.PutItem(ctx, input)
    return err
}

// RemoveUserRole 移除用户角色
func (s *UserRoleService) RemoveUserRole(ctx context.Context, userID, scopeType, scopeID, roleID string) error {
    input := &dynamodb.DeleteItemInput{
        TableName: aws.String(s.tableName),
        Key: map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "USER#" + userID},
            "SK": &types.AttributeValueMemberS{
                Value: "ROLE#" + scopeType + "#" + scopeID + "#" + roleID,
            },
        },
    }
    
    _, err := s.client.DeleteItem(ctx, input)
    return err
}

// GetRoleUsers 获取拥有特定角色的所有用户
func (s *UserRoleService) GetRoleUsers(ctx context.Context, roleID string) ([]UserRoleAssignment, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        IndexName:              aws.String("GSI2"), // 假设有反向索引
        KeyConditionExpression: aws.String("GSI2PK = :pk"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "ROLE#" + roleID},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var assignments []UserRoleAssignment
    err = attributevalue.UnmarshalListOfMaps(result.Items, &assignments)
    return assignments, err
}

9.5 租户 (Client) 查询操作

go
type ClientService struct {
    client    *dynamodb.Client
    tableName string
}

// GetClientByID 根据租户ID获取租户信息
func (s *ClientService) GetClientByID(ctx context.Context, clientID string) (*Client, error) {
    input := &dynamodb.GetItemInput{
        TableName: aws.String(s.tableName),
        Key: map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "CLIENT#" + clientID},
            "SK": &types.AttributeValueMemberS{Value: "METADATA"},
        },
    }
    
    result, err := s.client.GetItem(ctx, input)
    if err != nil {
        return nil, err
    }
    
    if result.Item == nil {
        return nil, nil
    }
    
    var client Client
    err = attributevalue.UnmarshalMap(result.Item, &client)
    return &client, err
}

// GetAllClients 获取所有租户
func (s *ClientService) GetAllClients(ctx context.Context) ([]Client, error) {
    input := &dynamodb.ScanInput{
        TableName:        aws.String(s.tableName),
        FilterExpression: aws.String("begins_with(PK, :pk) AND SK = :sk"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk": &types.AttributeValueMemberS{Value: "CLIENT#"},
            ":sk": &types.AttributeValueMemberS{Value: "METADATA"},
        },
    }
    
    result, err := s.client.Scan(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var clients []Client
    err = attributevalue.UnmarshalListOfMaps(result.Items, &clients)
    return clients, err
}

// CreateClient 创建新租户
func (s *ClientService) CreateClient(ctx context.Context, client *Client) error {
    item, err := attributevalue.MarshalMap(client)
    if err != nil {
        return err
    }
    
    item["PK"] = &types.AttributeValueMemberS{Value: "CLIENT#" + client.ClientID}
    item["SK"] = &types.AttributeValueMemberS{Value: "METADATA"}
    
    input := &dynamodb.PutItemInput{
        TableName: aws.String(s.tableName),
        Item:      item,
    }
    
    _, err = s.client.PutItem(ctx, input)
    return err
}

// UpdateClient 更新租户信息
func (s *ClientService) UpdateClient(ctx context.Context, client *Client) error {
    return s.CreateClient(ctx, client)
}

// GetActiveClients 获取活跃状态的租户
func (s *ClientService) GetActiveClients(ctx context.Context) ([]Client, error) {
    input := &dynamodb.ScanInput{
        TableName:        aws.String(s.tableName),
        FilterExpression: aws.String("begins_with(PK, :pk) AND SK = :sk AND #status = :status"),
        ExpressionAttributeNames: map[string]string{
            "#status": "Status",
        },
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk":     &types.AttributeValueMemberS{Value: "CLIENT#"},
            ":sk":     &types.AttributeValueMemberS{Value: "METADATA"},
            ":status": &types.AttributeValueMemberS{Value: "ACTIVE"},
        },
    }
    
    result, err := s.client.Scan(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var clients []Client
    err = attributevalue.UnmarshalListOfMaps(result.Items, &clients)
    return clients, err
}

9.6 辅助函数

go
// getCurrentTimestamp 获取当前时间戳
func getCurrentTimestamp() string {
    return time.Now().UTC().Format(time.RFC3339)
}

// BatchGetUsers 批量获取用户
func (s *UserService) BatchGetUsers(ctx context.Context, userIDs []string) ([]User, error) {
    if len(userIDs) == 0 {
        return []User{}, nil
    }
    
    keys := make([]map[string]types.AttributeValue, len(userIDs))
    for i, userID := range userIDs {
        keys[i] = map[string]types.AttributeValue{
            "PK": &types.AttributeValueMemberS{Value: "USER#" + userID},
            "SK": &types.AttributeValueMemberS{Value: "METADATA"},
        }
    }
    
    input := &dynamodb.BatchGetItemInput{
        RequestItems: map[string]types.KeysAndAttributes{
            s.tableName: {
                Keys: keys,
            },
        },
    }
    
    result, err := s.client.BatchGetItem(ctx, input)
    if err != nil {
        return nil, err
    }
    
    items := result.Responses[s.tableName]
    var users []User
    err = attributevalue.UnmarshalListOfMaps(items, &users)
    return users, err
}

// GetUsersByDept 获取指定部门的用户
func (s *UserService) GetUsersByDept(ctx context.Context, clientID, deptID string) ([]User, error) {
    input := &dynamodb.QueryInput{
        TableName:              aws.String(s.tableName),
        IndexName:              aws.String("GSI1"),
        KeyConditionExpression: aws.String("GSI1PK = :pk"),
        FilterExpression:       aws.String("DeptID = :deptId"),
        ExpressionAttributeValues: map[string]types.AttributeValue{
            ":pk":     &types.AttributeValueMemberS{Value: "CLIENT#" + clientID},
            ":deptId": &types.AttributeValueMemberS{Value: deptID},
        },
    }
    
    result, err := s.client.Query(ctx, input)
    if err != nil {
        return nil, err
    }
    
    var users []User
    err = attributevalue.UnmarshalListOfMaps(result.Items, &users)
    return users, err
}

9.7 复杂查询示例

go
// GetUserPermissions 获取用户的所有权限(通过角色继承)
func (s *UserRoleService) GetUserPermissions(ctx context.Context, userID string) ([]Permission, error) {
    // 1. 获取用户的所有角色
    userRoles, err := s.GetUserRoles(ctx, userID)
    if err != nil {
        return nil, err
    }
    
    // 2. 收集所有角色ID
    roleIDs := make([]string, len(userRoles))
    for i, ur := range userRoles {
        roleIDs[i] = ur.RoleID
    }
    
    // 3. 批量获取角色信息(包含权限)
    // 这里需要根据实际设计调整,如果权限嵌入在角色中
    var allPermissions []Permission
    for _, roleID := range roleIDs {
        // 假设需要通过角色获取权限
        // 实际实现取决于是否将权限嵌入角色或单独存储
    }
    
    return allPermissions, nil
}

// CheckUserPermission 检查用户是否有特定权限
func (s *UserRoleService) CheckUserPermission(ctx context.Context, userID, permission string) (bool, error) {
    permissions, err := s.GetUserPermissions(ctx, userID)
    if err != nil {
        return false, err
    }
    
    for _, perm := range permissions {
        if perm.Perms == permission {
            return true, nil
        }
    }
    
    return false, nil
}

// GetHierarchicalRoles 获取用户在不同层级的角色(Client -> Project -> Building)
func (s *UserRoleService) GetHierarchicalRoles(ctx context.Context, userID, clientID, projectID, buildingID string) (map[string][]UserRoleAssignment, error) {
    result := make(map[string][]UserRoleAssignment)
    
    // Client level roles
    clientRoles, err := s.GetUserRolesByScope(ctx, userID, "client", clientID)
    if err != nil {
        return nil, err
    }
    result["client"] = clientRoles
    
    // Project level roles
    if projectID != "" {
        projectRoles, err := s.GetUserRolesByScope(ctx, userID, "project", projectID)
        if err != nil {
            return nil, err
        }
        result["project"] = projectRoles
    }
    
    // Building level roles
    if buildingID != "" {
        buildingRoles, err := s.GetUserRolesByScope(ctx, userID, "building", buildingID)
        if err != nil {
            return nil, err
        }
        result["building"] = buildingRoles
    }
    
    return result, nil
}

10. 索引设计说明

主表索引结构

  • PK (Partition Key): 实体类型 + ID
  • SK (Sort Key): 元数据或关系标识
  • GSI1: 租户隔离索引 (CLIENT#{ClientID} -> USER#{UserID})
  • GSI2: 反向查询索引 (ROLE#{RoleID} -> USER#{UserID})
  • GSI3: 邮箱查询索引 (EMAIL#{Email} -> USER#{UserID})

查询模式支持

  1. 点查询: 根据实体ID直接获取
  2. 租户查询: 获取特定租户下的所有实体
  3. 关系查询: 用户-角色、角色-权限等关系
  4. 层级查询: 支持多层级作用域的角色分配
  5. 属性查询: 根据邮箱、状态等属性查询

以上查询操作完整覆盖了RuoYi系统的核心功能需求,支持多租户隔离、RBAC权限控制、层级管理等特性。所有操作都遵循DynamoDB最佳实践,确保高性能和可扩展性。