feat: complete Kra admin backend migration

This commit is contained in:
2026-08-14 00:28:37 +08:00
parent c4bf7ee03b
commit acba9624d7
88 changed files with 1765 additions and 8623 deletions
+209 -10
View File
@@ -409,17 +409,101 @@ func menuFromPO(po menuPO) *biz.Menu {
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 (r *accessRepo) CreateMenu(ctx context.Context, v *biz.Menu) error {
po := menuToPO(v)
if err := r.data.gormDB.WithContext(ctx).Create(&po).Error; err != nil {
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
}
v.ID = po.ID
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 {
po := menuToPO(v)
return r.data.gormDB.WithContext(ctx).Model(&menuPO{}).Where("id = ?", v.ID).Updates(&po).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 {
@@ -430,6 +514,29 @@ func (r *accessRepo) DeleteMenu(ctx context.Context, id uint) error {
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
}
@@ -441,7 +548,22 @@ func (r *accessRepo) FindMenu(ctx context.Context, id uint) (*biz.Menu, error) {
if err := r.data.gormDB.WithContext(ctx).First(&po, id).Error; err != nil {
return nil, err
}
return menuFromPO(po), nil
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
@@ -450,7 +572,11 @@ func (r *accessRepo) ListMenus(ctx context.Context) ([]*biz.Menu, error) {
}
out := make([]*biz.Menu, 0, len(pos))
for _, po := range pos {
out = append(out, menuFromPO(po))
menu := menuFromPO(po)
if err := r.loadMenuParameters(ctx, menu); err != nil {
return nil, err
}
out = append(out, menu)
}
return out, nil
}
@@ -760,6 +886,9 @@ func (r *accessRepo) CreateDepartment(ctx context.Context, v *biz.Department) er
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
@@ -771,12 +900,27 @@ func (r *accessRepo) UpdateDepartment(ctx context.Context, v *biz.Department) er
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("部门存在子部门")
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
}
@@ -820,7 +964,14 @@ func (r *accessRepo) SetDepartmentUsers(ctx context.Context, id uint, ids []uint
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
}
@@ -829,13 +980,39 @@ func (r *accessRepo) replaceUserDepartments(ctx context.Context, id uint, ids []
links = append(links, userDepartmentPO{UserID: uid, DepartmentID: id})
}
if len(links) > 0 {
return tx.Create(&links).Error
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
}
@@ -851,6 +1028,18 @@ func (r *accessRepo) SetUserDepartments(ctx context.Context, uid uint, ids []uin
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
})
}
@@ -865,6 +1054,16 @@ 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) {