Files
kra-new/internal/biz/system/user.go
T

202 lines
6.8 KiB
Go

package system
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
// 商务资料与系统身份共用主键;平台商务 ID 另由绑定关系持有。
Level int
ParentID uint
IDPath string
RealName string
QQNumber string
BusinessStatus int
RejectReason string
}
type UserListFilter struct {
Username, NickName, Phone, Email, OrderKey string
Desc bool
}
type UserRepo interface {
FindUserByUsername(context.Context, string) (*User, error)
FindUserByID(context.Context, uint) (*User, error)
FindUserByUUID(context.Context, string) (*User, error)
HasAuthorityMenu(context.Context, uint, string) (bool, error)
MenusByAuthority(context.Context, uint) ([]*Menu, error)
ListUsers(context.Context, int, int, *UserListFilter) ([]*User, int64, error)
CreateUserWithAuthorities(context.Context, *User, []uint) (*User, error)
UpdateUser(context.Context, *User) error
UpdateUserWithStatus(context.Context, *User, func(int) (int, error)) error
UpdateSelfUser(context.Context, *User) error
UpdateSelfUserWithStatus(context.Context, *User, func(int) (int, error)) 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 || u == nil || bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) != nil {
return nil, ErrInvalidCredentials
}
uc.fallbackDefaultRouter(ctx, u)
return u, nil
}
// UserByUUID resolves the identity from the UUID embedded in the JWT rather
// than from the mutable numeric ID used by administrative records.
func (uc *UserUsecase) UserByUUID(ctx context.Context, uuid string) (*User, error) {
user, err := uc.repo.FindUserByUUID(ctx, uuid)
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 {
return
}
hasMenu, err := uc.repo.HasAuthorityMenu(ctx, user.AuthorityID, user.Authority.DefaultRouter)
if err != nil {
return
}
if !hasMenu {
user.Authority.DefaultRouter = "404"
}
}
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.Enable == 0 {
user.Enable = 1
}
user.BusinessStatus = businessStatusForEnable(0, user.Enable)
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 {
// Role associations are updated first only when authorityIds was supplied,
// and then updates the basic user fields as a separate operation. Preserve
// that ordering and partial-failure behavior instead of coupling both writes
// into the data-layer transaction used by lower-level callers.
if len(authorityIDs) > 0 {
if err := uc.repo.SetUserAuthorities(ctx, user.ID, authorityIDs); err != nil {
return err
}
}
return uc.repo.UpdateUserWithStatus(ctx, user, func(current int) (int, error) {
return businessStatusForEnable(current, user.Enable), nil
})
}
// 启用代表恢复正常;未启用时保留待审核/驳回,避免编辑资料丢失业务审核状态。
func businessStatusForEnable(current, enable int) int {
if enable == 1 {
return 0
}
if current == 2 || current == 3 {
return current
}
return 1
}
func (uc *UserUsecase) UpdateSelfUser(ctx context.Context, user *User) error {
if user.Enable != 0 {
return uc.repo.UpdateSelfUserWithStatus(ctx, user, func(current int) (int, error) {
// 个人资料请求不能越过管理员刚写入的停用或审核状态。
if current != 0 {
return current, ErrUserDisabled
}
return businessStatusForEnable(current, user.Enable), nil
})
}
return uc.repo.UpdateSelfUser(ctx, user)
}
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 ErrAuthoritiesRequired
}
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)
}