241 lines
6.2 KiB
Go
241 lines
6.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"giter.top/smart/internal/iam/entity"
|
|
"giter.top/smart/internal/iam/repository"
|
|
"giter.top/smart/pkg/utils/id"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// UserService 用户
|
|
type UserService interface {
|
|
Create(ctx context.Context, tenantID string, req *CreateUserRequest) (*entity.User, error)
|
|
Update(ctx context.Context, tenantID string, uid string, req *UpdateUserRequest) (*entity.User, error)
|
|
Delete(ctx context.Context, tenantID string, ids []string) error
|
|
Get(ctx context.Context, tenantID string, uid string) (*entity.User, error)
|
|
List(ctx context.Context, tenantID string, q *UserListQuery) (*UserListResponse, error)
|
|
DataScopeForUser(ctx context.Context, userID string) (int16, error)
|
|
}
|
|
|
|
type CreateUserRequest struct {
|
|
UserName string `json:"user_name" binding:"required,max=64"`
|
|
Password string `json:"password" binding:"required,min=6,max=64"`
|
|
RealName string `json:"real_name" binding:"max=64"`
|
|
Phone string `json:"phone"`
|
|
Email string `json:"email"`
|
|
DeptID *string `json:"dept_id"`
|
|
DeptIDs []string `json:"dept_ids"`
|
|
RoleIDs []string `json:"role_ids"`
|
|
}
|
|
|
|
type UpdateUserRequest struct {
|
|
RealName *string `json:"real_name"`
|
|
Phone *string `json:"phone"`
|
|
Email *string `json:"email"`
|
|
DeptID *string `json:"dept_id"`
|
|
DeptIDs []string `json:"dept_ids"`
|
|
RoleIDs []string `json:"role_ids"`
|
|
Status *int16 `json:"status"`
|
|
Password *string `json:"password"`
|
|
}
|
|
|
|
type UserListQuery struct {
|
|
DeptID *string
|
|
RoleID *string
|
|
Keyword string
|
|
Status *int16
|
|
Page int
|
|
PageSize int
|
|
}
|
|
|
|
type UserListResponse struct {
|
|
Items []entity.User `json:"items"`
|
|
Total int64 `json:"total"`
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
TotalPages int `json:"total_pages"`
|
|
}
|
|
|
|
type userService struct {
|
|
users repository.UserRepository
|
|
roles repository.RoleRepository
|
|
}
|
|
|
|
func NewUserService(users repository.UserRepository, roles repository.RoleRepository) UserService {
|
|
return &userService{users: users, roles: roles}
|
|
}
|
|
|
|
func (s *userService) Create(ctx context.Context, tenantID string, req *CreateUserRequest) (*entity.User, error) {
|
|
ok, err := s.users.ExistsUserName(ctx, tenantID, req.UserName, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return nil, fmt.Errorf("账号已存在")
|
|
}
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
u := &entity.User{
|
|
ID: id.New(),
|
|
TenantID: tenantID,
|
|
UserName: req.UserName,
|
|
RealName: req.RealName,
|
|
Phone: req.Phone,
|
|
Email: req.Email,
|
|
PasswordHash: string(hash),
|
|
Status: 1,
|
|
}
|
|
if req.DeptID != nil {
|
|
u.DeptID = req.DeptID
|
|
}
|
|
if err := s.users.Create(ctx, u); err != nil {
|
|
return nil, err
|
|
}
|
|
depts := req.DeptIDs
|
|
primary := ""
|
|
if req.DeptID != nil {
|
|
primary = *req.DeptID
|
|
}
|
|
if len(depts) == 0 && primary != "" {
|
|
depts = []string{primary}
|
|
}
|
|
if len(depts) > 0 {
|
|
if primary == "" {
|
|
primary = depts[0]
|
|
}
|
|
u.DeptID = &primary
|
|
_ = s.users.Update(ctx, u)
|
|
if err := s.users.ReplaceUserDepts(ctx, u.ID, primary, depts); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if len(req.RoleIDs) > 0 {
|
|
if err := s.users.ReplaceUserRoles(ctx, u.ID, req.RoleIDs); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (s *userService) Update(ctx context.Context, tenantID string, uid string, req *UpdateUserRequest) (*entity.User, error) {
|
|
u, err := s.users.GetByID(ctx, uid)
|
|
if err != nil {
|
|
if errors.Is(err, repository.ErrNotFound) {
|
|
return nil, fmt.Errorf("用户不存在")
|
|
}
|
|
return nil, err
|
|
}
|
|
if u.TenantID != tenantID {
|
|
return nil, fmt.Errorf("用户不属于当前租户")
|
|
}
|
|
if req.RealName != nil {
|
|
u.RealName = *req.RealName
|
|
}
|
|
if req.Phone != nil {
|
|
u.Phone = *req.Phone
|
|
}
|
|
if req.Email != nil {
|
|
u.Email = *req.Email
|
|
}
|
|
if req.Status != nil {
|
|
u.Status = *req.Status
|
|
}
|
|
if req.Password != nil && *req.Password != "" {
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(*req.Password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
u.PasswordHash = string(hash)
|
|
}
|
|
if err := s.users.Update(ctx, u); err != nil {
|
|
return nil, err
|
|
}
|
|
if req.DeptIDs != nil || req.DeptID != nil {
|
|
depts := req.DeptIDs
|
|
primary := ""
|
|
if req.DeptID != nil {
|
|
primary = *req.DeptID
|
|
u.DeptID = req.DeptID
|
|
_ = s.users.Update(ctx, u)
|
|
}
|
|
if len(depts) == 0 && primary != "" {
|
|
depts = []string{primary}
|
|
}
|
|
if primary == "" && len(depts) > 0 {
|
|
primary = depts[0]
|
|
}
|
|
if err := s.users.ReplaceUserDepts(ctx, u.ID, primary, depts); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if req.RoleIDs != nil {
|
|
if err := s.users.ReplaceUserRoles(ctx, u.ID, req.RoleIDs); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (s *userService) Delete(ctx context.Context, tenantID string, ids []string) error {
|
|
for _, uid := range ids {
|
|
u, err := s.users.GetByID(ctx, uid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if u.TenantID != tenantID {
|
|
return fmt.Errorf("用户 %s 不属于当前租户", uid)
|
|
}
|
|
if err := s.users.Delete(ctx, uid); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *userService) Get(ctx context.Context, tenantID string, uid string) (*entity.User, error) {
|
|
u, err := s.users.GetByID(ctx, uid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if u.TenantID != tenantID {
|
|
return nil, fmt.Errorf("用户不属于当前租户")
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (s *userService) List(ctx context.Context, tenantID string, q *UserListQuery) (*UserListResponse, error) {
|
|
if q.Page <= 0 {
|
|
q.Page = 1
|
|
}
|
|
if q.PageSize <= 0 {
|
|
q.PageSize = 10
|
|
}
|
|
rows, total, err := s.users.List(ctx, tenantID, q.DeptID, q.RoleID, q.Keyword, q.Status, q.Page, q.PageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tp := int(total) / q.PageSize
|
|
if int(total)%q.PageSize != 0 {
|
|
tp++
|
|
}
|
|
return &UserListResponse{Items: rows, Total: total, Page: q.Page, PageSize: q.PageSize, TotalPages: tp}, nil
|
|
}
|
|
|
|
func (s *userService) DataScopeForUser(ctx context.Context, userID string) (int16, error) {
|
|
roles, err := s.roles.ListRolesByUser(ctx, userID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
scopes := make([]int16, 0, len(roles))
|
|
for _, r := range roles {
|
|
scopes = append(scopes, r.DataScope)
|
|
}
|
|
return MergeDataScope(scopes), nil
|
|
}
|