Appearance
ruoyi dynamodb adapter
RuoYi 现有数据结构迁移到 DynamoDB 适配
1. 用户表 sys_user → User Entity
| RuoYi字段 | 类型 | DynamoDB属性名 | 说明 |
|---|---|---|---|
| user_id | bigint | UserID | 主键,PK: USER# |
| tenant_id | varchar(20) | ClientID | 多租户隔离 |
| dept_id | bigint | DeptID | 部门ID |
| user_name | varchar(50) | UserName | 登录账号 |
| nick_name | varchar(30) | NickName | 昵称 |
| user_type | varchar(20) | UserType | 用户类型 |
| varchar(50) | 邮箱,GSI3: EMAIL# | ||
| phonenumber | varchar(11) | PhoneNumber | 手机号 |
| sex | char | Sex | 性别 |
| avatar | bigint | Avatar | 头像 |
| password | varchar(100) | PasswordHash | 密码Hash |
| status | char | Status | 状态 |
| del_flag | char | DelFlag | 删除标志 |
| login_ip | varchar(128) | LoginIP | 最后登录IP |
| login_date | datetime | LastLoginAt | 最后登录时间 |
| create_dept | bigint | CreateDept | 创建部门 |
| create_by | bigint | CreateBy | 创建者 |
| create_time | datetime | CreatedAt | 创建时间 |
| update_by | bigint | UpdateBy | 更新者 |
| update_time | datetime | UpdatedAt | 更新时间 |
| remark | varchar(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_id | bigint | RoleID | 主键,SK: ROLE# |
| tenant_id | varchar(20) | ClientID | 多租户隔离,PK: CLIENT# |
| role_name | varchar(30) | Name | 角色名 |
| role_key | varchar(100) | Key | 权限字符串 |
| role_sort | int | Sort | 排序 |
| data_scope | char | DataScope | 数据范围 |
| status | char | Status | 状态 |
| del_flag | char | DelFlag | 删除标志 |
| create_dept | bigint | CreateDept | 创建部门 |
| create_by | bigint | CreatedBy | 创建者 |
| create_time | datetime | CreatedAt | 创建时间 |
| update_by | bigint | UpdatedBy | 更新者 |
| update_time | datetime | UpdatedAt | 更新时间 |
| remark | varchar(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_id | bigint | PermissionID | 主键,SK: PERMISSION# |
| menu_name | varchar(50) | Name | 菜单/权限名 |
| perms | varchar(100) | Perms | 权限标识 |
| menu_type | char | Type | 类型(M/C/F) |
| status | char | Status | 状态 |
| parent_id | bigint | ParentID | 父菜单ID |
| path | varchar(200) | Path | 路由地址 |
| component | varchar(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_id | bigint | UserID | PK: USER# |
| role_id | bigint | RoleID | SK: ROLE#{scope_type}#{scope_id}# |
| scope_type | string | ScopeType | 作用域类型(client/project/building) |
| scope_id | string | ScopeID | 作用域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"`
// 可扩展更多字段
}5. 角色-权限关系 sys_role_menu → RolePermissionLink (如不嵌入Role)
| RuoYi字段 | 类型 | DynamoDB属性名 | 说明 |
|---|---|---|---|
| role_id | bigint | RoleID | PK: ROLE# |
| menu_id | bigint | PermissionID | SK: 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_id | varchar(20) | ClientID | PK: CLIENT# |
| company_name | varchar(50) | CompanyName | 企业名称 |
| contact_user_name | varchar(20) | ContactUserName | 联系人 |
| contact_phone | varchar(20) | ContactPhone | 联系电话 |
| status | char | Status | 状态 |
| expire_time | datetime | ExpireTime | 到期时间 |
| ... | ... | ... | ... |
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_id | bigint | DeptID | PK: DEPT# |
| tenant_id | varchar(20) | ClientID | 多租户 |
| parent_id | bigint | ParentID | 父部门 |
| dept_name | varchar(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_id | bigint | PostID | PK: POST# |
| tenant_id | varchar(20) | ClientID | 多租户 |
| post_code | varchar(64) | Code | 岗位编码 |
| post_name | varchar(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})
查询模式支持
- 点查询: 根据实体ID直接获取
- 租户查询: 获取特定租户下的所有实体
- 关系查询: 用户-角色、角色-权限等关系
- 层级查询: 支持多层级作用域的角色分配
- 属性查询: 根据邮箱、状态等属性查询
以上查询操作完整覆盖了RuoYi系统的核心功能需求,支持多租户隔离、RBAC权限控制、层级管理等特性。所有操作都遵循DynamoDB最佳实践,确保高性能和可扩展性。