package biz import ( "context" "time" "golang.org/x/crypto/bcrypt" ) type User struct { ID uint CreatedAt time.Time UpdatedAt time.Time UUID string Username string Password string NickName string HeaderImg string AuthorityID uint Authority Authority Authorities []Authority DeptID uint Department *Department Departments []Department Positions []Position Phone string Email string Enable int OriginSetting map[string]any MustChangePassword bool PasswordUpdatedAt *time.Time } type UserListFilter struct { Username, NickName, Phone, Email, OrderKey string Desc bool } type Authority struct { AuthorityID uint AuthorityName string ParentID *uint DataScope int DefaultRouter string Children []*Authority } type UserRepo interface { FindUserByUsername(context.Context, string) (*User, error) FindUserByID(context.Context, uint) (*User, error) MenusByAuthority(context.Context, uint) ([]*Menu, error) ListUsers(context.Context, int, int, *UserListFilter) ([]*User, int64, error) CreateUser(context.Context, *User) (*User, error) CreateUserWithAuthorities(context.Context, *User, []uint) (*User, error) UpdateUser(context.Context, *User) error UpdateSelfUser(context.Context, *User) error UpdateUserWithAuthorities(context.Context, *User, []uint) error DeleteUser(context.Context, uint) error UpdatePassword(context.Context, uint, string, bool) error SetUserAuthorities(context.Context, uint, []uint) error SetUserAuthority(context.Context, uint, uint) error SetUserSetting(context.Context, uint, map[string]any) error ListAuthorities(context.Context) ([]*Authority, error) FillDepartmentNamePaths(context.Context, *User) error } type UserUsecase struct{ repo UserRepo } func NewUserUsecase(repo UserRepo) *UserUsecase { return &UserUsecase{repo: repo} } func (uc *UserUsecase) Login(ctx context.Context, username, password string) (*User, error) { u, err := uc.repo.FindUserByUsername(ctx, username) if err != nil || bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) != nil { return nil, ErrInvalidCredentials } uc.fallbackDefaultRouter(ctx, u) return u, nil } func (uc *UserUsecase) User(ctx context.Context, id uint) (*User, error) { user, err := uc.repo.FindUserByID(ctx, id) if err != nil { return nil, err } _ = uc.repo.FillDepartmentNamePaths(ctx, user) uc.fallbackDefaultRouter(ctx, user) return user, nil } func (uc *UserUsecase) fallbackDefaultRouter(ctx context.Context, user *User) { if user == nil || user.Authority.DefaultRouter == "" { return } menus, err := uc.repo.MenusByAuthority(ctx, user.AuthorityID) if err != nil || !menuNameExists(menus, user.Authority.DefaultRouter) { user.Authority.DefaultRouter = "404" } } func menuNameExists(menus []*Menu, name string) bool { for _, menu := range menus { if menu.Name == name || menuNameExists(menu.Children, name) { return true } } return false } func (uc *UserUsecase) Menus(ctx context.Context, authorityID uint) ([]*Menu, error) { return uc.repo.MenusByAuthority(ctx, authorityID) } func (uc *UserUsecase) ListUsers(ctx context.Context, page, pageSize int, filter *UserListFilter) ([]*User, int64, error) { return uc.repo.ListUsers(ctx, page, pageSize, filter) } func (uc *UserUsecase) CreateUser(ctx context.Context, user *User, authorityIDs []uint) (*User, error) { if user.AuthorityID == 0 && len(authorityIDs) > 0 { user.AuthorityID = authorityIDs[0] } authorityIDs = includeAuthority(authorityIDs, user.AuthorityID) hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) if err != nil { return nil, err } user.Password = string(hash) return uc.repo.CreateUserWithAuthorities(ctx, user, authorityIDs) } func (uc *UserUsecase) UpdateUser(ctx context.Context, user *User, authorityIDs []uint) error { authorityIDs = includeAuthority(authorityIDs, user.AuthorityID) return uc.repo.UpdateUserWithAuthorities(ctx, user, authorityIDs) } func (uc *UserUsecase) UpdateSelfUser(ctx context.Context, user *User) error { return uc.repo.UpdateSelfUser(ctx, user) } func includeAuthority(ids []uint, primary uint) []uint { if primary == 0 { return ids } for _, id := range ids { if id == primary { return ids } } return append(ids, primary) } func (uc *UserUsecase) DeleteUser(ctx context.Context, id uint) error { return uc.repo.DeleteUser(ctx, id) } func (uc *UserUsecase) ResetPassword(ctx context.Context, id uint, password string) error { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return err } return uc.repo.UpdatePassword(ctx, id, string(hash), false) } func (uc *UserUsecase) ChangePassword(ctx context.Context, id uint, oldPassword, newPassword string) error { user, err := uc.repo.FindUserByID(ctx, id) if err != nil { return err } if bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(oldPassword)) != nil { return ErrInvalidCredentials } hash, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { return err } return uc.repo.UpdatePassword(ctx, id, string(hash), true) } func (uc *UserUsecase) Authorities(ctx context.Context) ([]*Authority, error) { return uc.repo.ListAuthorities(ctx) } func (uc *UserUsecase) SetUserAuthorities(ctx context.Context, id uint, authorityIDs []uint) error { if len(authorityIDs) == 0 { return ErrInvalidCredentials } return uc.repo.SetUserAuthorities(ctx, id, authorityIDs) } func (uc *UserUsecase) SetUserAuthority(ctx context.Context, id, authorityID uint) error { return uc.repo.SetUserAuthority(ctx, id, authorityID) } func (uc *UserUsecase) SetUserSetting(ctx context.Context, id uint, setting map[string]any) error { return uc.repo.SetUserSetting(ctx, id, setting) }