Merge pull request #6986 from feeeei/feat/site-billing-mode

feat(settings): 新增「站点类型」开关(充值 & 订阅 / 仅充值 / 仅订阅)
This commit is contained in:
Wesley Liddick
2026-09-11 16:34:46 +08:00
committed by GitHub
46 changed files with 938 additions and 47 deletions
@@ -383,6 +383,7 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
GrokDefaultBaseURLMode: settings.GrokDefaultBaseURLMode,
AvailableChannelsEnabled: settings.AvailableChannelsEnabled,
SubscriptionEnabled: settings.SubscriptionEnabled,
ModelPlazaEnabled: settings.ModelPlazaEnabled,
ModelPlazaRequireAuth: settings.ModelPlazaRequireAuth,
@@ -579,6 +579,9 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
if before.AvailableChannelsEnabled != after.AvailableChannelsEnabled {
changed = append(changed, "available_channels_enabled")
}
if before.SubscriptionEnabled != after.SubscriptionEnabled {
changed = append(changed, "subscription_enabled")
}
if before.ModelPlazaEnabled != after.ModelPlazaEnabled {
changed = append(changed, "model_plaza_enabled")
}
@@ -186,3 +186,20 @@ func TestUpdateSettingsValidatesTencentCaptchaAppIDWhenEnabledFlagIsOmitted(t *t
require.Equal(t, http.StatusBadRequest, rec.Code)
require.Contains(t, rec.Body.String(), "positive integer")
}
// subscription_enabled is an opt-out switch: an explicit false is written as-is,
// and a later payload that omits the field keeps the stored value.
func TestUpdateSettingsSubscriptionEnabledIsWritableAndKeptWhenOmitted(t *testing.T) {
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
service.SettingKeySubscriptionEnabled: "true",
})
rec := doUpdateSettings(t, h, map[string]any{"subscription_enabled": false}, nil)
require.Equal(t, http.StatusOK, rec.Code)
require.Equal(t, "false", repo.values[service.SettingKeySubscriptionEnabled])
rec = doUpdateSettings(t, h, map[string]any{"site_name": "Example Gateway"}, nil)
require.Equal(t, http.StatusOK, rec.Code)
require.Equal(t, "false", repo.values[service.SettingKeySubscriptionEnabled],
"a payload without subscription_enabled must not flip the stored value back to true")
}
@@ -344,6 +344,9 @@ type UpdateSettingsRequest struct {
// Available Channels feature switch (user-facing)
AvailableChannelsEnabled *bool `json:"available_channels_enabled"`
// Subscription feature switch (user-facing subscription surface; see SettingKeySubscriptionEnabled)
SubscriptionEnabled *bool `json:"subscription_enabled"`
// Model Plaza feature switches + description
ModelPlazaEnabled *bool `json:"model_plaza_enabled"`
ModelPlazaRequireAuth *bool `json:"model_plaza_require_auth"`
@@ -1937,6 +1940,12 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
return previousSettings.AvailableChannelsEnabled
}(),
SubscriptionEnabled: func() bool {
if req.SubscriptionEnabled != nil {
return *req.SubscriptionEnabled
}
return previousSettings.SubscriptionEnabled
}(),
ModelPlazaEnabled: func() bool {
if req.ModelPlazaEnabled != nil {
return *req.ModelPlazaEnabled
@@ -2380,6 +2389,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
GrokDefaultBaseURLMode: updatedSettings.GrokDefaultBaseURLMode,
AvailableChannelsEnabled: updatedSettings.AvailableChannelsEnabled,
SubscriptionEnabled: updatedSettings.SubscriptionEnabled,
ModelPlazaEnabled: updatedSettings.ModelPlazaEnabled,
ModelPlazaRequireAuth: updatedSettings.ModelPlazaRequireAuth,