feat(settings): support dual-mode wechat oauth defaults

This commit is contained in:
IanShaw027
2026-04-21 20:36:10 +08:00
parent 17c6348b57
commit 2cebb0dc60
15 changed files with 490 additions and 214 deletions

View File

@@ -115,6 +115,8 @@ const (
SettingKeyWeChatConnectEnabled = "wechat_connect_enabled"
SettingKeyWeChatConnectAppID = "wechat_connect_app_id"
SettingKeyWeChatConnectAppSecret = "wechat_connect_app_secret"
SettingKeyWeChatConnectOpenEnabled = "wechat_connect_open_enabled"
SettingKeyWeChatConnectMPEnabled = "wechat_connect_mp_enabled"
SettingKeyWeChatConnectMode = "wechat_connect_mode"
SettingKeyWeChatConnectScopes = "wechat_connect_scopes"
SettingKeyWeChatConnectRedirectURL = "wechat_connect_redirect_url"

View File

@@ -519,7 +519,7 @@ func (s *PaymentService) getWeChatPaymentOAuthCredential(ctx context.Context) (s
)
}
cfg, err := (&SettingService{settingRepo: s.configService.settingRepo}).GetWeChatConnectOAuthConfig(ctx)
if err != nil || cfg.Mode != "mp" || strings.TrimSpace(cfg.AppID) == "" || strings.TrimSpace(cfg.AppSecret) == "" {
if err != nil || !cfg.SupportsMode("mp") || strings.TrimSpace(cfg.AppID) == "" || strings.TrimSpace(cfg.AppSecret) == "" {
return "", "", infraerrors.ServiceUnavailable(
"WECHAT_PAYMENT_MP_NOT_CONFIGURED",
"wechat in-app payment requires a complete WeChat MP OAuth credential",

View File

@@ -209,6 +209,39 @@ func normalizeWeChatConnectScopeSetting(raw, mode string) string {
}
}
func parseWeChatConnectCapabilitySettings(settings map[string]string, enabled bool, mode string) (bool, bool) {
mode = normalizeWeChatConnectModeSetting(mode)
rawOpen, hasOpen := settings[SettingKeyWeChatConnectOpenEnabled]
rawMP, hasMP := settings[SettingKeyWeChatConnectMPEnabled]
openConfigured := hasOpen && strings.TrimSpace(rawOpen) != ""
mpConfigured := hasMP && strings.TrimSpace(rawMP) != ""
if openConfigured || mpConfigured {
openEnabled := strings.TrimSpace(rawOpen) == "true"
mpEnabled := strings.TrimSpace(rawMP) == "true"
return openEnabled, mpEnabled
}
if !enabled {
return false, false
}
if mode == "mp" {
return false, true
}
return true, false
}
func normalizeWeChatConnectStoredMode(openEnabled, mpEnabled bool, mode string) string {
switch {
case mpEnabled:
return "mp"
case openEnabled:
return "open"
default:
return normalizeWeChatConnectModeSetting(mode)
}
}
// NewSettingService 创建系统设置服务实例
func NewSettingService(settingRepo SettingRepository, cfg *config.Config) *SettingService {
return &SettingService{
@@ -277,6 +310,8 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
SettingKeyWeChatConnectEnabled,
SettingKeyWeChatConnectAppID,
SettingKeyWeChatConnectAppSecret,
SettingKeyWeChatConnectOpenEnabled,
SettingKeyWeChatConnectMPEnabled,
SettingKeyWeChatConnectMode,
SettingKeyWeChatConnectScopes,
SettingKeyWeChatConnectRedirectURL,
@@ -475,12 +510,19 @@ func DefaultWeChatConnectScopesForMode(mode string) string {
}
func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]string) (WeChatConnectOAuthConfig, error) {
enabled := settings[SettingKeyWeChatConnectEnabled] == "true"
mode := normalizeWeChatConnectModeSetting(settings[SettingKeyWeChatConnectMode])
openEnabled, mpEnabled := parseWeChatConnectCapabilitySettings(settings, enabled, mode)
mode = normalizeWeChatConnectStoredMode(openEnabled, mpEnabled, mode)
cfg := WeChatConnectOAuthConfig{
Enabled: settings[SettingKeyWeChatConnectEnabled] == "true",
Enabled: enabled,
AppID: strings.TrimSpace(settings[SettingKeyWeChatConnectAppID]),
AppSecret: strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret]),
Mode: normalizeWeChatConnectModeSetting(settings[SettingKeyWeChatConnectMode]),
Scopes: normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], settings[SettingKeyWeChatConnectMode]),
OpenEnabled: openEnabled,
MPEnabled: mpEnabled,
Mode: mode,
Scopes: normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], mode),
RedirectURL: strings.TrimSpace(settings[SettingKeyWeChatConnectRedirectURL]),
FrontendRedirectURL: strings.TrimSpace(settings[SettingKeyWeChatConnectFrontendRedirectURL]),
}
@@ -488,7 +530,7 @@ func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]strin
cfg.FrontendRedirectURL = defaultWeChatConnectFrontend
}
if !cfg.Enabled {
if !cfg.Enabled || (!cfg.OpenEnabled && !cfg.MPEnabled) {
return WeChatConnectOAuthConfig{}, infraerrors.NotFound("OAUTH_DISABLED", "wechat oauth is disabled")
}
if cfg.AppID == "" {
@@ -517,7 +559,7 @@ func (s *SettingService) weChatOAuthCapabilitiesFromSettings(settings map[string
if err != nil {
return false, false, false
}
return true, cfg.Mode == "open", cfg.Mode == "mp"
return true, cfg.OpenEnabled, cfg.MPEnabled
}
// filterUserVisibleMenuItems filters out admin-only menu items from a raw JSON
@@ -702,7 +744,11 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
settings.PaymentVisibleMethodWxpaySource = wxpaySource
settings.WeChatConnectAppID = strings.TrimSpace(settings.WeChatConnectAppID)
settings.WeChatConnectAppSecret = strings.TrimSpace(settings.WeChatConnectAppSecret)
settings.WeChatConnectMode = normalizeWeChatConnectModeSetting(settings.WeChatConnectMode)
settings.WeChatConnectMode = normalizeWeChatConnectStoredMode(
settings.WeChatConnectOpenEnabled,
settings.WeChatConnectMPEnabled,
settings.WeChatConnectMode,
)
settings.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings.WeChatConnectScopes, settings.WeChatConnectMode)
settings.WeChatConnectRedirectURL = strings.TrimSpace(settings.WeChatConnectRedirectURL)
settings.WeChatConnectFrontendRedirectURL = strings.TrimSpace(settings.WeChatConnectFrontendRedirectURL)
@@ -781,6 +827,8 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
// WeChat Connect OAuth 登录
updates[SettingKeyWeChatConnectEnabled] = strconv.FormatBool(settings.WeChatConnectEnabled)
updates[SettingKeyWeChatConnectAppID] = settings.WeChatConnectAppID
updates[SettingKeyWeChatConnectOpenEnabled] = strconv.FormatBool(settings.WeChatConnectOpenEnabled)
updates[SettingKeyWeChatConnectMPEnabled] = strconv.FormatBool(settings.WeChatConnectMPEnabled)
updates[SettingKeyWeChatConnectMode] = settings.WeChatConnectMode
updates[SettingKeyWeChatConnectScopes] = settings.WeChatConnectScopes
updates[SettingKeyWeChatConnectRedirectURL] = settings.WeChatConnectRedirectURL
@@ -1296,6 +1344,8 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
SettingKeyCustomMenuItems: "[]",
SettingKeyCustomEndpoints: "[]",
SettingKeyWeChatConnectEnabled: "false",
SettingKeyWeChatConnectOpenEnabled: "false",
SettingKeyWeChatConnectMPEnabled: "false",
SettingKeyWeChatConnectMode: "open",
SettingKeyWeChatConnectScopes: "snsapi_login",
SettingKeyWeChatConnectFrontendRedirectURL: defaultWeChatConnectFrontend,
@@ -1307,22 +1357,22 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
SettingKeyAuthSourceDefaultEmailBalance: "0",
SettingKeyAuthSourceDefaultEmailConcurrency: "5",
SettingKeyAuthSourceDefaultEmailSubscriptions: "[]",
SettingKeyAuthSourceDefaultEmailGrantOnSignup: "true",
SettingKeyAuthSourceDefaultEmailGrantOnSignup: "false",
SettingKeyAuthSourceDefaultEmailGrantOnFirstBind: "false",
SettingKeyAuthSourceDefaultLinuxDoBalance: "0",
SettingKeyAuthSourceDefaultLinuxDoConcurrency: "5",
SettingKeyAuthSourceDefaultLinuxDoSubscriptions: "[]",
SettingKeyAuthSourceDefaultLinuxDoGrantOnSignup: "true",
SettingKeyAuthSourceDefaultLinuxDoGrantOnSignup: "false",
SettingKeyAuthSourceDefaultLinuxDoGrantOnFirstBind: "false",
SettingKeyAuthSourceDefaultOIDCBalance: "0",
SettingKeyAuthSourceDefaultOIDCConcurrency: "5",
SettingKeyAuthSourceDefaultOIDCSubscriptions: "[]",
SettingKeyAuthSourceDefaultOIDCGrantOnSignup: "true",
SettingKeyAuthSourceDefaultOIDCGrantOnSignup: "false",
SettingKeyAuthSourceDefaultOIDCGrantOnFirstBind: "false",
SettingKeyAuthSourceDefaultWeChatBalance: "0",
SettingKeyAuthSourceDefaultWeChatConcurrency: "5",
SettingKeyAuthSourceDefaultWeChatSubscriptions: "[]",
SettingKeyAuthSourceDefaultWeChatGrantOnSignup: "true",
SettingKeyAuthSourceDefaultWeChatGrantOnSignup: "false",
SettingKeyAuthSourceDefaultWeChatGrantOnFirstBind: "false",
SettingKeyForceEmailOnThirdPartySignup: "false",
SettingKeySMTPPort: "587",
@@ -1595,8 +1645,17 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
result.WeChatConnectAppID = strings.TrimSpace(settings[SettingKeyWeChatConnectAppID])
result.WeChatConnectAppSecret = strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret])
result.WeChatConnectAppSecretConfigured = result.WeChatConnectAppSecret != ""
result.WeChatConnectMode = normalizeWeChatConnectModeSetting(settings[SettingKeyWeChatConnectMode])
result.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], settings[SettingKeyWeChatConnectMode])
result.WeChatConnectOpenEnabled, result.WeChatConnectMPEnabled = parseWeChatConnectCapabilitySettings(
settings,
result.WeChatConnectEnabled,
settings[SettingKeyWeChatConnectMode],
)
result.WeChatConnectMode = normalizeWeChatConnectStoredMode(
result.WeChatConnectOpenEnabled,
result.WeChatConnectMPEnabled,
settings[SettingKeyWeChatConnectMode],
)
result.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], result.WeChatConnectMode)
result.WeChatConnectRedirectURL = strings.TrimSpace(settings[SettingKeyWeChatConnectRedirectURL])
result.WeChatConnectFrontendRedirectURL = strings.TrimSpace(settings[SettingKeyWeChatConnectFrontendRedirectURL])
if result.WeChatConnectFrontendRedirectURL == "" {
@@ -1744,7 +1803,7 @@ func parseProviderDefaultGrantSettings(settings map[string]string, keys authSour
Balance: defaultAuthSourceBalance,
Concurrency: defaultAuthSourceConcurrency,
Subscriptions: []DefaultSubscriptionSetting{},
GrantOnSignup: true,
GrantOnSignup: false,
GrantOnFirstBind: false,
}
@@ -2092,6 +2151,8 @@ func (s *SettingService) GetWeChatConnectOAuthConfig(ctx context.Context) (WeCha
SettingKeyWeChatConnectEnabled,
SettingKeyWeChatConnectAppID,
SettingKeyWeChatConnectAppSecret,
SettingKeyWeChatConnectOpenEnabled,
SettingKeyWeChatConnectMPEnabled,
SettingKeyWeChatConnectMode,
SettingKeyWeChatConnectScopes,
SettingKeyWeChatConnectRedirectURL,

View File

@@ -81,10 +81,12 @@ func TestSettingService_GetAuthSourceDefaultSettings_ParsesValuesAndDefaults(t *
require.Equal(t, 0.0, got.LinuxDo.Balance)
require.Equal(t, 5, got.LinuxDo.Concurrency)
require.Equal(t, []DefaultSubscriptionSetting{}, got.LinuxDo.Subscriptions)
require.True(t, got.LinuxDo.GrantOnSignup)
require.False(t, got.LinuxDo.GrantOnSignup)
require.True(t, got.LinuxDo.GrantOnFirstBind)
require.Equal(t, 5, got.OIDC.Concurrency)
require.Equal(t, 5, got.WeChat.Concurrency)
require.False(t, got.OIDC.GrantOnSignup)
require.False(t, got.WeChat.GrantOnSignup)
require.True(t, got.ForceEmailOnThirdPartySignup)
}

View File

@@ -99,6 +99,8 @@ func TestSettingService_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *
SettingKeyWeChatConnectAppSecret: "wx-mp-secret",
SettingKeyWeChatConnectMode: "mp",
SettingKeyWeChatConnectScopes: "snsapi_base",
SettingKeyWeChatConnectOpenEnabled: "true",
SettingKeyWeChatConnectMPEnabled: "true",
SettingKeyWeChatConnectRedirectURL: "https://api.example.com/api/v1/auth/oauth/wechat/callback",
SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
},
@@ -107,6 +109,6 @@ func TestSettingService_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *
settings, err := svc.GetPublicSettings(context.Background())
require.NoError(t, err)
require.True(t, settings.WeChatOAuthEnabled)
require.False(t, settings.WeChatOAuthOpenEnabled)
require.True(t, settings.WeChatOAuthOpenEnabled)
require.True(t, settings.WeChatOAuthMPEnabled)
}

