234 lines
7.0 KiB
Go
234 lines
7.0 KiB
Go
package system
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"time"
|
||
|
||
"github.com/google/uuid"
|
||
"golang.org/x/crypto/bcrypt"
|
||
)
|
||
|
||
var (
|
||
ErrUserNotFound = errors.New("用户不存在")
|
||
ErrUserAlreadyExists = errors.New("用户名已注册")
|
||
ErrPasswordWrong = errors.New("密码错误")
|
||
ErrOldPasswordWrong = errors.New("原密码错误")
|
||
ErrUserNoAuthority = errors.New("该用户无此角色")
|
||
ErrDefaultRouterEmpty = errors.New("找不到默认路由,无法切换本角色")
|
||
)
|
||
|
||
// User 用户实体
|
||
type User struct {
|
||
ID uint
|
||
UUID uuid.UUID
|
||
Username string
|
||
Password string
|
||
NickName string
|
||
SideMode string
|
||
HeaderImg string
|
||
BaseColor string
|
||
AuthorityId uint
|
||
Phone string
|
||
Email string
|
||
Enable int
|
||
OriginSetting json.RawMessage
|
||
Authority *Authority
|
||
Authorities []*Authority
|
||
CreatedAt time.Time
|
||
UpdatedAt time.Time
|
||
}
|
||
|
||
// Authority 角色实体
|
||
type Authority struct {
|
||
AuthorityId uint
|
||
AuthorityName string
|
||
ParentId *uint
|
||
DefaultRouter string
|
||
}
|
||
|
||
// UserRepo 用户仓储接口
|
||
type UserRepo interface {
|
||
Create(ctx context.Context, user *User) error
|
||
Update(ctx context.Context, user *User) error
|
||
UpdateSelf(ctx context.Context, user *User) error
|
||
UpdateSelfSetting(ctx context.Context, id uint, setting json.RawMessage) error
|
||
Delete(ctx context.Context, id uint) error
|
||
FindByID(ctx context.Context, id uint) (*User, error)
|
||
FindByIDWithAuthorities(ctx context.Context, id uint) (*User, error)
|
||
FindByUUID(ctx context.Context, uuid string) (*User, error)
|
||
FindByUUIDWithAuthorities(ctx context.Context, uuid string) (*User, error)
|
||
FindByUsername(ctx context.Context, username string) (*User, error)
|
||
FindByUsernameWithAuthorities(ctx context.Context, username string) (*User, error)
|
||
List(ctx context.Context, page, pageSize int, filters map[string]string) ([]*User, int64, error)
|
||
UpdatePassword(ctx context.Context, id uint, password string) error
|
||
UpdateAuthorityID(ctx context.Context, id uint, authorityId uint) error
|
||
SetUserAuthorities(ctx context.Context, adminAuthorityID, userId uint, authorityIds []uint) error
|
||
CheckUserHasAuthority(ctx context.Context, userId, authorityId uint) (bool, error)
|
||
GetAuthorityMenuRouters(ctx context.Context, authorityId uint) ([]string, error)
|
||
GetAuthorityDefaultRouter(ctx context.Context, authorityId uint) (string, error)
|
||
}
|
||
|
||
// UserUsecase 用户用例
|
||
type UserUsecase struct {
|
||
repo UserRepo
|
||
}
|
||
|
||
// NewUserUsecase 创建用户用例
|
||
func NewUserUsecase(repo UserRepo) *UserUsecase {
|
||
return &UserUsecase{repo: repo}
|
||
}
|
||
|
||
// Register 用户注册
|
||
func (uc *UserUsecase) Register(ctx context.Context, user *User) (*User, error) {
|
||
// 检查用户名是否已存在
|
||
existing, _ := uc.repo.FindByUsername(ctx, user.Username)
|
||
if existing != nil {
|
||
return nil, ErrUserAlreadyExists
|
||
}
|
||
|
||
// 密码加密
|
||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
user.Password = string(hashedPassword)
|
||
user.UUID = uuid.New()
|
||
|
||
if err := uc.repo.Create(ctx, user); err != nil {
|
||
return nil, err
|
||
}
|
||
return user, nil
|
||
}
|
||
|
||
// Login 用户登录(预加载Authorities)
|
||
func (uc *UserUsecase) Login(ctx context.Context, username, password string) (*User, error) {
|
||
user, err := uc.repo.FindByUsernameWithAuthorities(ctx, username)
|
||
if err != nil {
|
||
return nil, ErrUserNotFound
|
||
}
|
||
|
||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||
return nil, ErrPasswordWrong
|
||
}
|
||
|
||
return user, nil
|
||
}
|
||
|
||
// ChangePassword 修改密码
|
||
func (uc *UserUsecase) ChangePassword(ctx context.Context, id uint, oldPassword, newPassword string) error {
|
||
user, err := uc.repo.FindByID(ctx, id)
|
||
if err != nil {
|
||
return ErrUserNotFound
|
||
}
|
||
|
||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(oldPassword)); err != nil {
|
||
return ErrOldPasswordWrong
|
||
}
|
||
|
||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return uc.repo.UpdatePassword(ctx, id, string(hashedPassword))
|
||
}
|
||
|
||
// ResetPassword 重置密码
|
||
func (uc *UserUsecase) ResetPassword(ctx context.Context, id uint, newPassword string) error {
|
||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return uc.repo.UpdatePassword(ctx, id, string(hashedPassword))
|
||
}
|
||
|
||
// GetUserInfo 获取用户信息(预加载Authorities)
|
||
func (uc *UserUsecase) GetUserInfo(ctx context.Context, uuid string) (*User, error) {
|
||
return uc.repo.FindByUUIDWithAuthorities(ctx, uuid)
|
||
}
|
||
|
||
// GetUserByID 通过ID获取用户
|
||
func (uc *UserUsecase) GetUserByID(ctx context.Context, id uint) (*User, error) {
|
||
return uc.repo.FindByID(ctx, id)
|
||
}
|
||
|
||
// FindUserById 通过ID获取用户(GVA兼容)
|
||
func (uc *UserUsecase) FindUserById(ctx context.Context, id uint) (*User, error) {
|
||
return uc.repo.FindByID(ctx, id)
|
||
}
|
||
|
||
// FindUserByUuid 通过UUID获取用户(GVA兼容)
|
||
func (uc *UserUsecase) FindUserByUuid(ctx context.Context, uuid string) (*User, error) {
|
||
return uc.repo.FindByUUID(ctx, uuid)
|
||
}
|
||
|
||
// GetUserList 获取用户列表
|
||
func (uc *UserUsecase) GetUserList(ctx context.Context, page, pageSize int, filters map[string]string) ([]*User, int64, error) {
|
||
return uc.repo.List(ctx, page, pageSize, filters)
|
||
}
|
||
|
||
// SetUserInfo 设置用户信息(管理员用)
|
||
func (uc *UserUsecase) SetUserInfo(ctx context.Context, user *User) error {
|
||
return uc.repo.Update(ctx, user)
|
||
}
|
||
|
||
// SetSelfInfo 设置自己的信息(用户用)
|
||
func (uc *UserUsecase) SetSelfInfo(ctx context.Context, user *User) error {
|
||
return uc.repo.UpdateSelf(ctx, user)
|
||
}
|
||
|
||
// SetSelfSetting 设置自己的配置
|
||
func (uc *UserUsecase) SetSelfSetting(ctx context.Context, id uint, setting json.RawMessage) error {
|
||
return uc.repo.UpdateSelfSetting(ctx, id, setting)
|
||
}
|
||
|
||
// DeleteUser 删除用户
|
||
func (uc *UserUsecase) DeleteUser(ctx context.Context, id uint) error {
|
||
return uc.repo.Delete(ctx, id)
|
||
}
|
||
|
||
// SetUserAuthority 设置用户角色(切换角色)
|
||
func (uc *UserUsecase) SetUserAuthority(ctx context.Context, id uint, authorityId uint) error {
|
||
// 检查用户是否拥有该角色
|
||
hasAuthority, err := uc.repo.CheckUserHasAuthority(ctx, id, authorityId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if !hasAuthority {
|
||
return ErrUserNoAuthority
|
||
}
|
||
|
||
// 获取角色的默认路由
|
||
defaultRouter, err := uc.repo.GetAuthorityDefaultRouter(ctx, authorityId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 获取角色的菜单路由列表
|
||
menuRouters, err := uc.repo.GetAuthorityMenuRouters(ctx, authorityId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 检查默认路由是否在菜单中
|
||
hasMenu := false
|
||
for _, router := range menuRouters {
|
||
if router == defaultRouter {
|
||
hasMenu = true
|
||
break
|
||
}
|
||
}
|
||
if !hasMenu {
|
||
return ErrDefaultRouterEmpty
|
||
}
|
||
|
||
return uc.repo.UpdateAuthorityID(ctx, id, authorityId)
|
||
}
|
||
|
||
// SetUserAuthorities 设置用户多角色
|
||
func (uc *UserUsecase) SetUserAuthorities(ctx context.Context, adminAuthorityID, userId uint, authorityIds []uint) error {
|
||
return uc.repo.SetUserAuthorities(ctx, adminAuthorityID, userId, authorityIds)
|
||
}
|