优化
This commit is contained in:
+136
-1
@@ -2,14 +2,18 @@ package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func migrateAll(db *gorm.DB) error {
|
||||
if err := migrateLegacyIgnoreAPITable(db); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.AutoMigrate(
|
||||
&userPO{}, &authorityPO{}, &menuPO{}, &userAuthorityPO{}, &authorityMenuPO{}, &menuParameterPO{},
|
||||
&apiPO{}, &ignoredAPIPO{}, &authorityAPIPO{}, &menuButtonPO{}, &authorityButtonPO{},
|
||||
&apiPO{}, &ignoredAPIPO{}, &authorityAPIPO{}, &casbinRulePO{}, &menuButtonPO{}, &authorityButtonPO{},
|
||||
&departmentPO{}, &positionPO{}, &userDepartmentPO{}, &userPositionPO{}, &authorityDepartmentPO{},
|
||||
&dictionaryPO{}, &dictionaryDetailPO{}, ¶meterPO{}, &apiTokenPO{}, &jwtBlacklistPO{}, &securityConfigPO{},
|
||||
&versionPO{}, &exportTemplatePO{}, &exportConditionPO{}, &exportJoinPO{},
|
||||
@@ -19,9 +23,140 @@ func migrateAll(db *gorm.DB) error {
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := migrateLegacyAuthorityAPIsToCasbinRules(db); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := normalizeErrorRecordStatuses(db); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := reconcileRootAuthorityAPIs(db); err != nil {
|
||||
return err
|
||||
}
|
||||
return reconcileReferenceIndexes(db)
|
||||
}
|
||||
|
||||
// Older Kra builds used a status label that is not part of the GVA error-log
|
||||
// contract and is therefore rendered as an unknown state by the compatible
|
||||
// administration page.
|
||||
func normalizeErrorRecordStatuses(db *gorm.DB) error {
|
||||
return db.Model(&errorRecordPO{}).Where("status = ?", "未解决").Update("status", "未处理").Error
|
||||
}
|
||||
|
||||
// migrateLegacyAuthorityAPIsToCasbinRules upgrades the early Kra join-table
|
||||
// representation to the independent Casbin policy table. Keep the legacy
|
||||
// table in place for backwards compatibility, but make casbin_rule the sole
|
||||
// live policy source. Existing policy rows are not duplicated.
|
||||
func migrateLegacyAuthorityAPIsToCasbinRules(db *gorm.DB) error {
|
||||
if !db.Migrator().HasTable(&authorityAPIPO{}) || !db.Migrator().HasTable(&casbinRulePO{}) {
|
||||
return nil
|
||||
}
|
||||
type legacyPolicy struct {
|
||||
AuthorityID uint
|
||||
Path string
|
||||
Method string
|
||||
}
|
||||
var rows []legacyPolicy
|
||||
if err := db.Table("sys_authority_apis sa").
|
||||
Select("sa.authority_id, a.path, a.method").
|
||||
Joins("JOIN sys_apis a ON a.id = sa.api_id").
|
||||
Where("a.deleted_at IS NULL").Find(&rows).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, row := range rows {
|
||||
exists, err := policyExists(db, row.AuthorityID, row.Path, row.Method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
if err := db.Create(&casbinRulePO{Ptype: "p", V0: fmt.Sprint(row.AuthorityID), V1: row.Path, V2: row.Method}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateLegacyIgnoreAPITable upgrades the early Kra-only composite-key
|
||||
// shape (path, method) to the compatible model shape (ID/timestamps/soft
|
||||
// delete). AutoMigrate can add columns but cannot replace an existing
|
||||
// composite primary key portably, so rebuild the small table once while
|
||||
// preserving every existing ignore rule.
|
||||
func migrateLegacyIgnoreAPITable(db *gorm.DB) error {
|
||||
if !db.Migrator().HasTable(&ignoredAPIPO{}) || db.Migrator().HasColumn(&ignoredAPIPO{}, "id") {
|
||||
return nil
|
||||
}
|
||||
legacyTable := fmt.Sprintf("sys_ignore_apis_legacy_%d", time.Now().UnixNano())
|
||||
type legacyIgnoredAPI struct {
|
||||
Path string
|
||||
Method string
|
||||
}
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Migrator().RenameTable(ignoredAPIPO{}.TableName(), legacyTable); err != nil {
|
||||
return fmt.Errorf("rename legacy ignore API table: %w", err)
|
||||
}
|
||||
if err := tx.AutoMigrate(&ignoredAPIPO{}); err != nil {
|
||||
return fmt.Errorf("create compatible ignore API table: %w", err)
|
||||
}
|
||||
var rows []legacyIgnoredAPI
|
||||
if err := tx.Table(legacyTable).Find(&rows).Error; err != nil {
|
||||
return fmt.Errorf("read legacy ignore API rows: %w", err)
|
||||
}
|
||||
if len(rows) > 0 {
|
||||
items := make([]ignoredAPIPO, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, ignoredAPIPO{Path: row.Path, Method: row.Method})
|
||||
}
|
||||
if err := tx.Create(&items).Error; err != nil {
|
||||
return fmt.Errorf("copy legacy ignore API rows: %w", err)
|
||||
}
|
||||
}
|
||||
if err := tx.Migrator().DropTable(legacyTable); err != nil {
|
||||
return fmt.Errorf("drop legacy ignore API table: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// reconcileRootAuthorityAPIs is a one-time upgrade path from the former Kra
|
||||
// implementation where authority 888 bypassed policy storage entirely. The compatible behavior
|
||||
// grants its root role through persisted Casbin policies, so when a legacy
|
||||
// database has the root role but no stored API links, materialize the same
|
||||
// policy set and let normal authorization read it thereafter.
|
||||
func reconcileRootAuthorityAPIs(db *gorm.DB) error {
|
||||
var authorityCount int64
|
||||
if err := db.Model(&authorityPO{}).Where("authority_id = ?", 888).Count(&authorityCount).Error; err != nil || authorityCount == 0 {
|
||||
return err
|
||||
}
|
||||
var policyCount int64
|
||||
if err := policyScope(db).Where("v0 = ?", "888").Count(&policyCount).Error; err != nil || policyCount != 0 {
|
||||
return err
|
||||
}
|
||||
var ignored []ignoredAPIPO
|
||||
if err := db.Find(&ignored).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
ignoreSet := make(map[string]struct{}, len(ignored))
|
||||
for _, item := range ignored {
|
||||
ignoreSet[item.Method+"\x00"+item.Path] = struct{}{}
|
||||
}
|
||||
var apis []apiPO
|
||||
if err := db.Find(&apis).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
rules := make([]casbinRulePO, 0, len(apis))
|
||||
for _, api := range apis {
|
||||
if _, ok := ignoreSet[api.Method+"\x00"+api.Path]; ok {
|
||||
continue
|
||||
}
|
||||
rules = append(rules, newPolicyRule(888, api.Path, api.Method))
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
return db.Create(&rules).Error
|
||||
}
|
||||
|
||||
// reconcileReferenceIndexes removes constraints created by older Kra builds
|
||||
// that are not part of the administration data model. Business services own
|
||||
// duplicate checks and their user-facing error messages.
|
||||
|
||||
Reference in New Issue
Block a user