feat: complete Kra admin backend migration
This commit is contained in:
+124
-12
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
@@ -11,6 +13,8 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -86,28 +90,72 @@ type authorityMenuPO struct {
|
||||
|
||||
func (authorityMenuPO) TableName() string { return "sys_authority_menus" }
|
||||
|
||||
type menuParameterPO struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
MenuID uint `gorm:"column:sys_base_menu_id;index"`
|
||||
Type string
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (menuParameterPO) TableName() string { return "sys_base_menu_parameters" }
|
||||
|
||||
type systemRepo struct{ data *Data }
|
||||
|
||||
func NewSystemRepo(data *Data) biz.SystemRepo { return &systemRepo{data: data} }
|
||||
func (r *systemRepo) PersistConfig(context.Context) error { return r.data.persistConfig() }
|
||||
func (r *systemRepo) PersistAdminConfig(_ context.Context, raw []byte) error {
|
||||
next := proto.Clone(r.data.admin).(*conf.AdminBackend)
|
||||
if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(raw, next); err != nil {
|
||||
return err
|
||||
}
|
||||
next.ConfigPath = r.data.admin.ConfigPath
|
||||
return r.data.persistConfigValues(r.data.config, next)
|
||||
}
|
||||
func (r *systemRepo) ReloadConfig(ctx context.Context) error {
|
||||
return r.data.reloadConfig(ctx)
|
||||
}
|
||||
|
||||
func (r *systemRepo) IsInitialized(ctx context.Context) (bool, error) {
|
||||
return r.data.gormDB.WithContext(ctx).Migrator().HasTable(&userPO{}), nil
|
||||
db := r.data.gormDB.WithContext(ctx)
|
||||
if !db.Migrator().HasTable(&userPO{}) {
|
||||
return false, nil
|
||||
}
|
||||
var count int64
|
||||
if err := db.Model(&userPO{}).Where("username = ?", "admin").Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *systemRepo) Initialize(ctx context.Context, input *biz.DatabaseConfig) error {
|
||||
config := &conf.Data_Database{Driver: input.Driver, Host: input.Host, Port: input.Port, User: input.User, Password: input.Password, Name: input.Name, Path: input.Path, Config: input.Config}
|
||||
if err := r.data.switchDatabase(config); err != nil {
|
||||
r.data.initMu.Lock()
|
||||
defer r.data.initMu.Unlock()
|
||||
initialized, err := r.IsInitialized(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
db := r.data.gormDB.WithContext(ctx)
|
||||
if err := db.AutoMigrate(&adminPO{}); err != nil {
|
||||
if initialized {
|
||||
return errors.New("数据库已初始化,无需重复初始化")
|
||||
}
|
||||
candidate, err := openDatabase(config, true, input.Template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.AutoMigrate(&userPO{}, &authorityPO{}, &menuPO{}, &userAuthorityPO{}, &authorityMenuPO{}, &apiPO{}, &ignoredAPIPO{}, &authorityAPIPO{}, &menuButtonPO{}, &authorityButtonPO{}, &departmentPO{}, &positionPO{}, &userDepartmentPO{}, &userPositionPO{}, &authorityDepartmentPO{}, &dictionaryPO{}, &dictionaryDetailPO{}, ¶meterPO{}, &apiTokenPO{}, &securityConfigPO{}, &versionPO{}, &exportTemplatePO{}, &exportConditionPO{}, &exportJoinPO{}, &operationPO{}, &loginLogPO{}, &dataAccessLogPO{}, &errorRecordPO{}, &taskPO{}, &taskLogPO{}, &mediaPO{}, &categoryPO{}, &uploadSessionPO{}, &uploadChunkPO{}, &announcementPO{}); err != nil {
|
||||
activated := false
|
||||
defer func() {
|
||||
if !activated {
|
||||
if sqlDB, closeErr := candidate.DB(); closeErr == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
db := candidate.WithContext(ctx)
|
||||
if err := migrateAll(db); err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error {
|
||||
authority := authorityPO{AuthorityID: 888, AuthorityName: "超级管理员", DataScope: 1, DefaultRouter: "dashboard"}
|
||||
if err := tx.FirstOrCreate(&authority, authorityPO{AuthorityID: 888}).Error; err != nil {
|
||||
return err
|
||||
@@ -127,10 +175,12 @@ func (r *systemRepo) Initialize(ctx context.Context, input *biz.DatabaseConfig)
|
||||
nameID[menu.Name] = menu.ID
|
||||
}
|
||||
for i := range menus {
|
||||
if menus[i].ParentID == 0 || menus[i].ActiveName == "" {
|
||||
if menus[i].ActiveName == "" {
|
||||
continue
|
||||
}
|
||||
_ = tx.Model(&menuPO{}).Where("name = ?", menus[i].Name).Updates(map[string]any{"parent_id": nameID[menus[i].ActiveName], "active_name": ""}).Error
|
||||
if err := tx.Model(&menuPO{}).Where("name = ?", menus[i].Name).Updates(map[string]any{"parent_id": nameID[menus[i].ActiveName], "active_name": ""}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tx.Where("sys_authority_authority_id = ?", 888).Delete(&authorityMenuPO{}).Error; err != nil {
|
||||
return err
|
||||
@@ -162,15 +212,56 @@ func (r *systemRepo) Initialize(ctx context.Context, input *biz.DatabaseConfig)
|
||||
return err
|
||||
}
|
||||
}
|
||||
department := departmentPO{Name: "总公司", ParentID: 0, Ancestors: "0", Sort: 0, Status: true}
|
||||
if err := tx.Where("name = ?", department.Name).FirstOrCreate(&department).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, position := range []positionPO{{Name: "总经理", Code: "CEO", Sort: 1, Status: true}, {Name: "普通员工", Code: "STAFF", Sort: 2, Status: true}} {
|
||||
if err := tx.Where("code = ?", position.Code).FirstOrCreate(&position).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
security := defaultSecurityConfig()
|
||||
if err := tx.FirstOrCreate(&security, securityConfigPO{ID: 1}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
exportTemplate := exportTemplatePO{Name: "api", DBTableName: "sys_apis", TemplateID: "api", TemplateInfo: "{\n\"path\":\"路径\",\n\"method\":\"方法(大写)\",\n\"description\":\"方法介绍\",\n\"api_group\":\"方法分组\"\n}"}
|
||||
if err := tx.Where("template_id = ?", exportTemplate.TemplateID).FirstOrCreate(&exportTemplate).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, task := range []taskPO{{Name: "ClearDB", Description: "定时清理数据库过期日志(操作记录/JWT黑名单/定时任务执行日志)", Spec: "@daily", ExecutorType: "method", MethodName: "ClearDB", Enabled: true}, {Name: "CleanStaleUploads", Description: "定时清理过期大文件上传会话", Spec: "@hourly", ExecutorType: "method", MethodName: "CleanStaleUploads", Enabled: true}} {
|
||||
if err := tx.Where("name = ?", task.Name).FirstOrCreate(&task).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, item := range input.APIs {
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
nextData := proto.Clone(r.data.config).(*conf.Data)
|
||||
nextData.Database = config
|
||||
if err := r.data.persistConfigValues(nextData, r.data.admin); err != nil {
|
||||
return fmt.Errorf("persist database configuration: %w", err)
|
||||
}
|
||||
r.data.activateDatabase(candidate, config)
|
||||
activated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
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.ErrAdminNotFound
|
||||
return nil, biz.ErrUserNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -181,7 +272,7 @@ func (r *systemRepo) FindUserByID(ctx context.Context, id uint) (*biz.User, erro
|
||||
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.ErrAdminNotFound
|
||||
return nil, biz.ErrUserNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -245,7 +336,22 @@ func (r *systemRepo) MenusByAuthority(ctx context.Context, authorityID uint) ([]
|
||||
}
|
||||
byID := make(map[uint]*biz.Menu, len(pos))
|
||||
for _, po := range pos {
|
||||
byID[po.ID] = &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{}}
|
||||
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(¶meters).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 {
|
||||
@@ -355,6 +461,12 @@ func (r *systemRepo) DeleteUser(ctx context.Context, id uint) 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
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user