332 lines
6.9 KiB
Go
332 lines
6.9 KiB
Go
package admin
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"server/internal/db"
|
||
"server/internal/db/sqlc"
|
||
"server/internal/middleware"
|
||
"server/internal/model/common"
|
||
"server/internal/model/request"
|
||
"server/internal/model/response"
|
||
"server/internal/pkg/cache"
|
||
"server/internal/pkg/cache/cachekey"
|
||
"server/internal/pkg/dberr"
|
||
"server/internal/pkg/errs"
|
||
|
||
"golang.org/x/sync/errgroup"
|
||
)
|
||
|
||
type UserService struct {
|
||
store *db.Store
|
||
jwt *middleware.JWTMiddleware
|
||
cache *cache.Caches
|
||
}
|
||
|
||
func NewUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *UserService {
|
||
return &UserService{
|
||
store: store,
|
||
jwt: jwt,
|
||
cache: cache,
|
||
}
|
||
}
|
||
|
||
// clearUserCache
|
||
func (s *UserService) clearUserCache(ctx context.Context, id int32) error {
|
||
if err := s.cache.Del(ctx, cachekey.UserApiPermissions(id)); err != nil {
|
||
return err
|
||
}
|
||
|
||
if err := s.cache.Del(ctx, cachekey.UserInfo(id)); err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// clearRefreshToken
|
||
func (s *UserService) clearRefreshToken(ctx context.Context, id int32) error {
|
||
|
||
keys, err := s.cache.SMembers(ctx, cachekey.AuthRefreshUser(id))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 循环删除所有的token
|
||
for _, key := range keys {
|
||
if err = s.cache.Del(ctx, cachekey.AuthRefresh(key)); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// 删除反向索引
|
||
err = s.cache.DelSetMembers(ctx, cachekey.AuthRefreshUser(id))
|
||
return err
|
||
}
|
||
|
||
func (s *UserService) GetCurrentUser(ctx context.Context, id int32, isAdmin bool) (*response.UserInfo, error) {
|
||
return cache.GetOrSetJSON[*response.UserInfo](ctx, s.cache, cachekey.UserInfo(id), 0, func() (*response.UserInfo, error) {
|
||
g, ctx := errgroup.WithContext(ctx)
|
||
|
||
var (
|
||
user sqlc.GetUserByIDRow
|
||
roles []sqlc.SysRole
|
||
menus []sqlc.SysMenu
|
||
permissions []*string
|
||
)
|
||
|
||
g.Go(func() error {
|
||
u, err := s.store.GetUserByID(ctx, id)
|
||
if err != nil {
|
||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||
}
|
||
|
||
user = u
|
||
return nil
|
||
})
|
||
|
||
g.Go(func() error {
|
||
r, err := s.store.ListUserRoles(ctx, id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
roles = r
|
||
return nil
|
||
})
|
||
|
||
g.Go(func() error {
|
||
var (
|
||
p []*string
|
||
err error
|
||
)
|
||
|
||
if isAdmin {
|
||
p, err = s.store.ListAdminPermissionCodes(ctx)
|
||
} else {
|
||
p, err = s.store.ListUserPermissionCodes(ctx, id)
|
||
}
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
permissions = p
|
||
|
||
return nil
|
||
})
|
||
|
||
g.Go(func() error {
|
||
var (
|
||
m []sqlc.SysMenu
|
||
err error
|
||
)
|
||
if isAdmin {
|
||
m, err = s.store.ListAdminMenus(ctx)
|
||
} else {
|
||
m, err = s.store.ListUserMenus(ctx, id)
|
||
}
|
||
if err != nil {
|
||
return err
|
||
}
|
||
menus = m
|
||
return nil
|
||
})
|
||
|
||
if err := g.Wait(); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 如果用户被禁用 返回错误 超管不用管状态
|
||
if user.ID != 1 && user.Status != 1 {
|
||
return nil, errs.ErrUserDisabled
|
||
}
|
||
|
||
// 处理角色
|
||
userInfo := response.NewUserInfo(user, roles, menus, permissions)
|
||
|
||
return userInfo, nil
|
||
})
|
||
}
|
||
|
||
func (s *UserService) List(ctx context.Context, p request.SearchUserParams) (*common.PageResult[sqlc.ListUsersRow], error) {
|
||
params := sqlc.ListUsersParams{
|
||
Limit: p.PageSize,
|
||
Offset: (p.Page - 1) * p.PageSize,
|
||
Username: p.Username,
|
||
}
|
||
|
||
total, err := s.store.CountUsers(ctx, p.Username)
|
||
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
list, err := s.store.ListUsers(ctx, params)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &common.PageResult[sqlc.ListUsersRow]{
|
||
List: list,
|
||
Total: total,
|
||
}, nil
|
||
}
|
||
|
||
func (s *UserService) ListRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) {
|
||
// 先查询用户是否存在
|
||
_, err := s.store.GetUserByID(ctx, id)
|
||
if err != nil {
|
||
return nil, dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||
}
|
||
|
||
return s.store.ListUserRoles(ctx, id)
|
||
}
|
||
|
||
func (s *UserService) Create(ctx context.Context, req request.CreateUserRequest) error {
|
||
passwordHash, err := generatePasswordHash(req.Password)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
user := sqlc.CreateUserParams{
|
||
Account: req.Account,
|
||
Username: req.Username,
|
||
PasswordHash: passwordHash,
|
||
AvatarID: req.AvatarID,
|
||
Status: *req.Status,
|
||
}
|
||
|
||
if err = s.store.CreateUser(ctx, user); err != nil {
|
||
return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *UserService) Update(ctx context.Context, id int32, req request.UpdateUserRequest) error {
|
||
user := sqlc.UpdateUserParams{
|
||
Username: req.Username,
|
||
ID: id,
|
||
Status: req.Status,
|
||
}
|
||
|
||
if req.AvatarID.Set {
|
||
user.UpdateAvatarID = true
|
||
if req.AvatarID.Valid {
|
||
user.AvatarID = &req.AvatarID.Value
|
||
}
|
||
}
|
||
|
||
rows, err := s.store.UpdateUser(ctx, user)
|
||
if err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 清理缓存
|
||
_ = s.clearUserCache(ctx, id)
|
||
|
||
// 如果将用户的状态修改为0,则清除用户刷新令牌
|
||
if req.Status != nil && *req.Status == 0 {
|
||
_ = s.clearRefreshToken(ctx, id)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *UserService) SetRoles(ctx context.Context, userID int32, req request.SetUserRolesRequest) error {
|
||
// 先查询用户是否存在
|
||
_, err := s.store.GetUserByID(ctx, userID)
|
||
if err != nil {
|
||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||
}
|
||
|
||
err = s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||
// 1. 先删除关联数据
|
||
if err = q.ClearUserRoles(ctx, userID); err != nil {
|
||
return err
|
||
}
|
||
|
||
var params []sqlc.CreateUserRoleParams
|
||
|
||
for _, roleID := range req.RoleIDs {
|
||
params = append(params, sqlc.CreateUserRoleParams{
|
||
UserID: userID,
|
||
RoleID: roleID,
|
||
})
|
||
}
|
||
|
||
// 2. 关联用户角色数据
|
||
_, err = q.CreateUserRole(ctx, params)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
})
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 3. 清理缓存
|
||
_ = s.clearUserCache(ctx, userID)
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *UserService) UpdatePassword(ctx context.Context, id int32, req request.UpdateUserPassword) error {
|
||
passwordHash, err := generatePasswordHash(req.Password)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
params := sqlc.UpdateUserPasswordParams{
|
||
ID: id,
|
||
PasswordHash: passwordHash,
|
||
}
|
||
|
||
rows, err := s.store.UpdateUserPassword(ctx, params)
|
||
err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 下线当前用户
|
||
_ = s.clearRefreshToken(ctx, id)
|
||
|
||
return nil
|
||
}
|
||
|
||
func (s *UserService) Delete(ctx context.Context, id int32) error {
|
||
isAdmin := middleware.UserIsAdmin(id)
|
||
|
||
if isAdmin {
|
||
return errs.ErrCannotDeleteSuperAdmin
|
||
}
|
||
|
||
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||
// 清理用户角色关联数据
|
||
err := q.ClearUserRoles(ctx, id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 删除用户
|
||
rows, err := q.DeleteUser(ctx, id)
|
||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||
})
|
||
|
||
if err != nil {
|
||
if errors.Is(err, errs.ErrUserNotFound) {
|
||
// 用户不存在也执行清理缓存操作
|
||
_ = s.clearUserCache(ctx, id)
|
||
}
|
||
return err
|
||
}
|
||
|
||
// 事务执行成功后清理缓存
|
||
_ = s.clearUserCache(ctx, id)
|
||
_ = s.clearRefreshToken(ctx, id)
|
||
return nil
|
||
}
|