kra-new/internal/data/access.go

1141 lines
40 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package data
import (
"context"
"errors"
"strconv"
"strings"
"time"
"kra/internal/biz"
"github.com/casbin/casbin/v3"
casbinmodel "github.com/casbin/casbin/v3/model"
"gorm.io/gorm"
)
type apiPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Path string `gorm:"uniqueIndex:idx_api_path_method"`
Description string
APIGroup string `gorm:"column:api_group"`
Method string `gorm:"uniqueIndex:idx_api_path_method"`
}
func (apiPO) TableName() string { return "sys_apis" }
type ignoredAPIPO struct {
Path string `gorm:"primaryKey"`
Method string `gorm:"primaryKey"`
}
func (ignoredAPIPO) TableName() string { return "sys_ignore_apis" }
type authorityAPIPO struct {
AuthorityID uint `gorm:"primaryKey;column:authority_id"`
APIID uint `gorm:"primaryKey;column:api_id"`
}
func (authorityAPIPO) TableName() string { return "sys_authority_apis" }
type menuButtonPO struct {
ID uint `gorm:"primaryKey"`
Name string
Description string `gorm:"column:desc"`
MenuID uint `gorm:"column:sys_base_menu_id"`
}
func (menuButtonPO) TableName() string { return "sys_base_menu_btns" }
type authorityButtonPO struct {
AuthorityID uint `gorm:"primaryKey;column:authority_id"`
MenuID uint `gorm:"primaryKey;column:sys_menu_id"`
ButtonID uint `gorm:"primaryKey;column:sys_base_menu_btn_id"`
}
func (authorityButtonPO) TableName() string { return "sys_authority_btns" }
type departmentPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string `gorm:"index"`
ParentID uint
Ancestors string
Sort int
LeaderID uint
Status bool `gorm:"default:true"`
}
func (departmentPO) TableName() string { return "sys_departments" }
type positionPO struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string `gorm:"index"`
Code string
Sort int
Status bool `gorm:"default:true"`
Remark string
}
func (positionPO) TableName() string { return "sys_positions" }
type userDepartmentPO struct {
UserID uint `gorm:"primaryKey;column:sys_user_id"`
DepartmentID uint `gorm:"primaryKey;column:sys_department_id"`
}
func (userDepartmentPO) TableName() string { return "sys_user_departments" }
type userPositionPO struct {
UserID uint `gorm:"primaryKey;column:sys_user_id"`
PositionID uint `gorm:"primaryKey;column:sys_position_id"`
}
func (userPositionPO) TableName() string { return "sys_user_positions" }
type authorityDepartmentPO struct {
AuthorityID uint `gorm:"primaryKey;column:authority_id"`
DepartmentID uint `gorm:"primaryKey;column:department_id"`
}
func (authorityDepartmentPO) TableName() string { return "sys_authority_departments" }
type accessRepo struct{ data *Data }
func NewAccessRepo(data *Data) biz.AccessRepo { return &accessRepo{data: data} }
func (r *accessRepo) CreateAuthority(ctx context.Context, value *biz.Authority) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var count int64
if err := tx.Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("存在相同角色id")
}
if value.DefaultRouter == "" {
value.DefaultRouter = "dashboard"
}
if err := tx.Create(&authorityPO{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}).Error; err != nil {
return err
}
var dashboard menuPO
if err := tx.Where("name = ?", "dashboard").First(&dashboard).Error; err == nil {
if err = tx.Create(&authorityMenuPO{SysAuthorityAuthorityID: value.AuthorityID, SysBaseMenuID: dashboard.ID}).Error; err != nil {
return err
}
}
defaults := []struct{ path, method string }{{"/menu/getMenu", "POST"}, {"/jwt/jsonInBlacklist", "POST"}, {"/user/changePassword", "POST"}, {"/user/setUserAuthority", "POST"}, {"/user/getUserInfo", "GET"}, {"/user/setSelfInfo", "PUT"}, {"/fileUploadAndDownload/upload", "POST"}, {"/sysDictionary/findSysDictionary", "GET"}}
for _, item := range defaults {
var api apiPO
if err := tx.Where("path = ? AND method = ?", item.path, item.method).First(&api).Error; err == nil {
if err = tx.Create(&authorityAPIPO{AuthorityID: value.AuthorityID, APIID: api.ID}).Error; err != nil {
return err
}
}
}
return nil
})
}
func (r *accessRepo) CopyAuthority(ctx context.Context, sourceID uint, value *biz.Authority) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var count int64
if err := tx.Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Count(&count).Error; err != nil {
return err
}
if count != 0 {
return errors.New("存在相同角色id")
}
if err := tx.Create(&authorityPO{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}).Error; err != nil {
return err
}
copyLinks := func(table string, destination any, columns map[string]any) error {
return tx.Table(table).Where(columns).Find(destination).Error
}
var menus []authorityMenuPO
if err := copyLinks("sys_authority_menus", &menus, map[string]any{"sys_authority_authority_id": sourceID}); err != nil {
return err
}
for i := range menus {
menus[i].SysAuthorityAuthorityID = value.AuthorityID
}
if len(menus) > 0 {
if err := tx.Create(&menus).Error; err != nil {
return err
}
}
var apis []authorityAPIPO
if err := copyLinks("sys_authority_apis", &apis, map[string]any{"authority_id": sourceID}); err != nil {
return err
}
for i := range apis {
apis[i].AuthorityID = value.AuthorityID
}
if len(apis) > 0 {
if err := tx.Create(&apis).Error; err != nil {
return err
}
}
var buttons []authorityButtonPO
if err := copyLinks("sys_authority_btns", &buttons, map[string]any{"authority_id": sourceID}); err != nil {
return err
}
for i := range buttons {
buttons[i].AuthorityID = value.AuthorityID
}
if len(buttons) > 0 {
if err := tx.Create(&buttons).Error; err != nil {
return err
}
}
var departments []authorityDepartmentPO
if err := copyLinks("sys_authority_departments", &departments, map[string]any{"authority_id": sourceID}); err != nil {
return err
}
for i := range departments {
departments[i].AuthorityID = value.AuthorityID
}
if len(departments) > 0 {
return tx.Create(&departments).Error
}
return nil
})
}
func (r *accessRepo) UpdateAuthority(ctx context.Context, value *biz.Authority) error {
return r.data.gormDB.WithContext(ctx).Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Updates(map[string]any{"authority_name": value.AuthorityName, "parent_id": value.ParentID, "data_scope": value.DataScope, "default_router": value.DefaultRouter}).Error
}
func (r *accessRepo) DeleteAuthority(ctx context.Context, id uint) error {
if id == 888 {
return errors.New("不能删除超级管理员")
}
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var users, children int64
if err := tx.Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Count(&users).Error; err != nil {
return err
}
if users > 0 {
return errors.New("此角色有用户正在使用禁止删除")
}
if err := tx.Model(&userPO{}).Where("authority_id = ?", id).Count(&users).Error; err != nil {
return err
}
if users > 0 {
return errors.New("此角色有用户正在使用禁止删除")
}
if err := tx.Model(&authorityPO{}).Where("parent_id = ?", id).Count(&children).Error; err != nil {
return err
}
if children > 0 {
return errors.New("此角色存在子角色不允许删除")
}
if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
if err := tx.Where("authority_id = ?", id).Delete(&authorityAPIPO{}).Error; err != nil {
return err
}
if err := tx.Where("authority_id = ?", id).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
if err := tx.Where("authority_id = ?", id).Delete(&authorityDepartmentPO{}).Error; err != nil {
return err
}
return tx.Delete(&authorityPO{}, "authority_id = ?", id).Error
})
}
func (r *accessRepo) 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
}
out := make([]*biz.Authority, 0, len(pos))
for _, po := range pos {
v := toBizAuthority(po)
out = append(out, &v)
}
return out, nil
}
func (r *accessRepo) SetAuthorityMenus(ctx context.Context, id uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
links := make([]authorityMenuPO, 0, len(ids))
for _, mid := range ids {
links = append(links, authorityMenuPO{SysAuthorityAuthorityID: id, SysBaseMenuID: mid})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) AuthorityMenuIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityMenuPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_base_menu_id", &ids).Error
return ids, err
}
func (r *accessRepo) SetAuthorityUsers(ctx context.Context, id uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var oldIDs []uint
if err := tx.Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_user_id", &oldIDs).Error; err != nil {
return err
}
if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&userAuthorityPO{}).Error; err != nil {
return err
}
links := make([]userAuthorityPO, 0, len(ids))
for _, uid := range ids {
links = append(links, userAuthorityPO{SysUserID: uid, SysAuthorityAuthorityID: id})
}
if len(links) > 0 {
if err := tx.Create(&links).Error; err != nil {
return err
}
}
selected := map[uint]bool{}
for _, uid := range ids {
selected[uid] = true
}
for _, uid := range oldIDs {
if selected[uid] {
continue
}
var user userPO
if err := tx.First(&user, uid).Error; err == nil && user.AuthorityID == id {
if err = tx.Model(&user).Update("authority_id", 888).Error; err != nil {
return err
}
fallback := userAuthorityPO{SysUserID: uid, SysAuthorityAuthorityID: 888}
if err = tx.FirstOrCreate(&fallback, fallback).Error; err != nil {
return err
}
}
}
return nil
})
}
func (r *accessRepo) AuthorityUserIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_user_id", &ids).Error
return ids, err
}
func (r *accessRepo) SetDataScope(ctx context.Context, id uint, scope int, deptIDs []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&authorityPO{}).Where("authority_id = ?", id).Update("data_scope", scope).Error; err != nil {
return err
}
if err := tx.Where("authority_id = ?", id).Delete(&authorityDepartmentPO{}).Error; err != nil {
return err
}
links := make([]authorityDepartmentPO, 0, len(deptIDs))
for _, did := range deptIDs {
links = append(links, authorityDepartmentPO{AuthorityID: id, DepartmentID: did})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) DataScopeDepartmentIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityDepartmentPO{}).Where("authority_id = ?", id).Pluck("department_id", &ids).Error
return ids, err
}
func (r *accessRepo) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) {
if authorityID == 888 {
return biz.DataScope{All: true}, nil
}
var authority authorityPO
if err := r.data.gormDB.WithContext(ctx).First(&authority, "authority_id = ?", authorityID).Error; err != nil {
return biz.DataScope{}, err
}
if authority.DataScope == 1 {
return biz.DataScope{All: true}, nil
}
if authority.DataScope == 4 {
return biz.DataScope{OwnerUserID: userID}, nil
}
var ids []uint
if authority.DataScope == 5 {
var err error
ids, err = r.DataScopeDepartmentIDs(ctx, authorityID)
if err != nil {
return biz.DataScope{}, err
}
} else {
if err := r.data.gormDB.WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_user_id = ?", userID).Pluck("sys_department_id", &ids).Error; err != nil {
return biz.DataScope{}, err
}
if authority.DataScope == 2 && len(ids) > 0 {
var departments []departmentPO
if err := r.data.gormDB.WithContext(ctx).Find(&departments).Error; err != nil {
return biz.DataScope{}, err
}
selected := make(map[uint]bool, len(ids))
for _, id := range ids {
selected[id] = true
}
for _, department := range departments {
for _, part := range strings.Split(department.Ancestors, ",") {
value, _ := strconv.ParseUint(part, 10, 64)
if selected[uint(value)] {
selected[department.ID] = true
break
}
}
}
ids = ids[:0]
for id := range selected {
ids = append(ids, id)
}
}
}
return biz.DataScope{DepartmentIDs: ids}, nil
}
func menuFromPO(po menuPO) *biz.Menu {
return &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}
}
func menuToPO(v *biz.Menu) menuPO {
return menuPO{ID: v.ID, ParentID: v.ParentID, Path: v.Path, Name: v.Name, Hidden: v.Hidden, Component: v.Component, Sort: v.Sort, ActiveName: v.ActiveName, KeepAlive: v.KeepAlive, DefaultMenu: v.DefaultMenu, Title: v.Title, Icon: v.Icon, CloseTab: v.CloseTab, TransitionType: v.TransitionType}
}
func replaceMenuRelations(tx *gorm.DB, menu *biz.Menu) error {
var oldButtonIDs []uint
if err := tx.Model(&menuButtonPO{}).Where("sys_base_menu_id = ?", menu.ID).Pluck("id", &oldButtonIDs).Error; err != nil {
return err
}
if len(oldButtonIDs) > 0 {
if err := tx.Where("sys_base_menu_btn_id IN ?", oldButtonIDs).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
}
if err := tx.Where("sys_base_menu_id = ?", menu.ID).Delete(&menuButtonPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", menu.ID).Delete(&menuParameterPO{}).Error; err != nil {
return err
}
buttons := make([]menuButtonPO, 0, len(menu.Buttons))
for _, button := range menu.Buttons {
buttons = append(buttons, menuButtonPO{Name: button.Name, Description: button.Description, MenuID: menu.ID})
}
if len(buttons) > 0 {
if err := tx.Create(&buttons).Error; err != nil {
return err
}
}
parameters := make([]menuParameterPO, 0, len(menu.Parameters))
for _, parameter := range menu.Parameters {
parameters = append(parameters, menuParameterPO{MenuID: menu.ID, Type: parameter.Type, Key: parameter.Key, Value: parameter.Value})
}
if len(parameters) > 0 {
return tx.Create(&parameters).Error
}
return nil
}
func (r *accessRepo) CreateMenu(ctx context.Context, v *biz.Menu) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var count int64
if err := tx.Model(&menuPO{}).Where("name = ?", v.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("存在重复name请修改name")
}
if v.ParentID != 0 {
var parent menuPO
if err := tx.First(&parent, v.ParentID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("父菜单不存在")
}
return err
}
if err := tx.Model(&menuPO{}).Where("parent_id = ?", v.ParentID).Count(&count).Error; err != nil {
return err
}
if count == 0 {
if err := tx.Model(&authorityPO{}).Where("default_router = ?", parent.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("父菜单已被其他角色的首页占用,请先释放父菜单的首页权限")
}
if err := tx.Where("sys_base_menu_id = ?", v.ParentID).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
}
}
po := menuToPO(v)
if err := tx.Create(&po).Error; err != nil {
return err
}
v.ID = po.ID
return replaceMenuRelations(tx, v)
})
}
func (r *accessRepo) UpdateMenu(ctx context.Context, v *biz.Menu) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var old menuPO
if err := tx.First(&old, v.ID).Error; err != nil {
return err
}
var count int64
if old.Name != v.Name {
if err := tx.Model(&menuPO{}).Where("id <> ? AND name = ?", v.ID, v.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("存在相同name修改失败")
}
}
if err := replaceMenuRelations(tx, v); err != nil {
return err
}
values := map[string]any{"parent_id": v.ParentID, "path": v.Path, "name": v.Name, "hidden": v.Hidden, "component": v.Component, "sort": v.Sort, "active_name": v.ActiveName, "keep_alive": v.KeepAlive, "default_menu": v.DefaultMenu, "title": v.Title, "icon": v.Icon, "close_tab": v.CloseTab, "transition_type": v.TransitionType}
return tx.Model(&old).Updates(values).Error
})
}
func (r *accessRepo) DeleteMenu(ctx context.Context, id uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var count int64
if err := tx.Model(&menuPO{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("此菜单存在子菜单不可删除")
}
var menu menuPO
if err := tx.First(&menu, id).Error; err != nil {
return errors.New("记录不存在")
}
if err := tx.Model(&authorityPO{}).Where("default_router = ?", menu.Name).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("此菜单有角色正在作为首页,不可删除")
}
var buttonIDs []uint
if err := tx.Model(&menuButtonPO{}).Where("sys_base_menu_id = ?", id).Pluck("id", &buttonIDs).Error; err != nil {
return err
}
if err := tx.Where("sys_menu_id = ?", id).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&menuButtonPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&menuParameterPO{}).Error; err != nil {
return err
}
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
return tx.Delete(&menuPO{}, id).Error
})
}
func (r *accessRepo) FindMenu(ctx context.Context, id uint) (*biz.Menu, error) {
var po menuPO
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err
}
menu := menuFromPO(po)
if err := r.loadMenuParameters(ctx, menu); err != nil {
return nil, err
}
return menu, nil
}
func (r *accessRepo) loadMenuParameters(ctx context.Context, menu *biz.Menu) error {
var pos []menuParameterPO
if err := r.data.gormDB.WithContext(ctx).Where("sys_base_menu_id = ?", menu.ID).Order("id").Find(&pos).Error; err != nil {
return err
}
menu.Parameters = make([]*biz.MenuParameter, 0, len(pos))
for _, po := range pos {
menu.Parameters = append(menu.Parameters, &biz.MenuParameter{ID: po.ID, MenuID: po.MenuID, Type: po.Type, Key: po.Key, Value: po.Value})
}
return nil
}
func (r *accessRepo) ListMenus(ctx context.Context) ([]*biz.Menu, error) {
var pos []menuPO
if err := r.data.gormDB.WithContext(ctx).Order("sort,id").Find(&pos).Error; err != nil {
return nil, err
}
out := make([]*biz.Menu, 0, len(pos))
for _, po := range pos {
menu := menuFromPO(po)
if err := r.loadMenuParameters(ctx, menu); err != nil {
return nil, err
}
out = append(out, menu)
}
return out, nil
}
func (r *accessRepo) MenuRoleIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityMenuPO{}).Where("sys_base_menu_id = ?", id).Pluck("sys_authority_authority_id", &ids).Error
return ids, err
}
func (r *accessRepo) SetMenuRoles(ctx context.Context, id uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_base_menu_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
return err
}
links := make([]authorityMenuPO, 0, len(ids))
for _, aid := range ids {
links = append(links, authorityMenuPO{SysAuthorityAuthorityID: aid, SysBaseMenuID: id})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) ReplaceMenuButtons(ctx context.Context, menuID uint, buttons []*biz.MenuButton) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var oldIDs []uint
if err := tx.Model(&menuButtonPO{}).Where("sys_base_menu_id = ?", menuID).Pluck("id", &oldIDs).Error; err != nil {
return err
}
if len(oldIDs) > 0 {
if err := tx.Where("sys_base_menu_btn_id IN ?", oldIDs).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
}
if err := tx.Where("sys_base_menu_id = ?", menuID).Delete(&menuButtonPO{}).Error; err != nil {
return err
}
pos := make([]menuButtonPO, 0, len(buttons))
for _, button := range buttons {
pos = append(pos, menuButtonPO{Name: button.Name, Description: button.Description, MenuID: menuID})
}
if len(pos) > 0 {
return tx.Create(&pos).Error
}
return nil
})
}
func apiFromPO(po apiPO) *biz.API {
return &biz.API{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Path: po.Path, Description: po.Description, APIGroup: po.APIGroup, Method: po.Method}
}
func (r *accessRepo) CreateAPI(ctx context.Context, v *biz.API) error {
return r.data.gormDB.WithContext(ctx).Create(&apiPO{Path: v.Path, Description: v.Description, APIGroup: v.APIGroup, Method: strings.ToUpper(v.Method)}).Error
}
func (r *accessRepo) UpdateAPI(ctx context.Context, v *biz.API) error {
return r.data.gormDB.WithContext(ctx).Model(&apiPO{}).Where("id = ?", v.ID).Updates(map[string]any{"path": v.Path, "description": v.Description, "api_group": v.APIGroup, "method": strings.ToUpper(v.Method)}).Error
}
func (r *accessRepo) DeleteAPIs(ctx context.Context, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("api_id IN ?", ids).Delete(&authorityAPIPO{}).Error; err != nil {
return err
}
return tx.Delete(&apiPO{}, ids).Error
})
}
func (r *accessRepo) FindAPI(ctx context.Context, id uint) (*biz.API, error) {
var po apiPO
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err
}
return apiFromPO(po), nil
}
func (r *accessRepo) ListAPIs(ctx context.Context, page, size int, q *biz.API) ([]*biz.API, int64, error) {
if page < 1 {
page = 1
}
if size < 1 {
size = 10
}
db := r.data.gormDB.WithContext(ctx).Model(&apiPO{})
if q != nil {
if q.Path != "" {
db = db.Where("path LIKE ?", "%"+q.Path+"%")
}
if q.APIGroup != "" {
db = db.Where("api_group = ?", q.APIGroup)
}
if q.Method != "" {
db = db.Where("method = ?", strings.ToUpper(q.Method))
}
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
var pos []apiPO
if err := db.Order("api_group,path,method").Offset((page - 1) * size).Limit(size).Find(&pos).Error; err != nil {
return nil, 0, err
}
out := make([]*biz.API, 0, len(pos))
for _, po := range pos {
out = append(out, apiFromPO(po))
}
return out, total, nil
}
func (r *accessRepo) APIRoleIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityAPIPO{}).Where("api_id = ?", id).Pluck("authority_id", &ids).Error
return ids, err
}
func (r *accessRepo) SetAPIRoles(ctx context.Context, id uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("api_id = ?", id).Delete(&authorityAPIPO{}).Error; err != nil {
return err
}
links := make([]authorityAPIPO, 0, len(ids))
for _, aid := range ids {
links = append(links, authorityAPIPO{AuthorityID: aid, APIID: id})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) Authorize(ctx context.Context, aid uint, path, method string) (bool, error) {
policies, err := r.PolicyPaths(ctx, aid)
if err != nil {
return false, err
}
model, err := casbinmodel.NewModelFromString(`[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)`)
if err != nil {
return false, err
}
enforcer, err := casbin.NewEnforcer(model)
if err != nil {
return false, err
}
subject := strconv.FormatUint(uint64(aid), 10)
for _, policy := range policies {
if _, err := enforcer.AddPolicy(subject, policy.Path, strings.ToUpper(policy.Method)); err != nil {
return false, err
}
}
return enforcer.Enforce(subject, path, strings.ToUpper(method))
}
func (r *accessRepo) PolicyPaths(ctx context.Context, aid uint) ([]*biz.API, error) {
var pos []apiPO
err := r.data.gormDB.WithContext(ctx).Table("sys_apis").Joins("JOIN sys_authority_apis ON sys_authority_apis.api_id = sys_apis.id").Where("sys_authority_apis.authority_id = ? AND sys_apis.deleted_at IS NULL", aid).Order("sys_apis.path,sys_apis.method").Find(&pos).Error
if err != nil {
return nil, err
}
out := make([]*biz.API, 0, len(pos))
for _, po := range pos {
out = append(out, apiFromPO(po))
}
return out, nil
}
func (r *accessRepo) SetPolicyPaths(ctx context.Context, aid uint, paths []*biz.API) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("authority_id = ?", aid).Delete(&authorityAPIPO{}).Error; err != nil {
return err
}
links := make([]authorityAPIPO, 0, len(paths))
for _, path := range paths {
var po apiPO
err := tx.Where("path = ? AND method = ?", path.Path, strings.ToUpper(path.Method)).First(&po).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
po = apiPO{Path: path.Path, Method: strings.ToUpper(path.Method), Description: path.Description, APIGroup: path.APIGroup}
if err = tx.Create(&po).Error; err != nil {
return err
}
} else if err != nil {
return err
}
links = append(links, authorityAPIPO{AuthorityID: aid, APIID: po.ID})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) IgnoredAPIs(ctx context.Context) ([]*biz.API, error) {
var pos []ignoredAPIPO
if err := r.data.gormDB.WithContext(ctx).Order("path,method").Find(&pos).Error; err != nil {
return nil, err
}
out := make([]*biz.API, 0, len(pos))
for _, po := range pos {
out = append(out, &biz.API{Path: po.Path, Method: po.Method})
}
return out, nil
}
func (r *accessRepo) SetAPIIgnored(ctx context.Context, path, method string, ignored bool) error {
po := ignoredAPIPO{Path: path, Method: strings.ToUpper(method)}
if ignored {
return r.data.gormDB.WithContext(ctx).FirstOrCreate(&po, po).Error
}
return r.data.gormDB.WithContext(ctx).Where("path = ? AND method = ?", po.Path, po.Method).Delete(&ignoredAPIPO{}).Error
}
func (r *accessRepo) ApplyAPISync(ctx context.Context, added, deleted []*biz.API) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
for _, item := range added {
po := apiPO{Path: item.Path, Method: strings.ToUpper(item.Method), Description: item.Description, APIGroup: item.APIGroup}
if err := tx.Where("path = ? AND method = ?", po.Path, po.Method).FirstOrCreate(&po).Error; err != nil {
return err
}
}
for _, item := range deleted {
var ids []uint
if err := tx.Model(&apiPO{}).Where("path = ? AND method = ?", item.Path, strings.ToUpper(item.Method)).Pluck("id", &ids).Error; err != nil {
return err
}
if len(ids) > 0 {
if err := tx.Where("api_id IN ?", ids).Delete(&authorityAPIPO{}).Error; err != nil {
return err
}
if err := tx.Where("id IN ?", ids).Delete(&apiPO{}).Error; err != nil {
return err
}
}
}
return nil
})
}
func (r *accessRepo) Buttons(ctx context.Context, menuID uint) ([]*biz.MenuButton, error) {
var pos []menuButtonPO
if err := r.data.gormDB.WithContext(ctx).Where("sys_base_menu_id = ?", menuID).Find(&pos).Error; err != nil {
return nil, err
}
out := make([]*biz.MenuButton, 0, len(pos))
for _, po := range pos {
out = append(out, &biz.MenuButton{ID: po.ID, Name: po.Name, Description: po.Description, MenuID: po.MenuID})
}
return out, nil
}
func (r *accessRepo) SetAuthorityButtons(ctx context.Context, aid uint, buttons map[uint][]uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("authority_id = ?", aid).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
var links []authorityButtonPO
for menuID, ids := range buttons {
for _, id := range ids {
links = append(links, authorityButtonPO{AuthorityID: aid, MenuID: menuID, ButtonID: id})
}
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) SelectedButtons(ctx context.Context, aid, menuID uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityButtonPO{}).Where("authority_id = ? AND sys_menu_id = ?", aid, menuID).Pluck("sys_base_menu_btn_id", &ids).Error
return ids, err
}
func (r *accessRepo) SetSelectedButtons(ctx context.Context, aid, menuID uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("authority_id = ? AND sys_menu_id = ?", aid, menuID).Delete(&authorityButtonPO{}).Error; err != nil {
return err
}
links := make([]authorityButtonPO, 0, len(ids))
for _, id := range ids {
links = append(links, authorityButtonPO{AuthorityID: aid, MenuID: menuID, ButtonID: id})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) CanRemoveButton(ctx context.Context, id uint) (bool, error) {
var count int64
err := r.data.gormDB.WithContext(ctx).Model(&authorityButtonPO{}).Where("sys_base_menu_btn_id = ?", id).Count(&count).Error
return count == 0, err
}
func (r *accessRepo) AuthorityButtonIDs(ctx context.Context, aid uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&authorityButtonPO{}).Where("authority_id = ?", aid).Pluck("sys_base_menu_btn_id", &ids).Error
return ids, err
}
func deptFromPO(po departmentPO) *biz.Department {
return &biz.Department{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, ParentID: po.ParentID, Ancestors: po.Ancestors, Sort: po.Sort, LeaderID: po.LeaderID, Status: po.Status}
}
func (r *accessRepo) CreateDepartment(ctx context.Context, v *biz.Department) error {
v.Ancestors = "0"
if v.ParentID != 0 {
var parent departmentPO
if err := r.data.gormDB.WithContext(ctx).First(&parent, v.ParentID).Error; err != nil {
return err
}
v.Ancestors = strings.Trim(parent.Ancestors+","+strconv.FormatUint(uint64(parent.ID), 10), ",")
}
return r.data.gormDB.WithContext(ctx).Create(&departmentPO{Name: v.Name, ParentID: v.ParentID, Ancestors: v.Ancestors, Sort: v.Sort, LeaderID: v.LeaderID, Status: v.Status}).Error
}
func (r *accessRepo) UpdateDepartment(ctx context.Context, v *biz.Department) error {
if v.ParentID == v.ID {
return errors.New("父部门不能是自己")
}
v.Ancestors = "0"
if v.ParentID != 0 {
var parent departmentPO
if err := r.data.gormDB.WithContext(ctx).First(&parent, v.ParentID).Error; err != nil {
return err
}
v.Ancestors = strings.Trim(parent.Ancestors+","+strconv.FormatUint(uint64(parent.ID), 10), ",")
}
return r.data.gormDB.WithContext(ctx).Model(&departmentPO{}).Where("id = ?", v.ID).Updates(map[string]any{"name": v.Name, "parent_id": v.ParentID, "ancestors": v.Ancestors, "sort": v.Sort, "leader_id": v.LeaderID, "status": v.Status}).Error
}
func (r *accessRepo) DeleteDepartment(ctx context.Context, id uint) error {
if id == 0 {
return errors.New("部门ID不能为空")
}
var count int64
if err := r.data.gormDB.WithContext(ctx).Model(&departmentPO{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("存在子部门,不允许删除")
}
if err := r.data.gormDB.WithContext(ctx).Model(&userPO{}).Where("dept_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("该部门下存在用户,不允许删除")
}
if err := r.data.gormDB.WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("该部门下存在用户,不允许删除")
}
return r.data.gormDB.WithContext(ctx).Delete(&departmentPO{}, id).Error
}
func (r *accessRepo) FindDepartment(ctx context.Context, id uint) (*biz.Department, error) {
var po departmentPO
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err
}
return deptFromPO(po), nil
}
func (r *accessRepo) ListDepartments(ctx context.Context, name string) ([]*biz.Department, error) {
var pos []departmentPO
db := r.data.gormDB.WithContext(ctx).Order("sort,id")
if name != "" {
db = db.Where("name LIKE ?", "%"+name+"%")
}
if err := db.Find(&pos).Error; err != nil {
return nil, err
}
nodes := map[uint]*biz.Department{}
for _, po := range pos {
nodes[po.ID] = deptFromPO(po)
}
var roots []*biz.Department
for _, po := range pos {
n := nodes[po.ID]
if p := nodes[po.ParentID]; p != nil {
p.Children = append(p.Children, n)
} else {
roots = append(roots, n)
}
}
return roots, nil
}
func (r *accessRepo) DepartmentUserIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Pluck("sys_user_id", &ids).Error
return ids, err
}
func (r *accessRepo) SetDepartmentUsers(ctx context.Context, id uint, ids []uint) error {
return r.replaceUserDepartments(ctx, id, ids)
}
func (r *accessRepo) replaceUserDepartments(ctx context.Context, id uint, ids []uint) error {
if id == 0 {
return errors.New("部门ID不能为空")
}
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var current []uint
if err := tx.Model(&userDepartmentPO{}).Where("sys_department_id = ?", id).Pluck("sys_user_id", &current).Error; err != nil {
return err
}
if err := tx.Where("sys_department_id = ?", id).Delete(&userDepartmentPO{}).Error; err != nil {
return err
}
links := make([]userDepartmentPO, 0, len(ids))
for _, uid := range ids {
links = append(links, userDepartmentPO{UserID: uid, DepartmentID: id})
}
if len(links) > 0 {
if err := tx.Create(&links).Error; err != nil {
return err
}
}
target := make(map[uint]struct{}, len(ids))
for _, uid := range ids {
target[uid] = struct{}{}
}
removed := make([]uint, 0)
for _, uid := range current {
if _, ok := target[uid]; !ok {
removed = append(removed, uid)
}
}
if len(removed) > 0 {
if err := tx.Model(&userPO{}).Where("dept_id = ? AND id IN ?", id, removed).Update("dept_id", 0).Error; err != nil {
return err
}
}
if len(ids) > 0 {
if err := tx.Model(&userPO{}).Where("dept_id = ? AND id IN ?", 0, ids).Update("dept_id", id).Error; err != nil {
return err
}
}
return nil
})
}
func (r *accessRepo) SetUserDepartments(ctx context.Context, uid uint, ids []uint, primary uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var user userPO
if err := tx.First(&user, uid).Error; err != nil {
return errors.New("查询用户数据失败")
}
if err := tx.Where("sys_user_id = ?", uid).Delete(&userDepartmentPO{}).Error; err != nil {
return err
}
links := make([]userDepartmentPO, 0, len(ids))
for _, id := range ids {
links = append(links, userDepartmentPO{UserID: uid, DepartmentID: id})
}
if len(links) > 0 {
if err := tx.Create(&links).Error; err != nil {
return err
}
}
if primary == 0 && len(ids) > 0 {
primary = ids[0]
}
if primary != 0 {
inSet := false
for _, id := range ids {
if id == primary {
inSet = true
break
}
}
if !inSet {
return errors.New("主部门必须在归属部门范围内")
}
}
return tx.Model(&userPO{}).Where("id = ?", uid).Update("dept_id", primary).Error
})
}
func posFromPO(po positionPO) *biz.Position {
return &biz.Position{ID: po.ID, CreatedAt: po.CreatedAt, UpdatedAt: po.UpdatedAt, Name: po.Name, Code: po.Code, Sort: po.Sort, Status: po.Status, Remark: po.Remark}
}
func (r *accessRepo) CreatePosition(ctx context.Context, v *biz.Position) error {
return r.data.gormDB.WithContext(ctx).Create(&positionPO{Name: v.Name, Code: v.Code, Sort: v.Sort, Status: v.Status, Remark: v.Remark}).Error
}
func (r *accessRepo) UpdatePosition(ctx context.Context, v *biz.Position) error {
return r.data.gormDB.WithContext(ctx).Model(&positionPO{}).Where("id = ?", v.ID).Updates(map[string]any{"name": v.Name, "code": v.Code, "sort": v.Sort, "status": v.Status, "remark": v.Remark}).Error
}
func (r *accessRepo) DeletePosition(ctx context.Context, id uint) error {
if id == 0 {
return errors.New("岗位ID不能为空")
}
var count int64
if err := r.data.gormDB.WithContext(ctx).Model(&userPositionPO{}).Where("sys_position_id = ?", id).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("该岗位下存在用户,不允许删除")
}
return r.data.gormDB.WithContext(ctx).Delete(&positionPO{}, id).Error
}
func (r *accessRepo) FindPosition(ctx context.Context, id uint) (*biz.Position, error) {
var po positionPO
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err
}
return posFromPO(po), nil
}
func (r *accessRepo) ListPositions(ctx context.Context, page, size int, q *biz.Position) ([]*biz.Position, int64, error) {
if page < 1 {
page = 1
}
if size < 1 {
size = 10
}
db := r.data.gormDB.WithContext(ctx).Model(&positionPO{})
if q != nil {
if q.Name != "" {
db = db.Where("name LIKE ?", "%"+q.Name+"%")
}
if q.Code != "" {
db = db.Where("code LIKE ?", "%"+q.Code+"%")
}
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
var pos []positionPO
if err := db.Order("sort,id").Offset((page - 1) * size).Limit(size).Find(&pos).Error; err != nil {
return nil, 0, err
}
out := make([]*biz.Position, 0, len(pos))
for _, po := range pos {
out = append(out, posFromPO(po))
}
return out, total, nil
}
func (r *accessRepo) PositionUserIDs(ctx context.Context, id uint) ([]uint, error) {
var ids []uint
err := r.data.gormDB.WithContext(ctx).Model(&userPositionPO{}).Where("sys_position_id = ?", id).Pluck("sys_user_id", &ids).Error
return ids, err
}
func (r *accessRepo) SetPositionUsers(ctx context.Context, id uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_position_id = ?", id).Delete(&userPositionPO{}).Error; err != nil {
return err
}
links := make([]userPositionPO, 0, len(ids))
for _, uid := range ids {
links = append(links, userPositionPO{UserID: uid, PositionID: id})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}
func (r *accessRepo) SetUserPositions(ctx context.Context, uid uint, ids []uint) error {
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Where("sys_user_id = ?", uid).Delete(&userPositionPO{}).Error; err != nil {
return err
}
links := make([]userPositionPO, 0, len(ids))
for _, id := range ids {
links = append(links, userPositionPO{UserID: uid, PositionID: id})
}
if len(links) > 0 {
return tx.Create(&links).Error
}
return nil
})
}