aeus-admin/service/user.go

308 lines
8.2 KiB
Go

package service
import (
"context"
"fmt"
"strconv"
"time"
"git.nobla.cn/golang/aeus-admin/internal/logic"
"git.nobla.cn/golang/aeus-admin/models"
"git.nobla.cn/golang/aeus-admin/pb"
"git.nobla.cn/golang/aeus-admin/pkg/dbcache"
"git.nobla.cn/golang/aeus/middleware/auth"
"git.nobla.cn/golang/aeus/pkg/errors"
"git.nobla.cn/golang/rest/types"
"gorm.io/gorm"
)
type (
userOptions struct {
db *gorm.DB
}
UserOption func(o *userOptions)
UserService struct {
user *logic.User
menu *logic.Menu
role *logic.Role
department *logic.Department
opts *userOptions
}
)
func WithUserDB(db *gorm.DB) UserOption {
return func(o *userOptions) {
o.db = db
}
}
func (s *UserService) getUidFromContext(ctx context.Context) (string, error) {
if claims, ok := auth.FromContext(ctx); !ok {
return "", errors.ErrAccessDenied
} else {
return claims.GetSubject()
}
}
func (s *UserService) hasPermission(menuName string, permissions []*models.Permission) bool {
for _, permission := range permissions {
if permission.Menu == menuName {
return true
}
}
return false
}
func (s *UserService) getPermissions(menuName string, permissions []*models.Permission) []*pb.PermissionItem {
ss := make([]*pb.PermissionItem, 0, 10)
for _, permission := range permissions {
if permission.Menu == menuName {
ss = append(ss, &pb.PermissionItem{
Value: permission.Permission,
Label: permission.Label,
})
}
}
return ss
}
func (s *UserService) recursiveNestedMenu(ctx context.Context, parent string, perm bool, menus []*models.Menu, permissions []*models.Permission) []*pb.MenuItem {
values := make([]*pb.MenuItem, 0)
for _, row := range menus {
if row.Parent == parent {
if !row.Public && !s.hasPermission(row.Name, permissions) {
continue
}
v := &pb.MenuItem{
Label: row.Label,
Name: row.Name,
Icon: row.Icon,
Hidden: row.Hidden,
Route: row.Uri,
Public: row.Public,
View: row.ViewPath,
Children: s.recursiveNestedMenu(ctx, row.Name, perm, menus, permissions),
}
if perm {
v.Permissions = s.getPermissions(row.Name, permissions)
}
values = append(values, v)
}
}
return values
}
func (s *UserService) GetMenus(ctx context.Context, req *pb.GetMenuRequest) (res *pb.GetMenuResponse, err error) {
var (
uid string
permissions []*models.Permission
)
if uid, err = s.getUidFromContext(ctx); err != nil {
return
}
res = &pb.GetMenuResponse{}
res.Data, err = dbcache.TryCache(ctx, fmt.Sprintf("user:menu:%s:%v", uid, req.Permission), func(tx *gorm.DB) ([]*pb.MenuItem, error) {
var (
userModel *models.User
menus []*models.Menu
)
userModel = &models.User{}
if err = tx.Where("uid=?", uid).First(userModel).Error; err != nil {
return nil, err
}
if menus, err = s.menu.GetMenus(ctx); err != nil {
return nil, err
}
roleName := userModel.Role
if userModel.Admin {
roleName = ""
}
if permissions, err = s.role.GetPermissions(ctx, roleName); err != nil {
return nil, err
}
return s.recursiveNestedMenu(ctx, "", req.Permission, menus, permissions), nil
},
dbcache.WithDB(s.opts.db),
dbcache.WithCacheDuration(time.Second*10),
)
return
}
func (s *UserService) GetProfile(ctx context.Context, req *pb.GetProfileRequest) (res *pb.GetProfileResponse, err error) {
if req.Uid == "" {
if req.Uid, err = s.getUidFromContext(ctx); err != nil {
return
}
}
res, err = dbcache.TryCache(ctx, fmt.Sprintf("user:profile:%s", req.Uid), func(tx *gorm.DB) (*pb.GetProfileResponse, error) {
profile := &pb.GetProfileResponse{}
err = tx.Table("users AS u").
Select("u.uid as uid", "u.username", "u.avatar", "u.email", "u.description", "u.role", "u.admin").
Where("u.uid=? ", req.Uid).
First(profile).Error
return profile, err
},
dbcache.WithDB(s.opts.db),
dbcache.WithDependency(dbcache.NewSqlDependency("SELECT `updated_at` FROM users WHERE `uid`=?", req.Uid)),
)
return
}
func (s *UserService) UpdateProfile(ctx context.Context, req *pb.UpdateProfileRequest) (res *pb.UpdateProfileResponse, err error) {
if req.Uid == "" {
if req.Uid, err = s.getUidFromContext(ctx); err != nil {
return
}
}
userModel := &models.User{}
tx := s.opts.db.WithContext(ctx)
if err = tx.Where("uid=?", req.Uid).First(userModel).Error; err != nil {
return
}
if req.Avatar != "" {
userModel.Avatar = req.Avatar
}
if req.Email != "" {
userModel.Email = req.Email
}
if req.Username != "" {
userModel.Username = req.Username
}
if req.Description != "" {
userModel.Description = req.Description
}
if err = tx.Save(userModel).Error; err == nil {
res = &pb.UpdateProfileResponse{
Uid: userModel.Uid,
}
}
return
}
func (s *UserService) ResetPassword(ctx context.Context, req *pb.ResetPasswordRequest) (res *pb.ResetPasswordResponse, err error) {
if req.Uid == "" {
if req.Uid, err = s.getUidFromContext(ctx); err != nil {
return
}
}
userModel := &models.User{}
tx := s.opts.db.WithContext(ctx)
if err = tx.Where("uid=?", req.Uid).First(userModel).Error; err != nil {
return
}
if userModel.Password == req.OldPassword {
if err = tx.Where("uid=?", req.Uid).Model(&models.User{}).UpdateColumn("password", req.NewPassword).Error; err == nil {
res = &pb.ResetPasswordResponse{
Uid: userModel.Uid,
}
}
} else {
err = errors.Format(errors.AccessDenied, "invalid old password")
}
return
}
func (s *UserService) GetPermissions(ctx context.Context, req *pb.GetPermissionRequest) (res *pb.GetPermissionResponse, err error) {
if req.Uid == "" {
if req.Uid, err = s.getUidFromContext(ctx); err != nil {
return
}
}
res = &pb.GetPermissionResponse{
Uid: req.Uid,
}
res.Permissions, err = s.user.GetPermissions(ctx, req.Uid)
return
}
func (s *UserService) GetUserLabels(ctx context.Context, req *pb.GetUserLabelRequest) (res *pb.GetUserLabelResponse, err error) {
res = &pb.GetUserLabelResponse{}
var values []*types.TypeValue[string]
if values, err = s.user.GetLabels(ctx); err == nil {
res.Data = make([]*pb.LabelValue, 0, len(values))
for _, row := range values {
res.Data = append(res.Data, &pb.LabelValue{
Label: row.Label,
Value: row.Value,
})
}
}
return
}
func (s *UserService) DepartmentUserNested(ctx context.Context) []*types.NestedValue[string] {
var (
err error
users []*models.User
departments []*models.Department
)
if departments, err = s.department.GetDepartments(ctx); err != nil {
return nil
}
if users, err = s.user.GeUsers(ctx); err != nil {
return nil
}
depts := s.department.RecursiveDepartment(ctx, 0, 0, departments)
values := make([]*types.NestedValue[string], 0)
for _, dept := range depts {
v := &types.NestedValue[string]{
Label: dept.Label,
Value: strconv.FormatInt(dept.Value, 10),
Children: make([]*types.NestedValue[string], 0),
}
for _, user := range users {
if strconv.FormatInt(user.DeptId, 10) == v.Value {
v.Children = append(v.Children, &types.NestedValue[string]{
Label: fmt.Sprintf("%s(%s)", user.Username, user.Uid),
Value: user.Uid,
})
}
}
values = append(values, v)
}
return values
}
func (s *UserService) GetUserTags(ctx context.Context, req *pb.GetUserTagRequest) (res *pb.GetUserTagResponse, err error) {
res = &pb.GetUserTagResponse{}
res.Data, err = dbcache.TryCache(ctx, fmt.Sprintf("user:tags"), func(tx *gorm.DB) ([]*pb.LabelValue, error) {
values := make([]*models.User, 0)
if err = tx.Select("DISTINCT(`tag`) AS `tag`").Find(&values).Error; err == nil {
items := make([]*pb.LabelValue, 0, len(values))
for _, v := range values {
if v.Tag == "" {
continue
}
items = append(items, &pb.LabelValue{
Label: v.Tag,
Value: v.Tag,
})
}
return items, nil
} else {
return nil, err
}
},
dbcache.WithDB(s.opts.db),
dbcache.WithDependency(dbcache.NewSqlDependency("SELECT MAX(`updated_at`) FROM users")),
)
return
}
func NewUserService(cbs ...UserOption) *UserService {
opts := &userOptions{}
for _, cb := range cbs {
cb(opts)
}
return &UserService{
opts: opts,
user: logic.NewUserLogic(opts.db),
role: logic.NewRoleLogic(opts.db),
menu: logic.NewMenuLogic(opts.db),
department: logic.NewDepartmentLogic(opts.db),
}
}