Files
portal-mini-go/internal/repository/admin/user_repo.go
T
2026-10-25 22:47:13 +08:00

163 lines
5.5 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 admin 提供后台 RBAC 相关表的数据访问。
// 包含管理员(admin_users)、角色(admin_roles)、权限(admin_permissions)
// 及其多对多关联表的读写。所有查询均参数化,杜绝 SQL 注入。
package admin
import (
"context"
"gorm.io/gorm"
"portal-mini-go/internal/model/admin"
"portal-mini-go/internal/pkg/errors"
)
// UserRepository 管理员账号与角色关联的数据访问。
type UserRepository struct {
db *gorm.DB
}
func NewUserRepository(db *gorm.DB) *UserRepository { return &UserRepository{db: db} }
// GetByUsername 按用户名查询管理员,不存在返回 ErrNotFound。
func (r *UserRepository) GetByUsername(ctx context.Context, username string) (*admin.User, error) {
var u admin.User
if err := r.db.WithContext(ctx).Where("username = ?", username).First(&u).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.ErrNotFound
}
return nil, errors.Wrap(50000, "query admin user", err)
}
return &u, nil
}
// GetByID 按 ID 查询管理员,不存在返回 ErrNotFound。
func (r *UserRepository) GetByID(ctx context.Context, id uint64) (*admin.User, error) {
var u admin.User
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&u).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.ErrNotFound
}
return nil, errors.Wrap(50000, "query admin user", err)
}
return &u, nil
}
// Create 新建管理员。
func (r *UserRepository) Create(ctx context.Context, u *admin.User) error {
if err := r.db.WithContext(ctx).Create(u).Error; err != nil {
return errors.Wrap(50000, "create admin user", err)
}
return nil
}
// Count 统计管理员总数。
func (r *UserRepository) Count(ctx context.Context) (int64, error) {
var n int64
if err := r.db.WithContext(ctx).Model(&admin.User{}).Count(&n).Error; err != nil {
return 0, errors.Wrap(50000, "count admin user", err)
}
return n, nil
}
// UserListFilter 管理员列表过滤条件。
type UserListFilter struct {
Keyword string
Status int8 // <0 表示不过滤
Page int
Size int
}
// List 分页查询管理员,支持关键字(用户名 / 昵称)与状态过滤。
func (r *UserRepository) List(ctx context.Context, f UserListFilter) ([]admin.User, int64, error) {
q := r.db.WithContext(ctx).Model(&admin.User{})
if f.Status >= 0 {
q = q.Where("status = ?", f.Status)
}
if f.Keyword != "" {
kw := "%" + f.Keyword + "%"
q = q.Where("username LIKE ? OR nickname LIKE ?", kw, kw)
}
var total int64
if err := q.Count(&total).Error; err != nil {
return nil, 0, errors.Wrap(50000, "count admin user", err)
}
var list []admin.User
if err := q.Order("id DESC").Offset((f.Page - 1) * f.Size).Limit(f.Size).Find(&list).Error; err != nil {
return nil, 0, errors.Wrap(50000, "list admin user", err)
}
return list, total, nil
}
// Update 按 ID 局部更新管理员非零值字段(cols 为待更新列)。
func (r *UserRepository) Update(ctx context.Context, id uint64, cols map[string]interface{}) error {
res := r.db.WithContext(ctx).Model(&admin.User{}).Where("id = ?", id).Updates(cols)
if res.Error != nil {
return errors.Wrap(50000, "update admin user", res.Error)
}
if res.RowsAffected == 0 {
return errors.ErrNotFound
}
return nil
}
// Delete 删除管理员,并清理其角色关联。
func (r *UserRepository) Delete(ctx context.Context, id uint64) error {
if err := r.db.WithContext(ctx).Where("admin_id = ?", id).Delete(&admin.UserRole{}).Error; err != nil {
return errors.Wrap(50000, "delete admin user roles", err)
}
res := r.db.WithContext(ctx).Where("id = ?", id).Delete(&admin.User{})
if res.Error != nil {
return errors.Wrap(50000, "delete admin user", res.Error)
}
if res.RowsAffected == 0 {
return errors.ErrNotFound
}
return nil
}
// SetRoles 全量替换管理员所属角色(先删后插)。
func (r *UserRepository) SetRoles(ctx context.Context, adminID uint64, roleIDs []uint64) error {
if err := r.db.WithContext(ctx).Where("admin_id = ?", adminID).Delete(&admin.UserRole{}).Error; err != nil {
return errors.Wrap(50000, "reset admin roles", err)
}
if len(roleIDs) == 0 {
return nil
}
rows := make([]admin.UserRole, 0, len(roleIDs))
for _, rid := range roleIDs {
rows = append(rows, admin.UserRole{AdminID: adminID, RoleID: rid})
}
if err := r.db.WithContext(ctx).Create(&rows).Error; err != nil {
return errors.Wrap(50000, "assign admin roles", err)
}
return nil
}
// RoleCodesOf 返回管理员所属角色 code 列表(经 admin_user_roles 关联)。
func (r *UserRepository) RoleCodesOf(ctx context.Context, adminID uint64) ([]string, error) {
var codes []string
if err := r.db.WithContext(ctx).
Table("admin_user_roles").
Joins("JOIN admin_roles ON admin_roles.id = admin_user_roles.role_id").
Where("admin_user_roles.admin_id = ?", adminID).
Pluck("admin_roles.code", &codes).Error; err != nil {
return nil, errors.Wrap(50000, "query admin role codes", err)
}
return codes, nil
}
// PermissionCodesOf 返回管理员经角色聚合后的全部权限 code 列表(去重)。
func (r *UserRepository) PermissionCodesOf(ctx context.Context, adminID uint64) ([]string, error) {
var codes []string
if err := r.db.WithContext(ctx).
Table("admin_user_roles ur").
Joins("JOIN admin_role_permissions rp ON rp.role_id = ur.role_id").
Joins("JOIN admin_permissions p ON p.id = rp.permission_id").
Where("ur.admin_id = ?", adminID).
Pluck("p.code", &codes).Error; err != nil {
return nil, errors.Wrap(50000, "query admin permission codes", err)
}
return codes, nil
}