topfans/backend/gateway/config/config.go
zerosaturation a337f43f86 fix(config): port alignment + service test doubles (batch 4.1/4.5)
- gateway/config Dubbo URL 默认端口修正(原 20004/20005 与服务实际 20001/20004 不符);
- userService main.go 硬编码 WithPort(20000) 修死码, -port flag / PORT env 真正生效;
- .env.example 11 个 DUBBO URL 同步(对齐 serviceDefaultPorts map + 实际服务 bind);
- 删除 DUBBO_STARBOOK_SERVICE_URL 死引用(starbookService 已在 batch 4.2 删除);
- .env.example Service Ports 注释块补全 10 行, 与网关 Dubbo URL + port_test 三方一致;
- 10 个 service 各加 port_test.go, 断言 flag 默认与 serviceDefaultPorts map 一致;
- 11 个 port_test 总计 13 测试覆盖(网关 + 10 service);
- .gitignore 加 userService/main.go.bak 排除(临时备份)。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 14:02:14 +08:00

283 lines
8.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package config
import (
"fmt"
"os"
"strconv"
)
// Config 网关配置
type Config struct {
Server ServerConfig
Dubbo DubboConfig
JWT JWTConfig
OSS OSSConfig
Segment SegmentConfig
Dify DifyConfig
Minimax MinimaxConfig
OpenAI OpenAIConfig
LaserGen LaserGenConfig
LaserCompositor LaserCompositorConfig
Redis RedisConfig
DB DBConfig
WebSocket WebSocketConfig
Root string
}
// LaserGenConfig 镭射卡生成器配置
// Provider:
// "minimax" (默认) - 后端直连 MiniMax + 并行 5 variant + compositor 合成
// "dify" - 调 Dify 工作流, 由 Dify 内部分发与合成, 阻塞模式
// "openai" - 后端直连 OpenAI Images API /v1/images/edits, 5 路并发 + 直接落 OSS
type LaserGenConfig struct {
Provider string
}
// MinimaxConfig MiniMax 图像生成配置
type MinimaxConfig struct {
APIKey string // MiniMax API 密钥
APIURL string // 默认 https://api.minimaxi.com/v1/image_generation
}
// LaserCompositorConfig laser-compositor 合成服务配置
type LaserCompositorConfig struct {
URL string // 默认 http://127.0.0.1:7000
}
// DifyConfig Dify 工作流配置
type DifyConfig struct {
APIBase string
APIKey string
Workflow string // 工作流名称/ID默认 laser_prompt_enhancer_v2
}
// OpenAIConfig OpenAI Images API 配置
// 仅在 LASER_GEN_PROVIDER=openai 时使用
type OpenAIConfig struct {
APIKey string // 必填
BaseURL string // 默认 https://api.openai.com/v1
Model string // 默认 gpt-image-1.5
}
// RedisConfig Redis 配置
type RedisConfig struct {
Host string
Port int
Password string
DB int
}
// DBConfig 数据库配置
type DBConfig struct {
Host string
Port int
User string
Password string
DBName string
SSLMode string
TimeZone string
}
// ServerConfig 服务器配置
type ServerConfig struct {
Port string
Mode string // debug, release, test
}
// DubboConfig Dubbo 服务配置
//
// ★ 批次 4.2:StarbookServiceURL 已删除(starbookService 孤儿代码,生产从未部署)。
// Gateway 同步移除 starbookService Dubbo 客户端与 /api/v1/starbook/* 路由,
// 前端如需星册展示,请直接调用 assetService(批次 4.2 起 assetService 是该能力的 owner)。
type DubboConfig struct {
UserServiceURL string
SocialServiceURL string
AssetServiceURL string
GalleryServiceURL string
ActivityServiceURL string
TaskServiceURL string
AIChatServiceURL string
StatisticServiceURL string
NotificationServiceURL string
ModerationServiceURL string
}
// JWTConfig JWT 配置
type JWTConfig struct {
Secret string
}
// SegmentConfig 人像抠图(服务端代理)
type SegmentConfig struct {
Provider string // auto | imageseg | viapi | ivpd | http
InferenceURL string // 自部署 rembg 等 HTTP 地址,如 http://127.0.0.1:7000/api/remove
}
// OSSConfig OSS 配置
type OSSConfig struct {
Region string
BucketName string
RoleArn string
AccessKeyID string
AccessKeySecret string
AvatarDir string // 头像上传目录,如 "avatar/"
AssetDir string // 资产上传目录,如 "asset/"
ReportDir string // 举报证据目录,如 "report/"
FeedbackDir string // 反馈截图目录,如 "feedback/"
TokenExpireTime int // Token 过期时间(秒),默认 3600
}
// GetUploadDir 根据类型获取上传目录spec §9.1report/feedback 与 avatar/asset 命名空间隔离)
func (c *OSSConfig) GetUploadDir(uploadType string) string {
switch uploadType {
case "avatar":
return c.AvatarDir
case "asset":
return c.AssetDir
case "report":
return c.ReportDir
case "feedback":
return c.FeedbackDir
default:
return c.AssetDir // 默认使用 asset 目录
}
}
// WebSocketConfig WebSocket 配置
type WebSocketConfig struct {
AIChatPath string // AI Chat WebSocket 路径,默认 /ai-chat
ActivityPath string // 活动实时推送 WS 路径,默认 /activity
}
// Load 加载配置
func Load() *Config {
root, _ := os.Getwd()
return &Config{
Root: root,
Server: ServerConfig{
Port: getEnv("SERVER_PORT", "8080"),
Mode: getEnv("GIN_MODE", "debug"),
},
Dubbo: DubboConfig{
UserServiceURL: getEnv("DUBBO_USER_SERVICE_URL", "tri://127.0.0.1:20000"),
SocialServiceURL: getEnv("DUBBO_SOCIAL_SERVICE_URL", "tri://127.0.0.1:20002"),
AssetServiceURL: getEnv("DUBBO_ASSET_SERVICE_URL", "tri://127.0.0.1:20003"),
GalleryServiceURL: getEnv("DUBBO_GALLERY_SERVICE_URL", "tri://127.0.0.1:20001"),
ActivityServiceURL: getEnv("DUBBO_ACTIVITY_SERVICE_URL", "tri://127.0.0.1:20004"),
TaskServiceURL: getEnv("DUBBO_TASK_SERVICE_URL", "tri://127.0.0.1:20006"),
AIChatServiceURL: getEnv("DUBBO_AI_CHAT_SERVICE_URL", "tri://127.0.0.1:20008"),
StatisticServiceURL: getEnv("DUBBO_STATISTIC_SERVICE_URL", "tri://127.0.0.1:20009"),
NotificationServiceURL: getEnv("DUBBO_NOTIFICATION_SERVICE_URL", "tri://127.0.0.1:20010"),
ModerationServiceURL: getEnv("DUBBO_MODERATION_SERVICE_URL", "tri://127.0.0.1:20011"),
},
JWT: JWTConfig{
Secret: getEnv("JWT_SECRET", ""),
},
OSS: OSSConfig{
Region: getEnv("OSS_REGION", "cn-shanghai"),
BucketName: getEnv("OSS_BUCKET_NAME", ""),
RoleArn: getEnv("OSS_STS_ROLE_ARN", ""),
AccessKeyID: getEnv("OSS_ACCESS_KEY_ID", ""),
AccessKeySecret: getEnv("OSS_ACCESS_KEY_SECRET", ""),
AvatarDir: getEnv("OSS_AVATAR_DIR", "avatar/"),
AssetDir: getEnv("OSS_ASSET_DIR", "asset/"),
ReportDir: getEnv("OSS_REPORT_DIR", "report/"),
FeedbackDir: getEnv("OSS_FEEDBACK_DIR", "feedback/"),
TokenExpireTime: getEnvInt("OSS_TOKEN_EXPIRE_TIME", 3600),
},
Segment: SegmentConfig{
Provider: getEnv("SEGMENT_PROVIDER", "imageseg"),
InferenceURL: getEnv("SEGMENT_INFERENCE_URL", ""),
},
Dify: DifyConfig{
APIBase: getEnv("DIFY_API_BASE", ""),
APIKey: getEnv("DIFY_API_KEY", ""),
Workflow: getEnv("DIFY_WORKFLOW", "laser_prompt_enhancer_v2"),
},
Minimax: MinimaxConfig{
APIKey: getEnv("MINIMAX_API_KEY", ""),
APIURL: getEnv("MINIMAX_API_URL", "https://api.minimaxi.com/v1/image_generation"),
},
OpenAI: OpenAIConfig{
APIKey: getEnv("OPENAI_API_KEY", ""),
BaseURL: getEnv("OPENAI_BASE_URL", "https://api.openai.com/v1"),
Model: getEnv("OPENAI_MODEL", "gpt-image-1.5"),
},
LaserGen: LaserGenConfig{
Provider: getEnv("LASER_GEN_PROVIDER", "minimax"),
},
LaserCompositor: LaserCompositorConfig{
URL: getEnv("LASER_COMPOSITOR_URL", "http://127.0.0.1:7000"),
},
Redis: RedisConfig{
Host: getEnv("REDIS_HOST", "127.0.0.1"),
Port: getEnvInt("REDIS_PORT", 6379),
Password: getEnv("REDIS_PASSWORD", ""),
DB: getEnvInt("REDIS_DB", 0),
},
DB: DBConfig{
Host: getEnv("DB_HOST", "localhost"),
Port: getEnvInt("DB_PORT", 5432),
User: getEnv("DB_USER", "postgres"),
Password: getEnv("DB_PASSWORD", ""),
DBName: getEnv("DB_NAME", "top-fans"),
SSLMode: getEnv("DB_SSLMODE", "disable"),
TimeZone: getEnv("DB_TIMEZONE", "Asia/Shanghai"),
},
WebSocket: WebSocketConfig{
AIChatPath: getEnv("WS_AI_CHAT_PATH", "/ai-chat"),
ActivityPath: getEnv("WS_ACTIVITY_PATH", "/activity"),
},
}
}
// getEnv 获取环境变量,如果不存在则返回默认值
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
// getEnvInt 获取整型环境变量,如果不存在或解析失败则返回默认值
func getEnvInt(key string, defaultValue int) int {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
intValue, err := strconv.Atoi(value)
if err != nil {
return defaultValue
}
return intValue
}
// Validate 验证配置
func (c *Config) Validate() error {
if c.Server.Port == "" {
return fmt.Errorf("server port is required")
}
if c.Dubbo.UserServiceURL == "" {
return fmt.Errorf("dubbo user service URL is required")
}
if c.Dubbo.SocialServiceURL == "" {
return fmt.Errorf("dubbo social service URL is required")
}
if c.Dubbo.AssetServiceURL == "" {
return fmt.Errorf("dubbo asset service URL is required")
}
if c.Dubbo.GalleryServiceURL == "" {
return fmt.Errorf("dubbo gallery service URL is required")
}
if c.Dubbo.ActivityServiceURL == "" {
return fmt.Errorf("dubbo activity service URL is required")
}
if c.Dubbo.TaskServiceURL == "" {
return fmt.Errorf("dubbo task service URL is required")
}
if c.JWT.Secret == "" {
return fmt.Errorf("JWT secret is required")
}
return nil
}