Files
blog-server/internal/service/sys_user.go
2026-07-29 22:10:36 +08:00

333 lines
7.1 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 service
import (
"context"
"errors"
"server/internal/db"
"server/internal/db/sqlc"
"server/internal/middleware"
"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"
"server/internal/pkg/httputil"
"golang.org/x/sync/errgroup"
)
type SysUserService struct {
store *db.Store
jwt *middleware.JWTMiddleware
cache *cache.Caches
}
func NewSysUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService {
return &SysUserService{
store: store,
jwt: jwt,
cache: cache,
}
}
// clearUserCache
func (s *SysUserService) 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 *SysUserService) 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 *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool) (*response.SysUserInfo, error) {
return cache.GetOrSetJSON[*response.SysUserInfo](ctx, s.cache, cachekey.UserInfo(id), 0, func() (*response.SysUserInfo, error) {
g, ctx := errgroup.WithContext(ctx)
var (
user sqlc.GetSysUserByIDRow
roles []sqlc.SysRole
menus []sqlc.SysMenu
permissions []*string
)
g.Go(func() error {
u, err := s.store.GetSysUserByID(ctx, id)
if err != nil {
return dberr.MapNoRows(err, errs.ErrUserNotFound)
}
user = u
return nil
})
g.Go(func() error {
r, err := s.store.GetSysUserRoles(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.GetSysAdminPermissions(ctx)
} else {
p, err = s.store.GetSysUserPermissions(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.GetSysAdminMenus(ctx)
} else {
m, err = s.store.GetSysUserMenus(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.NewSysUserInfo(user, roles, menus, permissions)
return userInfo, nil
})
}
func (s *SysUserService) ListPage(ctx context.Context, p request.SearchSysUserParams) ([]sqlc.ListSysUsersRow, int64, error) {
params := sqlc.ListSysUsersParams{
Limit: p.PageSize,
Offset: (p.Page - 1) * p.PageSize,
Username: p.Username,
}
total, err := s.store.CountSysUsers(ctx, p.Username)
if err != nil {
return nil, 0, err
}
users, err := s.store.ListSysUsers(ctx, params)
if err != nil {
return nil, 0, err
}
// 处理每个用户的头像URL
for i := range users {
url := httputil.BuildFileUrl(users[i].AvatarUrl)
users[i].AvatarUrl = &url
}
return users, total, nil
}
func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) {
// 先查询用户是否存在
_, err := s.store.GetSysUserByID(ctx, id)
if err != nil {
return nil, dberr.MapNoRows(err, errs.ErrUserNotFound)
}
return s.store.GetSysUserRoles(ctx, id)
}
func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRequest) error {
passwordHash, err := generatePasswordHash(req.Password)
if err != nil {
return err
}
user := sqlc.CreateSysUserParams{
Account: req.Account,
Username: req.Username,
PasswordHash: passwordHash,
AvatarID: req.AvatarID,
Status: *req.Status,
}
if err = s.store.CreateSysUser(ctx, user); err != nil {
return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists)
}
return nil
}
func (s *SysUserService) Update(ctx context.Context, id int32, req request.UpdateSysUserRequest) error {
user := sqlc.UpdateSysUserParams{
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.UpdateSysUser(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 *SysUserService) SetRoles(ctx context.Context, userID int32, req request.SetSysUserRolesRequest) error {
// 先查询用户是否存在
_, err := s.store.GetSysUserByID(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.ClearSysUserRoles(ctx, userID); err != nil {
return err
}
var params []sqlc.CreateSysUserRoleParams
for _, roleID := range req.RoleIDs {
params = append(params, sqlc.CreateSysUserRoleParams{
UserID: userID,
RoleID: roleID,
})
}
// 2. 关联用户角色数据
_, err = q.CreateSysUserRole(ctx, params)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// 3. 清理缓存
_ = s.clearUserCache(ctx, userID)
return nil
}
func (s *SysUserService) UpdatePassword(ctx context.Context, id int32, req request.UpdateSysUserPassword) error {
passwordHash, err := generatePasswordHash(req.Password)
if err != nil {
return err
}
params := sqlc.UpdateSysUserPasswordParams{
ID: id,
PasswordHash: passwordHash,
}
rows, err := s.store.UpdateSysUserPassword(ctx, params)
err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
if err != nil {
return err
}
// 下线当前用户
_ = s.clearRefreshToken(ctx, id)
return nil
}
func (s *SysUserService) Delete(ctx context.Context, id int32) error {
if id == 1 {
return errs.ErrCannotDeleteSuperAdmin
}
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
// 清理用户角色关联数据
err := q.ClearSysUserRoles(ctx, id)
if err != nil {
return err
}
// 删除用户
rows, err := q.DeleteSysUser(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
}