View File

@@ -59,6 +59,8 @@ func TestSettingService_GetWeChatConnectOAuthConfig_UsesDatabaseOverrides(t *tes
SettingKeyWeChatConnectAppSecret: "wx-db-secret",
SettingKeyWeChatConnectMode: "mp",
SettingKeyWeChatConnectScopes: "snsapi_base",
SettingKeyWeChatConnectOpenEnabled: "true",
SettingKeyWeChatConnectMPEnabled: "true",
SettingKeyWeChatConnectRedirectURL: "https://api.example.com/api/v1/auth/oauth/wechat/callback",
SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
},
@@ -70,6 +72,8 @@ func TestSettingService_GetWeChatConnectOAuthConfig_UsesDatabaseOverrides(t *tes
require.True(t, got.Enabled)
require.Equal(t, "wx-db-app", got.AppID)
require.Equal(t, "wx-db-secret", got.AppSecret)
require.True(t, got.OpenEnabled)
require.True(t, got.MPEnabled)
require.Equal(t, "mp", got.Mode)
require.Equal(t, "snsapi_base", got.Scopes)
require.Equal(t, "https://api.example.com/api/v1/auth/oauth/wechat/callback", got.RedirectURL)

View File

@@ -36,6 +36,8 @@ type SystemSettings struct {
WeChatConnectAppID string
WeChatConnectAppSecret string
WeChatConnectAppSecretConfigured bool
WeChatConnectOpenEnabled bool
WeChatConnectMPEnabled bool
WeChatConnectMode string
WeChatConnectScopes string
WeChatConnectRedirectURL string
@@ -191,12 +193,30 @@ type WeChatConnectOAuthConfig struct {
Enabled bool
AppID string
AppSecret string
OpenEnabled bool
MPEnabled bool
Mode string
Scopes string
RedirectURL string
FrontendRedirectURL string
}
func (cfg WeChatConnectOAuthConfig) SupportsMode(mode string) bool {
switch normalizeWeChatConnectModeSetting(mode) {
case "mp":
return cfg.MPEnabled
default:
return cfg.OpenEnabled
}
}
func (cfg WeChatConnectOAuthConfig) ScopeForMode(mode string) string {
if normalizeWeChatConnectModeSetting(mode) == "mp" {
return normalizeWeChatConnectScopeSetting(cfg.Scopes, "mp")
}
return defaultWeChatConnectScopeForMode("open")
}
// StreamTimeoutSettings 流超时处理配置(仅控制超时后的处理方式,超时判定由网关配置控制)
type StreamTimeoutSettings struct {
// Enabled 是否启用流超时处理