feat: rebuild pet app and backend around core care flows
This commit is contained in:
@@ -9,28 +9,14 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Config 全局配置
|
||||
type Config struct {
|
||||
Server ServerConfig `mapstructure:"server"`
|
||||
MySQL MySQLConfig `mapstructure:"mysql"`
|
||||
MinIO MinIOConfig `mapstructure:"minio"`
|
||||
JWT JWTConfig `mapstructure:"jwt"`
|
||||
WeChat WeChatConfig `mapstructure:"wechat"`
|
||||
Auth AuthConfig `mapstructure:"auth"`
|
||||
Admin AdminConfig `mapstructure:"admin"`
|
||||
AI AIConfig `mapstructure:"ai"`
|
||||
}
|
||||
|
||||
// AIConfig 大模型配置(provider 中立,openai 兼容 base_url)
|
||||
type AIConfig struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
Provider string `mapstructure:"provider"` // openai(兼容) / mock
|
||||
BaseURL string `mapstructure:"base_url"` // 如 https://api.deepseek.com
|
||||
APIKey string `mapstructure:"api_key"` // 用 PETS_AI_API_KEY 覆盖
|
||||
Model string `mapstructure:"model"` // 如 deepseek-chat
|
||||
Temperature float64 `mapstructure:"temperature"`
|
||||
MaxTokens int `mapstructure:"max_tokens"`
|
||||
TimeoutSec int `mapstructure:"timeout_sec"`
|
||||
Server ServerConfig `mapstructure:"server"`
|
||||
Postgres PostgresConfig `mapstructure:"postgres"`
|
||||
MinIO MinIOConfig `mapstructure:"minio"`
|
||||
JWT JWTConfig `mapstructure:"jwt"`
|
||||
WeChat WeChatConfig `mapstructure:"wechat"`
|
||||
Auth AuthConfig `mapstructure:"auth"`
|
||||
Admin AdminConfig `mapstructure:"admin"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
@@ -38,47 +24,36 @@ type ServerConfig struct {
|
||||
Mode string `mapstructure:"mode"`
|
||||
}
|
||||
|
||||
type MySQLConfig struct {
|
||||
type PostgresConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Port int `mapstructure:"port"`
|
||||
User string `mapstructure:"user"`
|
||||
Password string `mapstructure:"password"`
|
||||
Database string `mapstructure:"database"`
|
||||
Charset string `mapstructure:"charset"`
|
||||
SSLMode string `mapstructure:"ssl_mode"`
|
||||
}
|
||||
|
||||
// DSN 返回 GORM MySQL 连接串
|
||||
func (m MySQLConfig) DSN() string {
|
||||
return fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
|
||||
m.User, m.Password, m.Host, m.Port, m.Database, m.Charset,
|
||||
)
|
||||
func (p PostgresConfig) DSN() string {
|
||||
return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s TimeZone=Asia/Shanghai",
|
||||
p.Host, p.Port, p.User, p.Password, p.Database, p.SSLMode)
|
||||
}
|
||||
|
||||
type MinIOConfig struct {
|
||||
Endpoint string `mapstructure:"endpoint"`
|
||||
AccessKey string `mapstructure:"access_key"`
|
||||
SecretKey string `mapstructure:"secret_key"`
|
||||
Bucket string `mapstructure:"bucket"`
|
||||
UseSSL bool `mapstructure:"use_ssl"`
|
||||
PublicBaseURL string `mapstructure:"public_base_url"`
|
||||
Endpoint string `mapstructure:"endpoint"`
|
||||
AccessKey string `mapstructure:"access_key"`
|
||||
SecretKey string `mapstructure:"secret_key"`
|
||||
Bucket string `mapstructure:"bucket"`
|
||||
UseSSL bool `mapstructure:"use_ssl"`
|
||||
}
|
||||
|
||||
// JWTConfig access token 短、refresh token 长:
|
||||
// access 过期由前端拿 refresh 静默续期,refresh 每次使用都轮换。
|
||||
type JWTConfig struct {
|
||||
Secret string `mapstructure:"secret"`
|
||||
ExpireHours int `mapstructure:"expire_hours"` // access token 有效期
|
||||
RefreshExpireHours int `mapstructure:"refresh_expire_hours"` // 小程序用户刷新令牌有效期
|
||||
AdminRefreshExpireHours int `mapstructure:"admin_refresh_expire_hours"` // 后台刷新令牌有效期(更短)
|
||||
Secret string `mapstructure:"secret"`
|
||||
ExpireHours int `mapstructure:"expire_hours"`
|
||||
}
|
||||
|
||||
type WeChatConfig struct {
|
||||
AppID string `mapstructure:"app_id"`
|
||||
AppSecret string `mapstructure:"app_secret"`
|
||||
// MsgToken 消息推送(服务器配置)校验用的 Token。图片异步安全检测的结果
|
||||
// 由微信 push 到我们的回调地址,用它校验来源。留空则回调不做校验(仅本地调试)
|
||||
MsgToken string `mapstructure:"msg_token"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
@@ -90,79 +65,54 @@ type AdminConfig struct {
|
||||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
// setDefaults 为每个配置项登记默认值。
|
||||
// 必须登记:viper 的 AutomaticEnv 只对「已知的 key」生效,未登记的 key 即使设了
|
||||
// PETS_XXX 环境变量,Unmarshal 时也绑不上(容器里纯 .env 部署会静默取到空值)。
|
||||
func setDefaults(v *viper.Viper) {
|
||||
v.SetDefault("server.port", 9090)
|
||||
v.SetDefault("server.mode", "release")
|
||||
|
||||
v.SetDefault("mysql.host", "127.0.0.1")
|
||||
v.SetDefault("mysql.port", 3306)
|
||||
v.SetDefault("mysql.user", "root")
|
||||
v.SetDefault("mysql.password", "")
|
||||
v.SetDefault("mysql.database", "pets")
|
||||
v.SetDefault("mysql.charset", "utf8mb4")
|
||||
|
||||
v.SetDefault("minio.endpoint", "127.0.0.1:9000")
|
||||
v.SetDefault("minio.access_key", "")
|
||||
v.SetDefault("minio.secret_key", "")
|
||||
v.SetDefault("minio.bucket", "pets")
|
||||
v.SetDefault("minio.use_ssl", false)
|
||||
v.SetDefault("minio.public_base_url", "")
|
||||
|
||||
v.SetDefault("jwt.secret", "")
|
||||
v.SetDefault("jwt.expire_hours", 2)
|
||||
v.SetDefault("jwt.refresh_expire_hours", 720) // 30 天
|
||||
v.SetDefault("jwt.admin_refresh_expire_hours", 168)
|
||||
|
||||
v.SetDefault("wechat.app_id", "")
|
||||
v.SetDefault("wechat.app_secret", "")
|
||||
v.SetDefault("wechat.msg_token", "") // 用 PETS_WECHAT_MSG_TOKEN 覆盖
|
||||
|
||||
v.SetDefault("auth.dev_login", false)
|
||||
|
||||
v.SetDefault("admin.username", "sundynix")
|
||||
v.SetDefault("admin.password", "")
|
||||
|
||||
v.SetDefault("ai.enabled", false)
|
||||
v.SetDefault("ai.provider", "openai")
|
||||
v.SetDefault("ai.base_url", "")
|
||||
v.SetDefault("ai.api_key", "")
|
||||
v.SetDefault("ai.model", "")
|
||||
v.SetDefault("ai.temperature", 0.6)
|
||||
v.SetDefault("ai.max_tokens", 1024)
|
||||
v.SetDefault("ai.timeout_sec", 30)
|
||||
}
|
||||
|
||||
// Load 载入配置:默认值 → configs/config.yaml(可选)→ 环境变量覆盖。
|
||||
// 环境变量用 PETS_ 前缀、点转下划线,例如 PETS_MYSQL_PASSWORD 覆盖 mysql.password。
|
||||
// 容器部署不带 config.yaml,全部走 .env 注入的环境变量。
|
||||
func Load() (*Config, error) {
|
||||
v := viper.New()
|
||||
setDefaults(v)
|
||||
v.SetDefault("server.port", 9090)
|
||||
v.SetDefault("server.mode", "debug")
|
||||
v.SetDefault("postgres.host", "192.168.100.127")
|
||||
v.SetDefault("postgres.port", 5432)
|
||||
v.SetDefault("postgres.user", "root")
|
||||
v.SetDefault("postgres.password", "")
|
||||
v.SetDefault("postgres.database", "sundynix-pets")
|
||||
v.SetDefault("postgres.ssl_mode", "disable")
|
||||
v.SetDefault("minio.endpoint", "https://s3.sundynix.cn")
|
||||
v.SetDefault("minio.access_key", "")
|
||||
v.SetDefault("minio.secret_key", "")
|
||||
v.SetDefault("minio.bucket", "sundynix-pet")
|
||||
v.SetDefault("minio.use_ssl", true)
|
||||
v.SetDefault("jwt.secret", "")
|
||||
v.SetDefault("jwt.expire_hours", 168)
|
||||
v.SetDefault("wechat.app_id", "")
|
||||
v.SetDefault("wechat.app_secret", "")
|
||||
v.SetDefault("auth.dev_login", false)
|
||||
v.SetDefault("admin.username", "sundynix")
|
||||
v.SetDefault("admin.password", "")
|
||||
|
||||
v.SetConfigName("config")
|
||||
v.SetConfigType("yaml")
|
||||
v.AddConfigPath("./configs")
|
||||
v.AddConfigPath("../../configs")
|
||||
v.AddConfigPath(".")
|
||||
|
||||
v.SetEnvPrefix("PETS")
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
v.AutomaticEnv()
|
||||
|
||||
// 配置文件可选:找不到就只用默认值 + 环境变量;文件存在但解析失败仍要报错。
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
var notFound viper.ConfigFileNotFoundError
|
||||
if !errors.As(err, ¬Found) && !os.IsNotExist(err) {
|
||||
var missing viper.ConfigFileNotFoundError
|
||||
if !errors.As(err, &missing) && !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("read config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
if err := v.Unmarshal(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal config: %w", err)
|
||||
return nil, fmt.Errorf("decode config: %w", err)
|
||||
}
|
||||
if cfg.Postgres.Password == "" || cfg.JWT.Secret == "" {
|
||||
return nil, errors.New("postgres.password and jwt.secret are required")
|
||||
}
|
||||
if cfg.MinIO.AccessKey == "" || cfg.MinIO.SecretKey == "" {
|
||||
return nil, errors.New("minio access_key and secret_key are required")
|
||||
}
|
||||
if cfg.Server.Mode == "release" && cfg.Auth.DevLogin {
|
||||
return nil, errors.New("dev login must be disabled in release mode")
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user