init: 毛孩子计划 小程序 + Go 后端 + 内嵌后台
- pets-fe: 微信原生小程序(首页/计划/记录/报告/社区/引导), 服务端驱动、无假数据;弹层改用 scroll-view,打开时隐藏自定义 tabBar - pets-be: Gin + GORM(MySQL, sundynix_ 前缀) + MinIO,统一响应/分页, 微信 code2session 登录,provider-neutral AI(DeepSeek),go:embed React 后台 - 修复:分段选择类型不匹配(字符串 vs 数字)导致选不中 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"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"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Port int `mapstructure:"port"`
|
||||
Mode string `mapstructure:"mode"`
|
||||
}
|
||||
|
||||
type MySQLConfig 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"`
|
||||
}
|
||||
|
||||
// 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,
|
||||
)
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type JWTConfig struct {
|
||||
Secret string `mapstructure:"secret"`
|
||||
ExpireHours int `mapstructure:"expire_hours"`
|
||||
}
|
||||
|
||||
type WeChatConfig struct {
|
||||
AppID string `mapstructure:"app_id"`
|
||||
AppSecret string `mapstructure:"app_secret"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
DevLogin bool `mapstructure:"dev_login"`
|
||||
}
|
||||
|
||||
type AdminConfig struct {
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
// Load 载入配置:configs/config.yaml + 环境变量覆盖(PETS_ 前缀,点转下划线)
|
||||
// 例如 PETS_MYSQL_PASSWORD 覆盖 mysql.password
|
||||
func Load() (*Config, error) {
|
||||
v := viper.New()
|
||||
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 {
|
||||
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 &cfg, nil
|
||||
}
|
||||
Reference in New Issue
Block a user