aeus-admin/service/user.go

286 lines
8.0 KiB
Go

package service
import (
"context"
"fmt"
"time"
"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"
"gorm.io/gorm"
)
type (
userOptions struct {
db *gorm.DB
}
UserOption func(o *userOptions)
UserService struct {
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) getRolePermissions(db *gorm.DB, role string) (permissions []*models.Permission, err error) {
permissions = make([]*models.Permission, 0)
err = db.Where("permission IN (SELECT permission FROM role_permissions WHERE role = ?)", role).Find(&permissions).Error
return
}
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) {
userModel := &models.User{}
if err = tx.Where("uid=?", uid).First(userModel).Error; err != nil {
return nil, err
}
items := make([]*models.Menu, 0)
if err = tx.Find(&items).Error; err != nil {
return nil, err
}
if userModel.Admin {
permissions = make([]*models.Permission, 0)
if err = tx.Find(&permissions).Error; err != nil {
return nil, err
}
} else {
if permissions, err = s.getRolePermissions(tx, userModel.Role); err != nil {
return nil, err
}
}
return s.recursiveNestedMenu(ctx, "", req.Permission, items, permissions), nil
},
dbcache.WithDB(s.opts.db),
dbcache.WithDependency(dbcache.NewSqlDependency("SELECT `updated_at` FROM users WHERE `uid`=?", uid)),
)
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)),
// dbcache.WithDepend("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 = dbcache.TryCache(ctx, fmt.Sprintf("user:permissions:%s", req.Uid), func(tx *gorm.DB) ([]string, error) {
var ss []string
err = tx.Select("permission").Model(&models.RolePermission{}).
Joins("LEFT JOIN users on users.role = role_permissions.role").
Where("users.uid = ?", req.Uid).
Pluck("permission", &ss).
Error
return ss, err
}, dbcache.WithCacheDuration(time.Minute), dbcache.WithDB(s.opts.db))
return
}
func (s *UserService) GetUserLabels(ctx context.Context, req *pb.GetUserLabelRequest) (res *pb.GetUserLabelResponse, err error) {
res = &pb.GetUserLabelResponse{}
res.Data, err = dbcache.TryCache(ctx, fmt.Sprintf("user:labels"), func(tx *gorm.DB) ([]*pb.LabelValue, error) {
values := make([]*models.User, 0)
if err = tx.Find(&values).Error; err == nil {
items := make([]*pb.LabelValue, 0, len(values))
for _, v := range values {
items = append(items, &pb.LabelValue{
Label: v.Username,
Value: v.Uid,
})
}
return items, nil
} else {
return nil, err
}
},
dbcache.WithDB(s.opts.db),
dbcache.WithDependency(dbcache.NewSqlDependency("SELECT MAX(`updated_at`) FROM users")),
)
return
}
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,
}
}