Files
kra-new/internal/data/user.go
T

293 lines
12 KiB
Go

package data
import (
"context"
"encoding/json"
"errors"
"time"
"kra/internal/biz"
"github.com/google/uuid"
"gorm.io/gorm"
)
func (r *systemRepo) FindUserByUsername(ctx context.Context, username string) (*biz.User, error) {
var po userPO
if err := r.data.gormDB.WithContext(ctx).Where("username = ?", username).First(&po).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, biz.ErrUserNotFound
}
return nil, err
}
return r.toBizUser(ctx, &po)
}
func (r *systemRepo) FindUserByID(ctx context.Context, id uint) (*biz.User, error) {
var po userPO
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, biz.ErrUserNotFound
}
return nil, err
}
return r.toBizUser(ctx, &po)
}
func (r *systemRepo) toBizUser(ctx context.Context, po *userPO) (*biz.User, error) {
var authority authorityPO
if err := r.data.gormDB.WithContext(ctx).First(&authority, "authority_id = ?", po.AuthorityID).Error; err != nil {
return nil, err
}
var authorityPOs []authorityPO
if err := r.data.gormDB.WithContext(ctx).Table("sys_authorities").Joins("JOIN sys_user_authority ON sys_user_authority.sys_authority_authority_id = sys_authorities.authority_id").Where("sys_user_authority.sys_user_id = ? AND sys_authorities.deleted_at IS NULL", po.ID).Find(&authorityPOs).Error; err != nil {
return nil, err
}
authorities := make([]biz.Authority, 0, len(authorityPOs))
for _, item := range authorityPOs {
authorities = append(authorities, toBizAuthority(item))
}
setting := map[string]any(nil)
if po.OriginSetting != "" {
_ = json.Unmarshal([]byte(po.OriginSetting), &setting)
}
var departmentPOs []departmentPO
if err := r.data.gormDB.WithContext(ctx).Table("sys_departments").Joins("JOIN sys_user_departments ON sys_user_departments.sys_department_id = sys_departments.id").Where("sys_user_departments.sys_user_id = ? AND sys_departments.deleted_at IS NULL", po.ID).Find(&departmentPOs).Error; err != nil {
return nil, err
}
departments := make([]biz.Department, 0, len(departmentPOs))
var primary *biz.Department
for _, item := range departmentPOs {
value := deptFromPO(item)
departments = append(departments, *value)
if item.ID == po.DeptID {
copy := *value
primary = &copy
}
}
var positionPOs []positionPO
if err := r.data.gormDB.WithContext(ctx).Table("sys_positions").Joins("JOIN sys_user_positions ON sys_user_positions.sys_position_id = sys_positions.id").Where("sys_user_positions.sys_user_id = ? AND sys_positions.deleted_at IS NULL", po.ID).Find(&positionPOs).Error; err != nil {
return nil, err
}
positions := make([]biz.Position, 0, len(positionPOs))
for _, item := range positionPOs {
positions = append(positions, *posFromPO(item))
}
return &biz.User{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, UUID: po.UUID, Username: po.Username, Password: po.Password, NickName: po.NickName, HeaderImg: po.HeaderImg, AuthorityID: po.AuthorityID, Authority: toBizAuthority(authority), Authorities: authorities, DeptID: po.DeptID, Department: primary, Departments: departments, Positions: positions, Phone: po.Phone, Email: po.Email, Enable: po.Enable, OriginSetting: setting, MustChangePassword: po.MustChangePassword, PasswordUpdatedAt: po.PasswordUpdatedAt}, nil
}
func toBizAuthority(po authorityPO) biz.Authority {
return biz.Authority{AuthorityID: po.AuthorityID, AuthorityName: po.AuthorityName, ParentID: po.ParentID, DataScope: po.DataScope, DefaultRouter: po.DefaultRouter}
}
func (r *systemRepo) MenusByAuthority(ctx context.Context, authorityID uint) ([]*biz.Menu, error) {
var pos []menuPO
err := r.data.gormDB.WithContext(ctx).Table("sys_base_menus").
Joins("JOIN sys_authority_menus ON sys_authority_menus.sys_base_menu_id = sys_base_menus.id").
Where("sys_authority_menus.sys_authority_authority_id = ? AND sys_base_menus.deleted_at IS NULL", authorityID).
Order("sys_base_menus.sort asc, sys_base_menus.id asc").Scan(&pos).Error
if err != nil {
return nil, err
}
byID := make(map[uint]*biz.Menu, len(pos))
for _, po := range pos {
menu := &biz.Menu{ID: po.ID, ParentID: po.ParentID, Path: po.Path, Name: po.Name, Hidden: po.Hidden, Component: po.Component, Sort: po.Sort, ActiveName: po.ActiveName, KeepAlive: po.KeepAlive, DefaultMenu: po.DefaultMenu, Title: po.Title, Icon: po.Icon, CloseTab: po.CloseTab, TransitionType: po.TransitionType, Children: []*biz.Menu{}}
var parameters []menuParameterPO
if err := r.data.gormDB.WithContext(ctx).Where("sys_base_menu_id = ?", po.ID).Order("id").Find(&parameters).Error; err != nil {
return nil, err
}
for _, parameter := range parameters {
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{ID: parameter.ID, MenuID: parameter.MenuID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
}
var buttons []menuButtonPO
if err := r.data.gormDB.WithContext(ctx).Table("sys_base_menu_btns").Joins("JOIN sys_authority_btns ON sys_authority_btns.sys_base_menu_btn_id = sys_base_menu_btns.id").Where("sys_authority_btns.authority_id = ? AND sys_authority_btns.sys_menu_id = ?", authorityID, po.ID).Find(&buttons).Error; err != nil {
return nil, err
}
for _, button := range buttons {
menu.Buttons = append(menu.Buttons, &biz.MenuButton{ID: button.ID, Name: button.Name, Description: button.Description, MenuID: button.MenuID, AuthorityID: authorityID})
}
byID[po.ID] = menu
}
roots := make([]*biz.Menu, 0)
for _, po := range pos {
menu := byID[po.ID]
if parent := byID[po.ParentID]; parent != nil {
parent.Children = append(parent.Children, menu)
} else {
roots = append(roots, menu)
}
}
return roots, nil
}
func (r *systemRepo) ListUsers(ctx context.Context, page, pageSize int, filter *biz.UserListFilter) ([]*biz.User, int64, error) {
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 10
}
db := r.data.gormDB.WithContext(ctx).Model(&userPO{})
if scope, ok := biz.DataScopeFromContext(ctx); ok && !scope.All {
if scope.OwnerUserID != 0 {
db = db.Where("sys_users.id = ?", scope.OwnerUserID)
} else if len(scope.DepartmentIDs) > 0 {
db = db.Joins("JOIN sys_user_departments ON sys_user_departments.sys_user_id = sys_users.id").Where("sys_user_departments.sys_department_id IN ?", scope.DepartmentIDs).Distinct("sys_users.id")
} else {
db = db.Where("1 = 0")
}
}
order := "id desc"
if filter != nil {
if filter.Username != "" {
db = db.Where("username LIKE ?", "%"+filter.Username+"%")
}
if filter.NickName != "" {
db = db.Where("nick_name LIKE ?", "%"+filter.NickName+"%")
}
if filter.Phone != "" {
db = db.Where("phone LIKE ?", "%"+filter.Phone+"%")
}
if filter.Email != "" {
db = db.Where("email LIKE ?", "%"+filter.Email+"%")
}
allowed := map[string]bool{"id": true, "username": true, "nick_name": true, "phone": true, "email": true, "created_at": true}
if allowed[filter.OrderKey] {
order = filter.OrderKey
if filter.Desc {
order += " desc"
}
}
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
var pos []userPO
if err := db.Order(order).Offset((page - 1) * pageSize).Limit(pageSize).Find(&pos).Error; err != nil {
return nil, 0, err
}
users := make([]*biz.User, 0, len(pos))
for i := range pos {
user, err := r.toBizUser(ctx, &pos[i])
if err != nil {
return nil, 0, err
}
users = append(users, user)
}
return users, total, nil
}
func (r *systemRepo) CreateUser(ctx context.Context, user *biz.User) (*biz.User, error) {
if user.UUID == "" {
user.UUID = uuid.NewString()
}
if user.Enable == 0 {
user.Enable = 1
}
if user.AuthorityID == 0 {
user.AuthorityID = 888
}
if user.NickName == "" {
user.NickName = "系统用户"
}
now := time.Now()
po := userPO{UUID: user.UUID, Username: user.Username, Password: user.Password, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Phone: user.Phone, Email: user.Email, Enable: user.Enable, PasswordUpdatedAt: &now, MustChangePassword: user.MustChangePassword}
if err := r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&po).Error; err != nil {
return err
}
return tx.Create(&userAuthorityPO{SysUserID: po.ID, SysAuthorityAuthorityID: po.AuthorityID}).Error
}); err != nil {
return nil, err
}
return r.FindUserByID(ctx, po.ID)
}
func (r *systemRepo) UpdateUser(ctx context.Context, user *biz.User) error {
updates := map[string]any{"nick_name": user.NickName, "header_img": user.HeaderImg, "phone": user.Phone, "email": user.Email, "enable": user.Enable}
if user.AuthorityID != 0 {
updates["authority_id"] = user.AuthorityID
}
return r.data.gormDB.WithContext(ctx).Model(&userPO{}).Where("id = ?", user.ID).Updates(updates).Error
}
func (r *systemRepo) DeleteUser(ctx context.Context, id uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_user_id = ?", id).Delete(&userAuthorityPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_user_id = ?", id).Delete(&userDepartmentPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_user_id = ?", id).Delete(&userPositionPO{}).Error; err != nil {
return err
}
return tx.Delete(&userPO{}, id).Error
})
}
func (r *systemRepo) UpdatePassword(ctx context.Context, id uint, password string, clearMustChange bool) error {
now := time.Now()
updates := map[string]any{"password": password, "password_updated_at": now}
if clearMustChange {
updates["must_change_password"] = false
}
return r.data.gormDB.WithContext(ctx).Model(&userPO{}).Where("id = ?", id).Updates(updates).Error
}
func (r *systemRepo) ListAuthorities(ctx context.Context) ([]*biz.Authority, error) {
var pos []authorityPO
if err := r.data.gormDB.WithContext(ctx).Order("authority_id").Find(&pos).Error; err != nil {
return nil, err
}
result := make([]*biz.Authority, 0, len(pos))
for _, po := range pos {
a := toBizAuthority(po)
result = append(result, &a)
}
return result, nil
}
func (r *systemRepo) SetUserAuthorities(ctx context.Context, id uint, authorityIDs []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_user_id = ?", id).Delete(&userAuthorityPO{}).Error; err != nil {
return err
}
links := make([]userAuthorityPO, 0, len(authorityIDs))
for _, authorityID := range authorityIDs {
var count int64
if err := tx.Model(&authorityPO{}).Where("authority_id = ?", authorityID).Count(&count).Error; err != nil || count == 0 {
if err != nil {
return err
}
return errors.New("角色不存在")
}
links = append(links, userAuthorityPO{SysUserID: id, SysAuthorityAuthorityID: authorityID})
}
if err := tx.Create(&links).Error; err != nil {
return err
}
return tx.Model(&userPO{}).Where("id = ?", id).Update("authority_id", authorityIDs[0]).Error
})
}
func (r *systemRepo) SetUserAuthority(ctx context.Context, id, authorityID uint) error {
var count int64
if err := r.data.gormDB.WithContext(ctx).Model(&userAuthorityPO{}).Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityID).Count(&count).Error; err != nil {
return err
}
if count == 0 {
return errors.New("该用户无此角色")
}
return r.data.gormDB.WithContext(ctx).Model(&userPO{}).Where("id = ?", id).Update("authority_id", authorityID).Error
}
func (r *systemRepo) SetUserSetting(ctx context.Context, id uint, setting map[string]any) error {
value, err := json.Marshal(setting)
if err != nil {
return err
}
return r.data.gormDB.WithContext(ctx).Model(&userPO{}).Where("id = ?", id).Update("origin_setting", string(value)).Error
}
// ActiveName temporarily carries the parent menu name during seeding. It is
// cleared from responses by the service for these records.