From 15cd26c81a68af28f83a5f87fb9251c230285fbb Mon Sep 17 00:00:00 2001 From: huang Date: Fri, 24 Apr 2026 10:26:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=BA=E5=88=B6=E6=89=8B=E6=9C=BA=E7=BB=91?= =?UTF-8?q?=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.prod.yml | 5 +++++ docs/environment-variables.md | 6 ++++++ internal/service/client_auth/service.go | 21 +++++++++++++++++---- pkg/config/config.go | 6 ++++++ pkg/config/loader.go | 1 + scripts/setup-env.sh | 6 ++++++ 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 23077e9..628543e 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -24,6 +24,7 @@ version: '3.8' # - Gateway 服务配置(JUNHONG_GATEWAY_*) # - 对象存储配置(JUNHONG_STORAGE_*) # - 短信服务配置(JUNHONG_SMS_*) +# - 客户端手机号绑定开关(JUNHONG_CLIENT_REQUIRE_PHONE_BINDING,默认 true) # # 微信公众号/小程序/支付配置已迁移至数据库(tb_wechat_config 表), # 不再需要环境变量和证书文件挂载。 @@ -50,6 +51,8 @@ services: - JUNHONG_REDIS_DB=6 # JWT 配置(必填) - JUNHONG_JWT_SECRET_KEY=dev-secret-key-for-testing-only-32chars! + # 客户端配置(可选,默认 true;false=不强制绑定手机号) + - JUNHONG_CLIENT_REQUIRE_PHONE_BINDING=false # 日志配置 - JUNHONG_LOGGING_LEVEL=info - JUNHONG_LOGGING_DEVELOPMENT=false @@ -107,6 +110,8 @@ services: - JUNHONG_REDIS_DB=6 # JWT 配置(必填) - JUNHONG_JWT_SECRET_KEY=dev-secret-key-for-testing-only-32chars! + # 客户端配置(可选,默认 true;false=不强制绑定手机号) + - JUNHONG_CLIENT_REQUIRE_PHONE_BINDING=false # 日志配置 - JUNHONG_LOGGING_LEVEL=info - JUNHONG_LOGGING_DEVELOPMENT=false diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 7d257d7..5a9ec01 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -124,6 +124,12 @@ | `JUNHONG_JWT_ACCESS_TOKEN_TTL` | `24h` | Access Token TTL | | `JUNHONG_JWT_REFRESH_TOKEN_TTL` | `168h` | Refresh Token TTL (7天) | +### 客户端配置 + +| 环境变量 | 默认值 | 说明 | +|---------|--------|------| +| `JUNHONG_CLIENT_REQUIRE_PHONE_BINDING` | `true` | 是否强制 C 端用户绑定手机号(`true` 强制,`false` 不强制) | + ### 队列配置 | 环境变量 | 默认值 | 说明 | diff --git a/internal/service/client_auth/service.go b/internal/service/client_auth/service.go index 60d4b48..4706565 100644 --- a/internal/service/client_auth/service.go +++ b/internal/service/client_auth/service.go @@ -14,12 +14,12 @@ import ( wechatConfigSvc "github.com/break/junhong_cmp_fiber/internal/service/wechat_config" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/auth" + "github.com/break/junhong_cmp_fiber/pkg/config" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/wechat" "github.com/golang-jwt/jwt/v5" "github.com/redis/go-redis/v9" - "github.com/spf13/viper" "go.uber.org/zap" "gorm.io/gorm" ) @@ -439,19 +439,27 @@ func (s *Service) signAssetToken(assetType string, assetID uint) (string, error) } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(viper.GetString("jwt.secret_key") + ":asset")) + cfg := config.Get() + if cfg == nil || cfg.JWT.SecretKey == "" { + return "", errors.New(errors.CodeInternalError, "JWT 密钥未配置") + } + return token.SignedString([]byte(cfg.JWT.SecretKey + ":asset")) } func (s *Service) verifyAssetToken(assetToken string) (*assetTokenClaims, error) { if assetToken == "" { return nil, errors.New(errors.CodeInvalidParam) } + cfg := config.Get() + if cfg == nil || cfg.JWT.SecretKey == "" { + return nil, errors.New(errors.CodeInternalError, "JWT 密钥未配置") + } parsed, err := jwt.ParseWithClaims(assetToken, &assetTokenClaims{}, func(token *jwt.Token) (any, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New(errors.CodeInvalidToken) } - return []byte(viper.GetString("jwt.secret_key") + ":asset"), nil + return []byte(cfg.JWT.SecretKey + ":asset"), nil }) if err != nil { return nil, errors.New(errors.CodeInvalidToken) @@ -719,12 +727,17 @@ func (s *Service) issueLoginToken(ctx context.Context, customerID uint, assetTyp // 查询用户已绑定的主手机号,写入 JWT,供后续接口直接从 context 取用 var boundPhone string needBindPhone := false + cfg := config.Get() + requirePhoneBinding := true + if cfg != nil { + requirePhoneBinding = cfg.Client.RequirePhoneBinding + } primaryPhone, phoneErr := s.phoneStore.GetPrimaryPhone(ctx, customerID) if phoneErr == nil { boundPhone = primaryPhone.Phone } else if phoneErr != gorm.ErrRecordNotFound { return "", false, errors.Wrap(errors.CodeInternalError, phoneErr, "查询手机号绑定关系失败") - } else if viper.GetBool("client.require_phone_binding") { + } else if requirePhoneBinding { needBindPhone = true } diff --git a/pkg/config/config.go b/pkg/config/config.go index af3d375..22d5f9f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -19,6 +19,7 @@ type Config struct { Queue QueueConfig `mapstructure:"queue"` Logging LoggingConfig `mapstructure:"logging"` Middleware MiddlewareConfig `mapstructure:"middleware"` + Client ClientConfig `mapstructure:"client"` SMS SMSConfig `mapstructure:"sms"` JWT JWTConfig `mapstructure:"jwt"` DefaultAdmin DefaultAdminConfig `mapstructure:"default_admin"` @@ -94,6 +95,11 @@ type MiddlewareConfig struct { RateLimiter RateLimiterConfig `mapstructure:"rate_limiter"` // 限流器配置 } +// ClientConfig 客户端行为开关配置 +type ClientConfig struct { + RequirePhoneBinding bool `mapstructure:"require_phone_binding"` // 是否要求个人客户绑定手机号 +} + // RateLimiterConfig 限流器配置 type RateLimiterConfig struct { Max int `mapstructure:"max"` // 时间窗口内的最大请求数 diff --git a/pkg/config/loader.go b/pkg/config/loader.go index d0acb63..f0f23d5 100644 --- a/pkg/config/loader.go +++ b/pkg/config/loader.go @@ -101,6 +101,7 @@ func bindEnvVariables(v *viper.Viper) { "jwt.token_duration", "jwt.access_token_ttl", "jwt.refresh_token_ttl", + "client.require_phone_binding", "middleware.enable_rate_limiter", "middleware.rate_limiter.max", "middleware.rate_limiter.expiration", diff --git a/scripts/setup-env.sh b/scripts/setup-env.sh index 3506404..1caf623 100755 --- a/scripts/setup-env.sh +++ b/scripts/setup-env.sh @@ -273,6 +273,12 @@ export JUNHONG_REDIS_DB="$REDIS_DB" # ---------------------------------------------------------------------------- export JUNHONG_JWT_SECRET_KEY="$JWT_SECRET_KEY" +# ---------------------------------------------------------------------------- +# 客户端配置(可选) +# ---------------------------------------------------------------------------- +# 是否强制 C 端用户绑定手机号(true: 强制,false: 不强制) +export JUNHONG_CLIENT_REQUIRE_PHONE_BINDING="true" + # ---------------------------------------------------------------------------- # 服务器配置 # ----------------------------------------------------------------------------