diff --git a/internal/router/game_provider_routes.go b/internal/router/game_provider_routes.go index 183393a..398ab55 100644 --- a/internal/router/game_provider_routes.go +++ b/internal/router/game_provider_routes.go @@ -1,6 +1,7 @@ package router import ( + "errors" "net/http" "strings" @@ -160,7 +161,7 @@ func registerGameProviderRoutes(engine *gin.Engine, javaClient authGateway, regi writeError(c, common.NewAppError(http.StatusBadRequest, "bad_request", err.Error())) return } - resp, err := provider.LaunchGame(c.Request.Context(), mustAuthUser(c), req, resolveClientIP(c)) + resp, err := launchProviderGame(c, registry, provider, req) if err != nil { writeError(c, err) return @@ -214,6 +215,25 @@ func resolveGameProvider(c *gin.Context, registry *gameprovider.Registry) (gamep return provider, true } +func launchProviderGame(c *gin.Context, registry *gameprovider.Registry, provider gameprovider.Provider, req gameprovider.LaunchRequest) (*gameprovider.LaunchResponse, error) { + user := mustAuthUser(c) + clientIP := resolveClientIP(c) + resp, err := provider.LaunchGame(c.Request.Context(), user, req, clientIP) + if err == nil { + return resp, nil + } + if !isGameNotFoundError(err) { + return nil, err + } + // 旧客户端只能把热游列表项伪装成 LEADER 进入已有 WebView;这里再按 gameId 回到真实 provider,避免 LEADER 路由找不到 hg_*。 + return registry.LaunchGame(c.Request.Context(), user, req, clientIP) +} + +func isGameNotFoundError(err error) bool { + var appErr *common.AppError + return errors.As(err, &appErr) && appErr.Code == "game_not_found" +} + type publicRoomGameItem struct { ID int64 `json:"id"` GameID string `json:"gameId"` @@ -370,8 +390,14 @@ func publicLaunch(resp *gameprovider.LaunchResponse) *publicLaunchResponse { } func appCompatProviderName(provider string) string { + // 客户端历史上只认识 LEADER,不认识内部厂商名 LINGXIAN,所以出参继续保持旧字段值。 if strings.EqualFold(strings.TrimSpace(provider), "LINGXIAN") { return "LEADER" } + // 旧客户端没有 HOTGAME 页面;热游先按 LEADER 展示并进入已有 WebView,launch 路由再按 hg_* 分发回真实热游 provider。 + switch strings.ToUpper(strings.ReplaceAll(strings.TrimSpace(provider), "_", "")) { + case "HOTGAME", "REYOU", "LALU": + return "LEADER" + } return provider } diff --git a/internal/router/game_provider_routes_test.go b/internal/router/game_provider_routes_test.go index 7b5fa0b..c206864 100644 --- a/internal/router/game_provider_routes_test.go +++ b/internal/router/game_provider_routes_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "strings" "testing" "chatapp3-golang/internal/common" @@ -82,6 +83,41 @@ func TestPublicRoomGameListResponseMapsLingxianToLeaderForOldClients(t *testing. } } +func TestPublicRoomGameListResponseMapsHotgameAliasesToLeaderForOldClients(t *testing.T) { + for _, alias := range []string{"HOTGAME", "HOT_GAME", "REYOU", "LALU"} { + resp := publicRoomGameListResponse(&gameprovider.RoomGameListResponse{ + Items: []gameprovider.RoomGameListItem{ + { + ID: 9530, + GameID: "hg_1", + GameType: alias, + Provider: alias, + ProviderGameID: "1", + Name: "Hotgame", + LaunchParams: map[string]any{"gameType": alias, "screenMode": "seven"}, + }, + }, + }) + + if len(resp.Items) != 1 { + t.Fatalf("%s items = %d, want 1", alias, len(resp.Items)) + } + item := resp.Items[0] + if item.Provider != "LEADER" { + t.Fatalf("%s provider = %q, want LEADER", alias, item.Provider) + } + if item.GameType != "LEADER" { + t.Fatalf("%s gameType = %q, want LEADER", alias, item.GameType) + } + if item.VendorType != "LEADER" { + t.Fatalf("%s vendorType = %q, want LEADER", alias, item.VendorType) + } + if got := item.LaunchParams["gameType"]; got != "LEADER" { + t.Fatalf("%s launchParams.gameType = %v, want LEADER", alias, got) + } + } +} + func TestPublicLaunchKeepsLegacyBridgeAndProviderFields(t *testing.T) { launchConfig := map[string]any{"code": "launch-code"} resp := publicLaunch(&gameprovider.LaunchResponse{ @@ -234,6 +270,45 @@ func TestGameProviderRoutesReturnProvidersWhenVisibilityPolicyAllows(t *testing. } } +func TestProviderLaunchFallsBackToRegistryWhenLegacyProviderCannotFindGame(t *testing.T) { + gin.SetMode(gin.TestMode) + engine := gin.New() + registerGameProviderRoutes( + engine, + routeStubAuthGateway{}, + gameprovider.NewRegistry( + &routeLaunchStubProvider{key: "LINGXIAN"}, + &routeLaunchStubProvider{key: "HOTGAME", matchGameID: "hg_1"}, + ), + nil, + ) + + req := httptest.NewRequest(http.MethodPost, "/app/game/providers/LEADER/launch", strings.NewReader(`{"roomId":"room-1","gameId":"hg_1"}`)) + req.Header.Set("Authorization", "Bearer token") + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + engine.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d: %s", rec.Code, http.StatusOK, rec.Body.String()) + } + var payload struct { + Body publicLaunchResponse `json:"body"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { + t.Fatalf("response json error = %v: %s", err, rec.Body.String()) + } + if payload.Body.GameID != "hg_1" { + t.Fatalf("gameId = %q, want hg_1", payload.Body.GameID) + } + if payload.Body.Provider != "LEADER" { + t.Fatalf("provider = %q, want LEADER for old-client compat", payload.Body.Provider) + } + if payload.Body.GameSessionID != "hg-session" { + t.Fatalf("gameSessionId = %q, want hg-session", payload.Body.GameSessionID) + } +} + type routeStubAuthGateway struct{} func (routeStubAuthGateway) AuthenticateToken(context.Context, string) (integration.UserCredential, error) { @@ -304,3 +379,51 @@ func (p *routeStubProvider) LaunchGame(context.Context, gameprovider.AuthUser, g func (p *routeStubProvider) CloseGame(context.Context, gameprovider.AuthUser, gameprovider.CloseRequest) (*gameprovider.RoomStateResponse, error) { return &gameprovider.RoomStateResponse{}, nil } + +type routeLaunchStubProvider struct { + key string + matchGameID string +} + +func (p *routeLaunchStubProvider) Key() string { return p.key } + +func (p *routeLaunchStubProvider) DisplayName() string { return p.key } + +func (p *routeLaunchStubProvider) SupportsLaunch(_ context.Context, _ gameprovider.AuthUser, req gameprovider.LaunchRequest) (bool, error) { + return p.matchGameID != "" && req.GameID == p.matchGameID, nil +} + +func (p *routeLaunchStubProvider) ListShortcutGames(context.Context, gameprovider.AuthUser, string) ([]gameprovider.RoomGameListItem, error) { + return nil, nil +} + +func (p *routeLaunchStubProvider) ListRoomGames(context.Context, gameprovider.AuthUser, string, string) (*gameprovider.RoomGameListResponse, error) { + return &gameprovider.RoomGameListResponse{}, nil +} + +func (p *routeLaunchStubProvider) GetRoomState(context.Context, gameprovider.AuthUser, string) (*gameprovider.RoomStateResponse, error) { + return &gameprovider.RoomStateResponse{}, nil +} + +func (p *routeLaunchStubProvider) LaunchGame(_ context.Context, _ gameprovider.AuthUser, req gameprovider.LaunchRequest, _ string) (*gameprovider.LaunchResponse, error) { + if p.matchGameID == "" || req.GameID != p.matchGameID { + return nil, common.NewAppError(http.StatusNotFound, "game_not_found", "game not found") + } + return &gameprovider.LaunchResponse{ + GameSessionID: "hg-session", + Provider: p.key, + GameID: req.GameID, + ProviderGameID: "1", + Entry: gameprovider.LaunchEntry{LaunchMode: "H5_REMOTE"}, + RoomState: gameprovider.RoomStateResponse{ + RoomID: req.RoomID, + State: "PLAYING", + Provider: p.key, + GameSessionID: "hg-session", + }, + }, nil +} + +func (p *routeLaunchStubProvider) CloseGame(context.Context, gameprovider.AuthUser, gameprovider.CloseRequest) (*gameprovider.RoomStateResponse, error) { + return &gameprovider.RoomStateResponse{}, nil +} diff --git a/internal/service/gameprovider/registry.go b/internal/service/gameprovider/registry.go index e4ca859..fe555cd 100644 --- a/internal/service/gameprovider/registry.go +++ b/internal/service/gameprovider/registry.go @@ -251,6 +251,8 @@ func normalizeProviderAlias(key string) string { switch normalizeKey(key) { case "LEADER": return "LINGXIAN" + case "HOTGAME", "HOT_GAME", "REYOU", "LALU": + return "HOTGAME" default: return normalizeKey(key) } diff --git a/internal/service/gameprovider/registry_test.go b/internal/service/gameprovider/registry_test.go index bfb572e..1a9a510 100644 --- a/internal/service/gameprovider/registry_test.go +++ b/internal/service/gameprovider/registry_test.go @@ -80,6 +80,20 @@ func TestRegistryResolveMapsLeaderAliasToLingxian(t *testing.T) { } } +func TestRegistryResolveMapsHotgameAliases(t *testing.T) { + registry := NewRegistry(&stubProvider{key: "HOTGAME", name: "热游"}) + + for _, alias := range []string{"HOTGAME", "hot_game", "REYOU", "lalu"} { + provider, err := registry.Resolve(alias) + if err != nil { + t.Fatalf("Resolve(%q) error = %v", alias, err) + } + if provider.Key() != "HOTGAME" { + t.Fatalf("Resolve(%q) key = %q, want HOTGAME", alias, provider.Key()) + } + } +} + func TestRegistryListKeepsRegistrationOrder(t *testing.T) { registry := NewRegistry( &stubProvider{key: "BAISHUN", name: "百顺"}, diff --git a/internal/service/managercenter/bd_leader.go b/internal/service/managercenter/bd_leader.go index 3dcb495..1b5a769 100644 --- a/internal/service/managercenter/bd_leader.go +++ b/internal/service/managercenter/bd_leader.go @@ -124,7 +124,7 @@ func (s *Service) ListBDLeaders(ctx context.Context, user AuthUser) (*BDLeaderLi } func (s *Service) AddBDLeader(ctx context.Context, user AuthUser, req AddBDLeaderRequest) (*AddBDLeaderResponse, error) { - manager, err := s.requireManagerInfo(ctx, user) + manager, err := s.requireManagerFeature(ctx, user, managerFeatureAddBDLeader) if err != nil { return nil, err } diff --git a/internal/service/managercenter/service.go b/internal/service/managercenter/service.go index bf26597..302c2e1 100644 --- a/internal/service/managercenter/service.go +++ b/internal/service/managercenter/service.go @@ -14,13 +14,17 @@ import ( ) type teamManagerInfoRow struct { - ID int64 `gorm:"column:id;primaryKey"` - UserID int64 `gorm:"column:user_id"` - Origin string `gorm:"column:sys_origin"` - Contact string `gorm:"column:contact"` - RegionID string `gorm:"column:region_id"` - CreateUser int64 `gorm:"column:create_user"` - UpdateUser int64 `gorm:"column:update_user"` + ID int64 `gorm:"column:id;primaryKey"` + UserID int64 `gorm:"column:user_id"` + Origin string `gorm:"column:sys_origin"` + Contact string `gorm:"column:contact"` + RegionID string `gorm:"column:region_id"` + CanAddBDLeader *bool `gorm:"column:can_add_bd_leader"` + CanGiftProps *bool `gorm:"column:can_gift_props"` + CanBanUser *bool `gorm:"column:can_ban_user"` + CanUnbanUser *bool `gorm:"column:can_unban_user"` + CreateUser int64 `gorm:"column:create_user"` + UpdateUser int64 `gorm:"column:update_user"` } func (teamManagerInfoRow) TableName() string { @@ -73,7 +77,8 @@ func (propsNobleVIPAbilityRow) TableName() string { } func (s *Service) GetProfile(ctx context.Context, user AuthUser) (*ProfileResponse, error) { - if err := s.ensureManager(ctx, user); err != nil { + manager, err := s.requireManagerInfo(ctx, user) + if err != nil { return nil, err } @@ -81,7 +86,7 @@ func (s *Service) GetProfile(ctx context.Context, user AuthUser) (*ProfileRespon if err != nil { return nil, err } - return &ProfileResponse{Manager: view}, nil + return &ProfileResponse{Manager: view, Features: managerFeaturesFromRow(manager)}, nil } func (s *Service) SearchUser(ctx context.Context, user AuthUser, account string) (*UserSearchResponse, error) { @@ -148,7 +153,7 @@ func (s *Service) ListProps(ctx context.Context, user AuthUser, propsType string } func (s *Service) SendProps(ctx context.Context, user AuthUser, req SendPropsRequest) (*SendPropsResponse, error) { - if err := s.ensureManager(ctx, user); err != nil { + if _, err := s.requireManagerFeature(ctx, user, managerFeatureGiftProps); err != nil { return nil, err } if req.AcceptUserID.Int64() <= 0 { @@ -243,7 +248,7 @@ func (s *Service) SendProps(ctx context.Context, user AuthUser, req SendPropsReq } func (s *Service) BanUser(ctx context.Context, user AuthUser, req BanUserRequest) (*BanUserResponse, error) { - if err := s.ensureManager(ctx, user); err != nil { + if _, err := s.requireManagerFeature(ctx, user, managerFeatureBanUser); err != nil { return nil, err } if req.UserID.Int64() <= 0 { @@ -290,7 +295,7 @@ func (s *Service) BanUser(ctx context.Context, user AuthUser, req BanUserRequest } func (s *Service) UnbanUser(ctx context.Context, user AuthUser, req UnbanUserRequest) (*UnbanUserResponse, error) { - if err := s.ensureManager(ctx, user); err != nil { + if _, err := s.requireManagerFeature(ctx, user, managerFeatureUnbanUser); err != nil { return nil, err } if req.UserID.Int64() <= 0 { @@ -332,6 +337,48 @@ func (s *Service) ensureManager(ctx context.Context, user AuthUser) error { return err } +// requireManagerFeature 先确认调用人仍然是团队经理,再检查 admin 给这个经理打开的单项 H5 操作权限。 +// 这样前端隐藏按钮和后端写操作使用同一张 team_manager_base_info 表做判断,H5 被绕过时也会被服务层拦截。 +func (s *Service) requireManagerFeature(ctx context.Context, user AuthUser, feature managerFeature) (teamManagerInfoRow, error) { + manager, err := s.requireManagerInfo(ctx, user) + if err != nil { + return teamManagerInfoRow{}, err + } + features := managerFeaturesFromRow(manager) + if !features.enabled(feature) { + return teamManagerInfoRow{}, forbidden("manager_feature_disabled", "manager feature is disabled") + } + return manager, nil +} + +func managerFeaturesFromRow(row teamManagerInfoRow) ManagerFeatures { + return ManagerFeatures{ + AddBDLeader: defaultEnabled(row.CanAddBDLeader), + GiftProps: defaultEnabled(row.CanGiftProps), + BanUser: defaultEnabled(row.CanBanUser), + UnbanUser: defaultEnabled(row.CanUnbanUser), + } +} + +func defaultEnabled(value *bool) bool { + return value == nil || *value +} + +func (features ManagerFeatures) enabled(feature managerFeature) bool { + switch feature { + case managerFeatureAddBDLeader: + return features.AddBDLeader + case managerFeatureGiftProps: + return features.GiftProps + case managerFeatureBanUser: + return features.BanUser + case managerFeatureUnbanUser: + return features.UnbanUser + default: + return false + } +} + func (s *Service) findUserView(ctx context.Context, account string) (UserView, error) { if userID, err := strconv.ParseInt(account, 10, 64); err == nil && userID > 0 { if row, err := s.loadUserRowByID(ctx, userID); err == nil { diff --git a/internal/service/managercenter/service_test.go b/internal/service/managercenter/service_test.go index ecb9333..4e2e6c1 100644 --- a/internal/service/managercenter/service_test.go +++ b/internal/service/managercenter/service_test.go @@ -167,6 +167,26 @@ func TestGetProfileToleratesRechargeQueryFailure(t *testing.T) { } } +func TestGetProfileReturnsManagerFeatures(t *testing.T) { + service, _ := newManagerCenterTestService(t) + db := service.db.(*gorm.DB) + seedManagerWithFeatures(t, db, 1, ManagerFeatures{ + AddBDLeader: true, + GiftProps: false, + BanUser: true, + UnbanUser: false, + }) + seedUsers(t, db, userBaseInfoRow{ID: 1, Account: "1"}) + + resp, err := service.GetProfile(context.Background(), AuthUser{UserID: 1}) + if err != nil { + t.Fatalf("GetProfile() error = %v", err) + } + if !resp.Features.AddBDLeader || resp.Features.GiftProps || !resp.Features.BanUser || resp.Features.UnbanUser { + t.Fatalf("features = %+v, want row switches returned", resp.Features) + } +} + func TestAddBDLeaderCreatesLeaderAndListEntry(t *testing.T) { service, _ := newManagerCenterTestService(t) db := service.db.(*gorm.DB) @@ -234,6 +254,24 @@ func TestAddBDLeaderRejectsTargetFromOtherOrigin(t *testing.T) { assertAppErrorCode(t, err, "bd_leader_origin_mismatch") } +func TestAddBDLeaderRejectsDisabledFeature(t *testing.T) { + service, _ := newManagerCenterTestService(t) + db := service.db.(*gorm.DB) + seedManagerWithFeatures(t, db, 1, ManagerFeatures{GiftProps: true, BanUser: true, UnbanUser: true}) + seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2002", OriginSys: "LIKEI"}) + + _, err := service.AddBDLeader(context.Background(), AuthUser{UserID: 1, SysOrigin: "LIKEI"}, AddBDLeaderRequest{UserID: ID(2)}) + assertAppErrorCode(t, err, "manager_feature_disabled") + + var count int64 + if err := db.Model(&bdLeaderRow{}).Where("user_id = ?", int64(2)).Count(&count).Error; err != nil { + t.Fatalf("count bd leaders: %v", err) + } + if count != 0 { + t.Fatalf("bd leader count = %d, want 0", count) + } +} + func TestSendPropsRejectsNonAdminFree(t *testing.T) { service, fake := newManagerCenterTestService(t) db := service.db.(*gorm.DB) @@ -294,6 +332,23 @@ func TestSendNobleVIPGivesAccessoriesAndSwitchesMax(t *testing.T) { } } +func TestSendPropsRejectsDisabledFeature(t *testing.T) { + service, fake := newManagerCenterTestService(t) + db := service.db.(*gorm.DB) + seedManagerWithFeatures(t, db, 1, ManagerFeatures{AddBDLeader: true, BanUser: true, UnbanUser: true}) + seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2"}) + seedProps(t, db, propsSourceRecordRow{ID: 101, Type: propsTypeRide, Name: "Giftable ride", AdminFree: true}) + + _, err := service.SendProps(context.Background(), AuthUser{UserID: 1}, SendPropsRequest{ + AcceptUserID: ID(2), + PropsID: ID(101), + }) + assertAppErrorCode(t, err, "manager_feature_disabled") + if len(fake.giveRequests) != 0 { + t.Fatalf("give requests = %+v, want none", fake.giveRequests) + } +} + func TestSendBadgeActivatesBadgeBackpack(t *testing.T) { service, fake := newManagerCenterTestService(t) db := service.db.(*gorm.DB) @@ -513,6 +568,19 @@ func assertManagerVIPCleanupBadge(t *testing.T, db *gorm.DB, badgeID int64, expi } } +func TestBanUserRejectsDisabledFeature(t *testing.T) { + service, fake := newManagerCenterTestService(t) + db := service.db.(*gorm.DB) + seedManagerWithFeatures(t, db, 1, ManagerFeatures{AddBDLeader: true, GiftProps: true, UnbanUser: true}) + seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2"}) + + _, err := service.BanUser(context.Background(), AuthUser{UserID: 1}, BanUserRequest{UserID: ID(2)}) + assertAppErrorCode(t, err, "manager_feature_disabled") + if len(fake.punishRequests) != 0 { + t.Fatalf("punish requests = %+v, want none", fake.punishRequests) + } +} + func TestBanUserRejectsRechargedUser(t *testing.T) { service, fake := newManagerCenterTestService(t) db := service.db.(*gorm.DB) @@ -592,6 +660,19 @@ func TestBanUserWithoutRechargeCallsPunishment(t *testing.T) { } } +func TestUnbanUserRejectsDisabledFeature(t *testing.T) { + service, fake := newManagerCenterTestService(t) + db := service.db.(*gorm.DB) + seedManagerWithFeatures(t, db, 1, ManagerFeatures{AddBDLeader: true, GiftProps: true, BanUser: true}) + seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2", AccountStatus: accountStatusArchive}) + + _, err := service.UnbanUser(context.Background(), AuthUser{UserID: 1}, UnbanUserRequest{UserID: ID(2)}) + assertAppErrorCode(t, err, "manager_feature_disabled") + if len(fake.unblockRequests) != 0 { + t.Fatalf("unblock requests = %+v, want none", fake.unblockRequests) + } +} + func TestUnbanUserRejectsNormalUser(t *testing.T) { service, fake := newManagerCenterTestService(t) db := service.db.(*gorm.DB) @@ -682,11 +763,34 @@ func newManagerCenterTestService(t *testing.T) (*Service, *fakeManagerGateway) { func seedManager(t *testing.T, db *gorm.DB, userID int64) { t.Helper() - if err := db.Create(&teamManagerInfoRow{ID: userID, UserID: userID, Origin: "LIKEI", RegionID: "SA"}).Error; err != nil { + seedManagerWithFeatures(t, db, userID, ManagerFeatures{ + AddBDLeader: true, + GiftProps: true, + BanUser: true, + UnbanUser: true, + }) +} + +func seedManagerWithFeatures(t *testing.T, db *gorm.DB, userID int64, features ManagerFeatures) { + t.Helper() + if err := db.Create(&teamManagerInfoRow{ + ID: userID, + UserID: userID, + Origin: "LIKEI", + RegionID: "SA", + CanAddBDLeader: boolPtr(features.AddBDLeader), + CanGiftProps: boolPtr(features.GiftProps), + CanBanUser: boolPtr(features.BanUser), + CanUnbanUser: boolPtr(features.UnbanUser), + }).Error; err != nil { t.Fatalf("seed manager: %v", err) } } +func boolPtr(value bool) *bool { + return &value +} + func seedUsers(t *testing.T, db *gorm.DB, rows ...userBaseInfoRow) { t.Helper() for _, row := range rows { diff --git a/internal/service/managercenter/types.go b/internal/service/managercenter/types.go index 41eb216..fced10b 100644 --- a/internal/service/managercenter/types.go +++ b/internal/service/managercenter/types.go @@ -38,6 +38,15 @@ const ( accountStatusArchiveDevice = "ARCHIVE_DEVICE" ) +type managerFeature string + +const ( + managerFeatureAddBDLeader managerFeature = "add_bd_leader" + managerFeatureGiftProps managerFeature = "gift_props" + managerFeatureBanUser managerFeature = "ban_user" + managerFeatureUnbanUser managerFeature = "unban_user" +) + type AppError = common.AppError type AuthUser = common.AuthUser @@ -106,7 +115,15 @@ func (id *ID) UnmarshalJSON(data []byte) error { } type ProfileResponse struct { - Manager UserView `json:"manager"` + Manager UserView `json:"manager"` + Features ManagerFeatures `json:"features"` +} + +type ManagerFeatures struct { + AddBDLeader bool `json:"addBdLeader"` + GiftProps bool `json:"giftProps"` + BanUser bool `json:"banUser"` + UnbanUser bool `json:"unbanUser"` } type UserSearchResponse struct { diff --git a/migrations/054_team_manager_feature_switch.sql b/migrations/054_team_manager_feature_switch.sql new file mode 100644 index 0000000..5b130f2 --- /dev/null +++ b/migrations/054_team_manager_feature_switch.sql @@ -0,0 +1,61 @@ +SET @table_schema = DATABASE(); + +SET @ddl = IF( + EXISTS( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @table_schema + AND TABLE_NAME = 'team_manager_base_info' + AND COLUMN_NAME = 'can_add_bd_leader' + ), + 'SELECT 1', + "ALTER TABLE `team_manager_base_info` ADD COLUMN `can_add_bd_leader` tinyint(1) NOT NULL DEFAULT 1 COMMENT '经理中心是否允许添加BD Leader' AFTER `region_id`" +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @ddl = IF( + EXISTS( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @table_schema + AND TABLE_NAME = 'team_manager_base_info' + AND COLUMN_NAME = 'can_gift_props' + ), + 'SELECT 1', + "ALTER TABLE `team_manager_base_info` ADD COLUMN `can_gift_props` tinyint(1) NOT NULL DEFAULT 1 COMMENT '经理中心是否允许赠送道具' AFTER `can_add_bd_leader`" +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @ddl = IF( + EXISTS( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @table_schema + AND TABLE_NAME = 'team_manager_base_info' + AND COLUMN_NAME = 'can_ban_user' + ), + 'SELECT 1', + "ALTER TABLE `team_manager_base_info` ADD COLUMN `can_ban_user` tinyint(1) NOT NULL DEFAULT 1 COMMENT '经理中心是否允许封禁用户' AFTER `can_gift_props`" +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @ddl = IF( + EXISTS( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = @table_schema + AND TABLE_NAME = 'team_manager_base_info' + AND COLUMN_NAME = 'can_unban_user' + ), + 'SELECT 1', + "ALTER TABLE `team_manager_base_info` ADD COLUMN `can_unban_user` tinyint(1) NOT NULL DEFAULT 1 COMMENT '经理中心是否允许解封用户' AFTER `can_ban_user`" +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt;