package appconfig import ( "errors" "fmt" "strings" "hyapp-admin-server/internal/appctx" "hyapp-admin-server/internal/model" ) const ( runtimeCapabilitiesGroup = "runtime-capabilities" activityLeaderboardScopeKey = "activity_leaderboard_scope" activityLeaderboardScopeAppGlobal = "app_global" activityLeaderboardScopeViewerRegion = "viewer_region" resourceEquipCommandIDPolicyKey = "resource_equip_command_id_policy" resourceEquipCommandIDPolicyLegacyOptional = "legacy_optional" resourceEquipCommandIDPolicyRequired = "required" ) type RuntimeCapabilitiesConfig struct { AppCode string `json:"appCode"` ActivityLeaderboardScope string `json:"activityLeaderboardScope"` ResourceEquipCommandIDPolicy string `json:"resourceEquipCommandIdPolicy"` UpdatedAtMS int64 `json:"updatedAtMs"` } func defaultRuntimeCapabilitiesConfig(appCode string) RuntimeCapabilitiesConfig { return RuntimeCapabilitiesConfig{ AppCode: appctx.Normalize(appCode), ActivityLeaderboardScope: activityLeaderboardScopeAppGlobal, ResourceEquipCommandIDPolicy: resourceEquipCommandIDPolicyLegacyOptional, } } func (s *AppConfigService) GetRuntimeCapabilitiesConfig(appCode string) (RuntimeCapabilitiesConfig, error) { appCode = appctx.Normalize(appCode) items, err := s.store.ListOwnedAppConfigs(appCode, runtimeCapabilitiesGroup) if err != nil { return RuntimeCapabilitiesConfig{}, err } return runtimeCapabilitiesConfigFromModels(appCode, items) } func (s *AppConfigService) UpdateRuntimeCapabilitiesConfig(appCode string, request runtimeCapabilitiesConfigRequest) (RuntimeCapabilitiesConfig, error) { items, err := runtimeCapabilitiesConfigModels(appCode, request) if err != nil { return RuntimeCapabilitiesConfig{}, err } // 两项能力必须在同一批 upsert 中写入当前鉴权 App,避免页面保存后 Gateway 看到半套策略。 if err := s.store.UpsertScopedAppConfigs(appctx.Normalize(appCode), items); err != nil { return RuntimeCapabilitiesConfig{}, err } return s.GetRuntimeCapabilitiesConfig(appCode) } func runtimeCapabilitiesConfigModels(appCode string, request runtimeCapabilitiesConfigRequest) ([]model.AppConfig, error) { appCode = appctx.Normalize(appCode) leaderboardScope := strings.ToLower(strings.TrimSpace(request.ActivityLeaderboardScope)) if leaderboardScope != activityLeaderboardScopeAppGlobal && leaderboardScope != activityLeaderboardScopeViewerRegion { return nil, errors.New("活动榜单范围不正确") } equipPolicy := strings.ToLower(strings.TrimSpace(request.ResourceEquipCommandIDPolicy)) if equipPolicy != resourceEquipCommandIDPolicyLegacyOptional && equipPolicy != resourceEquipCommandIDPolicyRequired { return nil, errors.New("资源佩戴 command_id 策略不正确") } return []model.AppConfig{ { AppCode: appCode, Group: runtimeCapabilitiesGroup, Key: activityLeaderboardScopeKey, Value: leaderboardScope, Description: "活动榜单范围:app_global 或 viewer_region", }, { AppCode: appCode, Group: runtimeCapabilitiesGroup, Key: resourceEquipCommandIDPolicyKey, Value: equipPolicy, Description: "资源佩戴幂等键策略:legacy_optional 或 required", }, }, nil } func runtimeCapabilitiesConfigFromModels(appCode string, items []model.AppConfig) (RuntimeCapabilitiesConfig, error) { config := defaultRuntimeCapabilitiesConfig(appCode) for _, item := range items { value := strings.ToLower(strings.TrimSpace(item.Value)) switch strings.TrimSpace(item.Key) { case activityLeaderboardScopeKey: if value != activityLeaderboardScopeAppGlobal && value != activityLeaderboardScopeViewerRegion { return RuntimeCapabilitiesConfig{}, fmt.Errorf("活动榜单范围配置无效: %s", value) } config.ActivityLeaderboardScope = value case resourceEquipCommandIDPolicyKey: if value != resourceEquipCommandIDPolicyLegacyOptional && value != resourceEquipCommandIDPolicyRequired { return RuntimeCapabilitiesConfig{}, fmt.Errorf("资源佩戴 command_id 策略配置无效: %s", value) } config.ResourceEquipCommandIDPolicy = value default: continue } if item.UpdatedAtMS > config.UpdatedAtMS { config.UpdatedAtMS = item.UpdatedAtMS } } return config, nil }