feat: release v1.0.0
This commit is contained in:
@@ -2,165 +2,166 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
db "server/internal/db/sqlc"
|
||||
"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"
|
||||
"server/internal/pkg/httputil"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type SysUserService struct {
|
||||
queries *db.Queries
|
||||
pool *pgxpool.Pool
|
||||
jwt *middleware.JWTMiddleware
|
||||
cache *cache.Caches
|
||||
store *db.Store
|
||||
jwt *middleware.JWTMiddleware
|
||||
cache *cache.Caches
|
||||
}
|
||||
|
||||
func NewSysUserService(queries *db.Queries, pool *pgxpool.Pool, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService {
|
||||
func NewSysUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService {
|
||||
return &SysUserService{
|
||||
queries: queries,
|
||||
pool: pool,
|
||||
jwt: jwt,
|
||||
cache: cache,
|
||||
store: store,
|
||||
jwt: jwt,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// generatePasswordHash 生成密码哈希
|
||||
func generatePasswordHash(password string) (string, error) {
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// clearUserCache
|
||||
func (s *SysUserService) clearUserCache(ctx context.Context, id int32) error {
|
||||
if err := s.cache.Del(ctx, cachekey.UserApiPermissions(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
return string(hashed), nil
|
||||
|
||||
if err := s.cache.Del(ctx, cachekey.UserInfo(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// comparePasswordHash 比较密码哈希
|
||||
func comparePasswordHash(passwordHash, inputPassword string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(inputPassword))
|
||||
}
|
||||
|
||||
func (s *SysUserService) Login(ctx context.Context, req request.LoginRequest) (*response.LoginResponse, error) {
|
||||
user, err := s.queries.GetSysUserByAccount(ctx, req.Account)
|
||||
// clearRefreshToken
|
||||
func (s *SysUserService) clearRefreshToken(ctx context.Context, id int32) error {
|
||||
|
||||
keys, err := s.cache.SMembers(ctx, cachekey.AuthRefreshUser(id))
|
||||
if err != nil {
|
||||
return nil, errs.ErrInvalidCredentials
|
||||
return err
|
||||
}
|
||||
|
||||
if err = comparePasswordHash(user.PasswordHash, req.Password); err != nil {
|
||||
return nil, errs.ErrInvalidCredentials
|
||||
// 循环删除所有的token
|
||||
for _, key := range keys {
|
||||
if err = s.cache.Del(ctx, cachekey.AuthRefresh(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refreshToken, refreshTokenExp, err := s.jwt.GenerateRefreshToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.LoginResponse{
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExp: accessTokenExp,
|
||||
RefreshToken: refreshToken,
|
||||
RefreshTokenExp: refreshTokenExp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) RefreshToken(ctx context.Context, refreshToken string) (*response.LoginResponse, error) {
|
||||
claims, err := s.jwt.ParseRefreshToken(refreshToken)
|
||||
if err != nil {
|
||||
return nil, errs.ErrInvalidRefreshToken
|
||||
}
|
||||
|
||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(claims.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.LoginResponse{
|
||||
AccessToken: accessToken,
|
||||
AccessTokenExp: accessTokenExp,
|
||||
}, nil
|
||||
// 删除反向索引
|
||||
err = s.cache.DelSetMembers(ctx, cachekey.AuthRefreshUser(id))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool) (*response.SysUserInfo, error) {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
return cache.GetOrSetJSON[*response.SysUserInfo](ctx, s.cache, cachekey.UserInfo(id), 0, func() (*response.SysUserInfo, error) {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var (
|
||||
user db.GetSysUserByIDRow
|
||||
roles []db.SysRole
|
||||
menus []db.SysMenu
|
||||
)
|
||||
|
||||
g.Go(func() error {
|
||||
u, err := s.queries.GetSysUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||
}
|
||||
user = u
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
r, err := s.queries.GetSysUserRoles(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
roles = r
|
||||
return nil
|
||||
})
|
||||
|
||||
g.Go(func() error {
|
||||
var (
|
||||
m []db.SysMenu
|
||||
err error
|
||||
user sqlc.GetSysUserByIDRow
|
||||
roles []sqlc.SysRole
|
||||
menus []sqlc.SysMenu
|
||||
permissions []*string
|
||||
)
|
||||
if isAdmin {
|
||||
m, err = s.queries.GetSysAdminMenus(ctx)
|
||||
} else {
|
||||
m, err = s.queries.GetSysUserMenus(ctx, id)
|
||||
|
||||
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 err != nil {
|
||||
return err
|
||||
|
||||
// 如果用户被禁用 返回错误 超管不用管状态
|
||||
if user.ID != 1 && user.Status != 1 {
|
||||
return nil, errs.ErrUserDisabled
|
||||
}
|
||||
menus = m
|
||||
return nil
|
||||
|
||||
// 处理角色
|
||||
userInfo := response.NewSysUserInfo(user, roles, menus, permissions)
|
||||
|
||||
return userInfo, nil
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 处理角色
|
||||
userInfo := response.NewSysUserInfo(user, roles, menus)
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListSysUsersRow, int64, error) {
|
||||
params := db.ListSysUsersParams{
|
||||
Limit: p.PageSize,
|
||||
Offset: (p.Page - 1) * p.PageSize,
|
||||
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.queries.CountSysUsers(ctx)
|
||||
total, err := s.store.CountSysUsers(ctx, p.Username)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
users, err := s.queries.ListSysUsers(ctx, params)
|
||||
users, err := s.store.ListSysUsers(ctx, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -174,14 +175,14 @@ func (s *SysUserService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
||||
return users, total, nil
|
||||
}
|
||||
|
||||
func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]db.SysRole, error) {
|
||||
func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) {
|
||||
// 先查询用户是否存在
|
||||
_, err := s.queries.GetSysUserByID(ctx, id)
|
||||
_, err := s.store.GetSysUserByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||
}
|
||||
|
||||
return s.queries.GetSysUserRoles(ctx, id)
|
||||
return s.store.GetSysUserRoles(ctx, id)
|
||||
}
|
||||
|
||||
func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRequest) error {
|
||||
@@ -190,14 +191,15 @@ func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRe
|
||||
return err
|
||||
}
|
||||
|
||||
user := db.CreateSysUserParams{
|
||||
user := sqlc.CreateSysUserParams{
|
||||
Account: req.Account,
|
||||
Username: req.Username,
|
||||
PasswordHash: passwordHash,
|
||||
AvatarID: req.AvatarID,
|
||||
Status: *req.Status,
|
||||
}
|
||||
|
||||
if err = s.queries.CreateSysUser(ctx, user); err != nil {
|
||||
if err = s.store.CreateSysUser(ctx, user); err != nil {
|
||||
return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists)
|
||||
}
|
||||
|
||||
@@ -205,9 +207,10 @@ func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRe
|
||||
}
|
||||
|
||||
func (s *SysUserService) Update(ctx context.Context, id int32, req request.UpdateSysUserRequest) error {
|
||||
user := db.UpdateSysUserParams{
|
||||
user := sqlc.UpdateSysUserParams{
|
||||
Username: req.Username,
|
||||
ID: id,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
if req.AvatarID.Set {
|
||||
@@ -217,47 +220,59 @@ func (s *SysUserService) Update(ctx context.Context, id int32, req request.Updat
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := s.queries.UpdateSysUser(ctx, user)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
}
|
||||
|
||||
func (s *SysUserService) SetRoles(ctx context.Context, userID int32, req request.SetSysUserRolesRequest) error {
|
||||
// 先查询用户是否存在
|
||||
_, err := s.queries.GetSysUserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||
}
|
||||
|
||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func(ctx context.Context) {
|
||||
_ = tx.Rollback(ctx)
|
||||
}(ctx)
|
||||
|
||||
q := db.New(tx)
|
||||
|
||||
if err = q.ClearSysUserRoles(ctx, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, roleID := range req.RoleIDs {
|
||||
if err = q.CreateSysUserRole(ctx, db.CreateSysUserRoleParams{
|
||||
UserID: userID,
|
||||
RoleID: roleID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = tx.Commit(ctx); err != nil {
|
||||
rows, err := s.store.UpdateSysUser(ctx, user)
|
||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearSysUserCache(userID)
|
||||
_ = 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
|
||||
}
|
||||
@@ -268,13 +283,21 @@ func (s *SysUserService) UpdatePassword(ctx context.Context, id int32, req reque
|
||||
return err
|
||||
}
|
||||
|
||||
params := db.UpdateSysUserPasswordParams{
|
||||
params := sqlc.UpdateSysUserPasswordParams{
|
||||
ID: id,
|
||||
PasswordHash: passwordHash,
|
||||
}
|
||||
|
||||
rows, err := s.queries.UpdateSysUserPassword(ctx, params)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
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 {
|
||||
@@ -282,9 +305,28 @@ func (s *SysUserService) Delete(ctx context.Context, id int32) error {
|
||||
return errs.ErrCannotDeleteSuperAdmin
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.cache.ClearSysUserCache(id)
|
||||
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||
// 清理用户角色关联数据
|
||||
err := q.ClearSysUserRoles(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := s.queries.DeleteSysUser(ctx, id)
|
||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||
// 删除用户
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user