feat: automate business upgrades and sync reviewed frontend

This commit is contained in:
Yvan
2026-11-06 15:48:35 +08:00
parent 7df74bc8e8
commit 9fccd804a3
58 changed files with 5689 additions and 259 deletions
+2 -17
View File
@@ -204,26 +204,11 @@ func (r *accountRepo) Update(ctx context.Context, a *business.Account) error {
}
func (r *accountRepo) AuthorityForLevel(ctx context.Context, level int) (uint, error) {
// 原角色 ID 仅用于查迁移映射,不作为 Kra 角色 ID 使用。
legacy := map[int]string{1: "2083497078545207297", 2: "2087435436573081601", 3: "2087361690139152385"}
oldID, ok := legacy[level]
if !ok {
if level < 1 || level > 3 {
return 0, business.ErrBusinessRole
}
db := r.data.DB().WithContext(ctx)
var id uint
if db.Migrator().HasTable("sw_kra_id_map") {
if err := db.Table("sw_kra_id_map m").Select("m.new_id").Joins("JOIN sys_authorities a ON a.authority_id=m.new_id AND a.deleted_at IS NULL").Where("m.entity = ? AND m.old_id = ?", "role", oldID).Scan(&id).Error; err != nil {
return 0, business.ErrStorage
}
if id != 0 && id != 888 {
return id, nil
}
}
// 新安装可使用对应中文角色;匹配不唯一时停止创建,避免错授权限。
names := map[int]string{1: "一级商务", 2: "二级商务", 3: "三级商务"}
var ids []uint
if err := db.Table("sys_authorities").Where("authority_name = ? AND deleted_at IS NULL AND authority_id <> ?", names[level], 888).Pluck("authority_id", &ids).Error; err != nil {
if err := r.data.DB().WithContext(ctx).Table("sys_authorities").Where("business_level=? AND deleted_at IS NULL AND authority_id<>888", level).Pluck("authority_id", &ids).Error; err != nil {
return 0, business.ErrStorage
}
if len(ids) != 1 {
+5 -2
View File
@@ -15,10 +15,10 @@ func prepareAccounts(t *testing.T) *gorm.DB {
}
for _, sql := range []string{
"CREATE TABLE sys_user_authority (sys_user_id INTEGER,sys_authority_authority_id INTEGER)",
"CREATE TABLE sys_authorities (authority_id INTEGER,authority_name TEXT,deleted_at DATETIME)",
"CREATE TABLE sys_authorities (authority_id INTEGER,authority_name TEXT,deleted_at DATETIME,business_level INTEGER UNIQUE)",
"CREATE TABLE sw_kra_id_map (entity TEXT,old_id TEXT,new_id INTEGER)",
"CREATE TABLE tab_promote (id INTEGER,source_db TEXT,busier_id INTEGER,promote_level INTEGER,status INTEGER)",
"INSERT INTO sys_authorities VALUES (901,'一级商务',NULL),(902,'已改名二级角色',NULL),(903,'三级商务',NULL)",
"INSERT INTO sys_authorities VALUES (901,'一级商务',NULL,1),(902,'已改名二级角色',NULL,2),(903,'三级商务',NULL,3)",
"INSERT INTO sw_kra_id_map VALUES ('role','2087435436573081601',902)",
} {
if err := db.Exec(sql).Error; err != nil {
@@ -32,6 +32,9 @@ func TestAccountCreateIsAtomicAndPreferencesDefaultOn(t *testing.T) {
db := prepareAccounts(t)
r := NewAccountRepo(testData{db})
ctx := context.Background()
if err := db.Exec("DROP TABLE sw_kra_id_map").Error; err != nil {
t.Fatal(err)
}
role, err := r.AuthorityForLevel(ctx, 2)
if err != nil || role != 902 {
t.Fatalf("角色映射不正确: %d %v", role, err)
+1 -36
View File
@@ -394,13 +394,11 @@ func fillLedgerNames(db *gorm.DB, scope business.LedgerScope, filter business.Le
SourceDB string `gorm:"column:source_db"`
PlatformBusierID uint64
PlatformBusierAccount, Username string
UserID uint
IDPath string `gorm:"column:id_path"`
}
names := map[business.PlatformKey]bindName{}
for start := 0; start < len(pairs); start += 300 {
var rows []bindName
q := channelBindingQuery(db, scope.Viewer, business.ChannelFilter{}).Select("b.source_db,b.platform_busier_id,b.platform_busier_account,b.user_id,COALESCE(NULLIF(u.nick_name,''),u.username) AS username,u.id_path").Where("(b.source_db,b.platform_busier_id) IN ?", portraitBindingPairs(pairs[start:min(start+300, len(pairs))])).Order("b.user_id")
q := channelBindingQuery(db, scope.Viewer, business.ChannelFilter{}).Select("b.source_db,b.platform_busier_id,b.platform_busier_account,u.username").Where("(b.source_db,b.platform_busier_id) IN ?", portraitBindingPairs(pairs[start:min(start+300, len(pairs))])).Order("b.user_id")
if err := q.Scan(&rows).Error; err != nil {
return business.ErrStorage
}
@@ -411,31 +409,6 @@ func fillLedgerNames(db *gorm.DB, scope business.LedgerScope, filter business.Le
}
}
}
ancestorIDs := map[uint]bool{}
paths := map[business.PlatformKey][]uint{}
for key, binding := range names {
for _, part := range strings.Split(binding.IDPath, ",") {
id, err := strconv.ParseUint(strings.TrimSpace(part), 10, strconv.IntSize)
if err == nil && id > 0 && uint(id) != binding.UserID {
paths[key] = append(paths[key], uint(id))
ancestorIDs[uint(id)] = true
}
}
}
ids := make([]uint, 0, len(ancestorIDs))
for id := range ancestorIDs {
ids = append(ids, id)
}
ancestors := map[uint]string{}
for start := 0; start < len(ids); start += 300 {
var rows []accountPO
if err := db.Select("id,COALESCE(NULLIF(nick_name,''),username) AS username").Where("id IN ? AND level > 0", ids[start:min(start+300, len(ids))]).Find(&rows).Error; err != nil {
return business.ErrStorage
}
for _, row := range rows {
ancestors[row.ID] = row.Username
}
}
for _, target := range targets {
n := target.Names
if n.PromoteID == nil || *n.PromoteID < 0 {
@@ -448,14 +421,6 @@ func fillLedgerNames(db *gorm.DB, scope business.LedgerScope, filter business.Le
if binding, ok := names[business.PlatformKey{SourceDB: c.SourceDB, BusierID: c.BusierID}]; ok {
n.BindingAccount = binding.PlatformBusierAccount
n.BindingUserName = binding.Username
for _, id := range paths[business.PlatformKey{SourceDB: c.SourceDB, BusierID: c.BusierID}] {
if name := strings.TrimSpace(ancestors[id]); name != "" {
n.BusinessPath = append(n.BusinessPath, name)
}
}
if name := strings.TrimSpace(binding.Username); name != "" {
n.BusinessPath = append(n.BusinessPath, name)
}
}
}
}
+1 -1
View File
@@ -82,7 +82,7 @@ func TestRegistrationScopePuidAndUnboundedPlayerPay(t *testing.T) {
first = row
}
}
if first == nil || first.TotalPayAmount == nil || portraitTestMoney(*first.TotalPayAmount) != "710.10" || first.FirstGameName != "历史游戏名" || first.DisplayBusiness() != "parent→branch→leaf" {
if first == nil || first.TotalPayAmount == nil || portraitTestMoney(*first.TotalPayAmount) != "710.10" || first.FirstGameName != "历史游戏名" || first.DisplayBusiness() != "lx-business(leaf)" {
t.Fatalf("累计充值不能受本次时间/渠道限制,且需保留游戏快照: %+v", first)
}
filter.SortField = "totalPayAmount"
@@ -5,6 +5,8 @@ import (
"strings"
"gorm.io/gorm"
"kra/internal/data/swmigration"
datasystem "kra/internal/data/system"
"kra/pkg/database/migration"
)
@@ -36,6 +38,9 @@ func CheckPrerequisites(db *gorm.DB) error {
}
func CheckSchema(db *gorm.DB) error {
if err := swmigration.CheckSchema(db); err != nil {
return err
}
if err := CheckPrerequisites(db); err != nil {
return err
}
@@ -50,6 +55,9 @@ func CheckSchema(db *gorm.DB) error {
if db.Dialector.Name() != "mysql" {
return nil
}
if err := datasystem.CheckBusinessRoles(db); err != nil {
return err
}
indexes := append([]businessQueryIndex{}, businessQueryIndexes...)
var idType string
if err := db.Raw("SELECT data_type FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tab_promote' AND column_name='id'").Scan(&idType).Error; err != nil {
+2 -1
View File
@@ -2,11 +2,12 @@ package business
import (
"gorm.io/gorm"
"kra/internal/data/swmigration"
"kra/pkg/database/migration"
)
func Migrations() []migration.Step {
return []migration.Step{{ID: "202611060023_business_binding_status_index", Migrate: migrateBindingStatusIndex}, {ID: "202611060022_business_promote_id_capacity", Migrate: migratePromoteIDCapacity}, {ID: "202611050020_business_alert_v2", Migrate: migrateAlertV2}, {ID: "202611040017_business_groups_goals_effectiveness", Migrate: migrateBusinessManagement}, {ID: "202611040018_business_effectiveness_query_index", Migrate: migrateEffectivenessQueryIndex}, {ID: "202611040015_business_approved_game_index", Migrate: migrateApprovedGameIndexes}, {ID: "202611040014_business_report_build_indexes", Migrate: migrateReportBuildQueryIndexes}, {ID: "202611040013_business_query_indexes", Migrate: migrateBusinessQueryIndexes}, {ID: "202611040012_business_server_report_index", Migrate: migrateServerReportIndex}, {ID: "202611040011_business_registration_index", Migrate: migrateRegistrationIndex}, {ID: "202611040010_business_alert_trigger_locks", Migrate: func(db *gorm.DB) error { return migration.CreateMissingTables(db, &alertTriggerLockPO{}) }}, {ID: "202611040009_business_menu_icons", Migrate: migrateBusinessMenuIcons}, {ID: "202611040008_business_report_build_locks", Migrate: func(db *gorm.DB) error { return migration.CreateMissingTables(db, &reportBuildLockPO{}) }}, {ID: "202611030007_business_alert_attachments", Migrate: migrateAlertAttachments}, {ID: "202611030006_business_alert_schema", Migrate: migrateAlertSchema}, {ID: "202611030005_business_password_reset", Migrate: func(db *gorm.DB) error {
return []migration.Step{swmigration.StateMigration(), {ID: "202611060023_business_binding_status_index", Migrate: migrateBindingStatusIndex}, {ID: "202611060022_business_promote_id_capacity", Migrate: migratePromoteIDCapacity}, {ID: "202611050020_business_alert_v2", Migrate: migrateAlertV2}, {ID: "202611040017_business_groups_goals_effectiveness", Migrate: migrateBusinessManagement}, {ID: "202611040018_business_effectiveness_query_index", Migrate: migrateEffectivenessQueryIndex}, {ID: "202611040015_business_approved_game_index", Migrate: migrateApprovedGameIndexes}, {ID: "202611040014_business_report_build_indexes", Migrate: migrateReportBuildQueryIndexes}, {ID: "202611040013_business_query_indexes", Migrate: migrateBusinessQueryIndexes}, {ID: "202611040012_business_server_report_index", Migrate: migrateServerReportIndex}, {ID: "202611040011_business_registration_index", Migrate: migrateRegistrationIndex}, {ID: "202611040010_business_alert_trigger_locks", Migrate: func(db *gorm.DB) error { return migration.CreateMissingTables(db, &alertTriggerLockPO{}) }}, {ID: "202611040009_business_menu_icons", Migrate: migrateBusinessMenuIcons}, {ID: "202611040008_business_report_build_locks", Migrate: func(db *gorm.DB) error { return migration.CreateMissingTables(db, &reportBuildLockPO{}) }}, {ID: "202611030007_business_alert_attachments", Migrate: migrateAlertAttachments}, {ID: "202611030006_business_alert_schema", Migrate: migrateAlertSchema}, {ID: "202611030005_business_password_reset", Migrate: func(db *gorm.DB) error {
return migration.CreateMissingTables(db, &resetCodePO{}, &resetTokenPO{})
}}, {ID: "202611030004_business_binding_locks", Migrate: func(db *gorm.DB) error {
return migration.CreateMissingTables(db, &bindingLockPO{})