316 lines
8.4 KiB
Go
316 lines
8.4 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/cache"
|
|
"git.nobla.cn/golang/aeus/pkg/errors"
|
|
"git.nobla.cn/golang/rest/types"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type (
|
|
userOptions struct {
|
|
db *gorm.DB
|
|
cache cache.Cache
|
|
}
|
|
|
|
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 WithUserCache(cache cache.Cache) UserOption {
|
|
return func(o *userOptions) {
|
|
o.cache = cache
|
|
}
|
|
}
|
|
|
|
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.GetUserMenuRequest) (res *pb.GetUserMenuResponse, err error) {
|
|
var (
|
|
uid string
|
|
permissions []*models.Permission
|
|
)
|
|
if uid, err = s.getUidFromContext(ctx); err != nil {
|
|
return
|
|
}
|
|
res = &pb.GetUserMenuResponse{}
|
|
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, opts.cache),
|
|
role: logic.NewRoleLogic(opts.db, opts.cache),
|
|
menu: logic.NewMenuLogic(opts.db, opts.cache),
|
|
department: logic.NewDepartmentLogic(opts.db, opts.cache),
|
|
}
|
|
}
|