Files
user-system/internal/repository/user_role.go
long-agent 09beb173cc feat: complete production readiness improvements
- Fix DIP violations in service layer (device, stats, auth middleware)
- Add ReplaceUserRoles interface method for transaction safety
- Implement Magic Bytes validation for avatar uploads
- Standardize OAuth error handling with ErrOAuthProviderNotSupported
- Use crypto/rand for JWT secret generation instead of weak fixed key
- Apply code formatting with gofumpt and goimports
- Fix staticcheck issues (S1024, S1008, ST1005)
- Add comprehensive quality and functional test reports
- Achieve 36.3% test coverage (up from 16.3%)
- All E2E, integration, and business logic tests passing
2026-04-12 16:15:32 +08:00

218 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package repository
import (
"context"
"gorm.io/gorm"
"github.com/user-management-system/internal/domain"
)
// UserRoleRepository 用户角色关联数据访问层
type UserRoleRepository struct {
db *gorm.DB
}
// NewUserRoleRepository 创建用户角色关联数据访问层
func NewUserRoleRepository(db *gorm.DB) *UserRoleRepository {
return &UserRoleRepository{db: db}
}
// DB returns the underlying GORM DB for transaction support
func (r *UserRoleRepository) DB() *gorm.DB {
return r.db
}
// WithTx returns a new repository instance that uses the given transaction
func (r *UserRoleRepository) WithTx(tx *gorm.DB) *UserRoleRepository {
return &UserRoleRepository{db: tx}
}
// Create 创建用户角色关联
func (r *UserRoleRepository) Create(ctx context.Context, userRole *domain.UserRole) error {
return r.db.WithContext(ctx).Create(userRole).Error
}
// Delete 删除用户角色关联
func (r *UserRoleRepository) Delete(ctx context.Context, id int64) error {
return r.db.WithContext(ctx).Delete(&domain.UserRole{}, id).Error
}
// DeleteByUserID 删除用户的所有角色
func (r *UserRoleRepository) DeleteByUserID(ctx context.Context, userID int64) error {
return r.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&domain.UserRole{}).Error
}
// DeleteByUserAndRole 删除指定用户和角色的关联
func (r *UserRoleRepository) DeleteByUserAndRole(ctx context.Context, userID, roleID int64) error {
return r.db.WithContext(ctx).Where("user_id = ? AND role_id = ?", userID, roleID).Delete(&domain.UserRole{}).Error
}
// DeleteByRoleID 删除角色的所有用户
func (r *UserRoleRepository) DeleteByRoleID(ctx context.Context, roleID int64) error {
return r.db.WithContext(ctx).Where("role_id = ?", roleID).Delete(&domain.UserRole{}).Error
}
// GetByUserID 根据用户ID获取角色列表
func (r *UserRoleRepository) GetByUserID(ctx context.Context, userID int64) ([]*domain.UserRole, error) {
var userRoles []*domain.UserRole
err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&userRoles).Error
if err != nil {
return nil, err
}
return userRoles, nil
}
// GetByRoleID 根据角色ID获取用户列表
func (r *UserRoleRepository) GetByRoleID(ctx context.Context, roleID int64) ([]*domain.UserRole, error) {
var userRoles []*domain.UserRole
err := r.db.WithContext(ctx).Where("role_id = ?", roleID).Find(&userRoles).Error
if err != nil {
return nil, err
}
return userRoles, nil
}
// GetRoleIDsByUserID 根据用户ID获取角色ID列表
func (r *UserRoleRepository) GetRoleIDsByUserID(ctx context.Context, userID int64) ([]int64, error) {
var roleIDs []int64
err := r.db.WithContext(ctx).Model(&domain.UserRole{}).Where("user_id = ?", userID).Pluck("role_id", &roleIDs).Error
if err != nil {
return nil, err
}
return roleIDs, nil
}
// GetUserRolesAndPermissions 获取用户角色和权限PERF-01 优化:合并为单次 JOIN 查询)
func (r *UserRoleRepository) GetUserRolesAndPermissions(ctx context.Context, userID int64) ([]*domain.Role, []*domain.Permission, error) {
var results []struct {
RoleID int64
RoleName string
RoleCode string
RoleStatus int
PermissionID int64
PermissionCode string
PermissionName string
}
// 使用 LEFT JOIN 一次性获取用户角色和权限
err := r.db.WithContext(ctx).
Raw(`
SELECT DISTINCT r.id as role_id, r.name as role_name, r.code as role_code, r.status as role_status,
p.id as permission_id, p.code as permission_code, p.name as permission_name
FROM user_roles ur
JOIN roles r ON ur.role_id = r.id
LEFT JOIN role_permissions rp ON r.id = rp.role_id
LEFT JOIN permissions p ON rp.permission_id = p.id
WHERE ur.user_id = ? AND r.status = 1
`, userID).
Scan(&results).Error
if err != nil {
return nil, nil, err
}
// 构建角色和权限列表
roleMap := make(map[int64]*domain.Role)
permMap := make(map[int64]*domain.Permission)
for _, row := range results {
if _, ok := roleMap[row.RoleID]; !ok {
roleMap[row.RoleID] = &domain.Role{
ID: row.RoleID,
Name: row.RoleName,
Code: row.RoleCode,
Status: domain.RoleStatus(row.RoleStatus),
}
}
if row.PermissionID > 0 {
if _, ok := permMap[row.PermissionID]; !ok {
permMap[row.PermissionID] = &domain.Permission{
ID: row.PermissionID,
Code: row.PermissionCode,
Name: row.PermissionName,
}
}
}
}
roles := make([]*domain.Role, 0, len(roleMap))
for _, role := range roleMap {
roles = append(roles, role)
}
perms := make([]*domain.Permission, 0, len(permMap))
for _, perm := range permMap {
perms = append(perms, perm)
}
return roles, perms, nil
}
// GetUserIDByRoleID 根据角色ID获取用户ID列表
func (r *UserRoleRepository) GetUserIDByRoleID(ctx context.Context, roleID int64) ([]int64, error) {
var userIDs []int64
err := r.db.WithContext(ctx).Model(&domain.UserRole{}).Where("role_id = ?", roleID).Pluck("user_id", &userIDs).Error
if err != nil {
return nil, err
}
return userIDs, nil
}
// Exists 检查用户角色关联是否存在
func (r *UserRoleRepository) Exists(ctx context.Context, userID, roleID int64) (bool, error) {
var count int64
err := r.db.WithContext(ctx).Model(&domain.UserRole{}).
Where("user_id = ? AND role_id = ?", userID, roleID).
Count(&count).Error
return count > 0, err
}
// BatchCreate 批量创建用户角色关联
func (r *UserRoleRepository) BatchCreate(ctx context.Context, userRoles []*domain.UserRole) error {
if len(userRoles) == 0 {
return nil
}
return r.db.WithContext(ctx).Create(&userRoles).Error
}
// BatchDelete 批量删除用户角色关联
func (r *UserRoleRepository) BatchDelete(ctx context.Context, userRoles []*domain.UserRole) error {
if len(userRoles) == 0 {
return nil
}
var ids []int64
for _, ur := range userRoles {
ids = append(ids, ur.ID)
}
return r.db.WithContext(ctx).Delete(&domain.UserRole{}, ids).Error
}
// ReplaceUserRoles replaces all roles for a user in a single transaction
// This encapsulates the delete-then-create pattern to ensure atomicity
func (r *UserRoleRepository) ReplaceUserRoles(ctx context.Context, userID int64, roleIDs []int64) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Delete all existing roles for the user
if err := tx.Where("user_id = ?", userID).Delete(&domain.UserRole{}).Error; err != nil {
return err
}
// Create new role associations if any
if len(roleIDs) > 0 {
userRoles := make([]*domain.UserRole, len(roleIDs))
for i, roleID := range roleIDs {
userRoles[i] = &domain.UserRole{
UserID: userID,
RoleID: roleID,
}
}
if err := tx.Create(&userRoles).Error; err != nil {
return err
}
}
return nil
})
}