feat: integrate OA activation workflows and business migration

This commit is contained in:
Yvan
2026-11-07 00:09:19 +08:00
parent 64aca495f2
commit 9caf5b1abc
47 changed files with 1831 additions and 152 deletions
@@ -0,0 +1,41 @@
package business
import (
"gorm.io/gorm"
datasystem "kra/internal/data/system"
"kra/internal/modules/surface"
"kra/pkg/module"
)
func migrateActivationSurface(db *gorm.DB) error {
if !db.Migrator().HasTable("sys_authorities") {
return nil
}
var roots int64
if err := db.Table("sys_authorities").Where("authority_id=888 AND deleted_at IS NULL").Count(&roots).Error; err != nil {
return err
}
if roots == 0 {
return nil
}
if db.Migrator().HasTable("sys_base_menus") {
if err := db.Table("sys_base_menus").Where("name=?", "sw_ChannelActivationList").Update("title", "激活列表").Error; err != nil {
return err
}
}
apis := surface.APIsForPrefix("/sw/promoteActivePool/", 0)
if err := datasystem.EnsureAdminSurface(db, module.Surface{APIs: apis}, 888); err != nil {
return err
}
// 申请绑定与流失记录对各级商务开放,升级旧的仅三级菜单授权。
if db.Migrator().HasTable("sys_authority_menus") && db.Migrator().HasTable("sys_base_menus") {
if err := db.Exec(`INSERT INTO sys_authority_menus(sys_authority_authority_id,sys_base_menu_id)
SELECT a.authority_id,m.id FROM sys_authorities a CROSS JOIN sys_base_menus m
WHERE a.business_level IN (1,2,3) AND a.deleted_at IS NULL AND m.deleted_at IS NULL
AND m.name IN ('sw_ChannelActivationList','sw_BusMgmt')
AND NOT EXISTS (SELECT 1 FROM sys_authority_menus x WHERE x.sys_authority_authority_id=a.authority_id AND x.sys_base_menu_id=m.id)`).Error; err != nil {
return err
}
}
return datasystem.GrantMenuAPIs(db, "sw_ChannelActivationList", apis)
}
@@ -0,0 +1,43 @@
package business
import (
"github.com/glebarez/sqlite"
"gorm.io/gorm"
datasystem "kra/internal/data/system"
"path/filepath"
"testing"
)
func TestActivationUpgradeGivesAllBusinessLevelsApplicationAccess(t *testing.T) {
db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "surface.db")), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
pool, _ := db.DB()
defer pool.Close()
for _, step := range datasystem.Migrations() {
if err := step.Migrate(db); err != nil {
t.Fatal(err)
}
}
for _, sql := range []string{"INSERT INTO sys_authorities(authority_id,business_level) VALUES(888,NULL),(41,1),(57,2),(83,3),(100,NULL)", "INSERT INTO sys_base_menus(id,name,title) VALUES(1,'sw_BusMgmt','商务管理'),(2,'sw_ChannelActivationList','渠道激活')"} {
if err := db.Exec(sql).Error; err != nil {
t.Fatal(err)
}
}
for i := 0; i < 2; i++ {
if err := migrateActivationSurface(db); err != nil {
t.Fatal(err)
}
}
var count int64
if err := db.Table("sys_authority_menus").Where("sys_base_menu_id=2 AND sys_authority_authority_id IN (41,57,83)").Count(&count).Error; err != nil || count != 3 {
t.Fatalf("business menu access %d: %v", count, err)
}
if err := db.Table("casbin_rule").Where("v0 IN ('41','57','83') AND v1='/sw/promoteActivePool/apply' AND v2='POST'").Count(&count).Error; err != nil || count != 3 {
t.Fatalf("application grants %d: %v", count, err)
}
if err := db.Table("casbin_rule").Where("v0='100' AND v1='/sw/promoteActivePool/apply'").Count(&count).Error; err != nil || count != 0 {
t.Fatal("unrelated role received grant")
}
}
+2 -1
View File
@@ -106,6 +106,7 @@ func Definition() module.Definition {
})
}})
migrations = append(migrations, migration.Step{ID: "202611050021_business_alert_surface", Migrate: migrateAlertSurface})
migrations = append(migrations, migration.Step{ID: "202611060025_business_activation_surface", Migrate: migrateActivationSurface})
definition := module.Definition{Name: "business", Migrations: migrations, Prerequisites: databusiness.CheckPrerequisites, ValidateSchema: databusiness.CheckSchema, TimedTasks: append([]module.TimedTask{alertDetectionTask}, tasks...), Surface: module.Surface{
Menus: []module.Menu{
alertExemptionMenu,
@@ -119,7 +120,7 @@ func Definition() module.Definition {
{Name: "sw_RegistrationDetails", Path: "RegistrationDetails", ParentName: "sw_DataAnalysis", Component: "view/sw/RegistrationDetails.vue", Title: "注册明细", Icon: databusiness.MenuIcon("sw_RegistrationDetails"), Sort: 3},
{Name: "sw_RechargeDetails", Path: "RechargeDetails", ParentName: "sw_DataAnalysis", Component: "view/sw/RechargeDetails.vue", Title: "充值明细", Icon: databusiness.MenuIcon("sw_RechargeDetails"), Sort: 2},
{Name: "sw_ChannelList", Path: "ChannelList", ParentName: "sw_BusMgmt", Component: "view/sw/ChannelList.vue", Title: "渠道列表", Icon: databusiness.MenuIcon("sw_ChannelList"), Sort: 2},
{Name: "sw_ChannelActivationList", Path: "ChannelActivationList", ParentName: "sw_BusMgmt", Component: "view/sw/ChannelActivationList.vue", Title: "渠道激活", Icon: databusiness.MenuIcon("sw_ChannelActivationList"), Sort: 3},
{Name: "sw_ChannelActivationList", Path: "ChannelActivationList", ParentName: "sw_BusMgmt", Component: "view/sw/ChannelActivationList.vue", Title: "激活列表", Icon: databusiness.MenuIcon("sw_ChannelActivationList"), Sort: 3},
{Name: "sw_AlertList", Path: "AlertList", ParentName: "sw_AlertCenter", Component: "view/sw/AlertList.vue", Title: "预警列表", Icon: databusiness.MenuIcon("sw_AlertList"), Sort: 1},
{Name: "sw_AlertCenter", Path: "sw-AlertCenter", Component: "view/routerHolder.vue", Title: "预警中心", Icon: databusiness.MenuIcon("sw_AlertCenter"), Sort: 4},
{Name: "sw_AlertRules", Path: "AlertRules", ParentName: "sw_AlertCenter", Component: "view/sw/AlertRules.vue", Title: "预警规则", Icon: databusiness.MenuIcon("sw_AlertRules"), Sort: 2},
+4 -4
View File
@@ -25,16 +25,16 @@ func TestCatalogContainsBuiltInModulesInDependencyOrder(t *testing.T) {
t.Fatalf("definition[%d] = %q, want %q", index, got, name)
}
}
if got := catalog.MigrationSteps(); len(got) != 39 {
t.Fatalf("module migrations = %d, want 39", len(got))
if got := catalog.MigrationSteps(); len(got) != 41 {
t.Fatalf("module migrations = %d, want 41", len(got))
}
var menus, apis int
for _, definition := range catalog.Definitions {
menus += len(definition.Surface.Menus)
apis += len(definition.Surface.APIs)
}
if menus != 21 || apis != 90 {
t.Fatalf("admin surface = %d menus/%d APIs, want 21/90", menus, apis)
if menus != 21 || apis != 94 {
t.Fatalf("admin surface = %d menus/%d APIs, want 21/94", menus, apis)
}
if got := catalog.DefaultTimedTasks(); len(got) != 5 {
t.Fatalf("default timed tasks = %d, want 5", len(got))