duidui_fiber/config/config.go
2026-03-27 10:34:03 +08:00

256 lines
8.7 KiB
Go
Raw Permalink 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"
"path/filepath"
"strings"
"github.com/spf13/viper"
)
// Config 应用配置
type Config struct {
Service ServiceConfig `mapstructure:"service"`
Redis RedisConfig `mapstructure:"redis"`
MySQL MySQLConfig `mapstructure:"mysql"`
MongoDB MongoDBConfig `mapstructure:"mongodb"`
OSS OSSConfig `mapstructure:"oss"`
Wechat WechatConfig `mapstructure:"wechat"`
Scheduler SchedulerConfig `mapstructure:"scheduler"`
Admin AdminConfig `mapstructure:"admin"`
}
// ServiceConfig 服务配置
type ServiceConfig struct {
Name string `mapstructure:"name"`
Version string `mapstructure:"version"`
Host string `mapstructure:"host"`
AdminPort int `mapstructure:"admin_port"`
APIPort int `mapstructure:"api_port"`
}
// RedisConfig Redis配置
type RedisConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
PoolSize int `mapstructure:"pool_size"`
MinIdleConns int `mapstructure:"min_idle_conns"`
}
// OSSConfig 阿里云OSS配置
type OSSConfig struct {
AccessKeyID string `mapstructure:"accessKeyId"`
AccessKeySecret string `mapstructure:"accessKeySecret"`
RoleARN string `mapstructure:"roleArn"`
RoleSessionName string `mapstructure:"roleSessionName"`
Region string `mapstructure:"region"`
BucketName string `mapstructure:"bucketName"`
}
// WechatConfig 微信支付配置
type WechatConfig struct {
AppID string `mapstructure:"app_id"` // 微信小程序或公众号 APPID
MchID string `mapstructure:"mch_id"` // 商户号
NotifyURL string `mapstructure:"notify_url"` // 支付结果通知地址
APIKeyV3 string `mapstructure:"api_key_v3"` // API v3密钥
SerialNo string `mapstructure:"serial_no"` // 证书序列号
PrivateKey string `mapstructure:"private_key"` // 私钥内容PEM格式
PrivateKeyPath string `mapstructure:"private_key_path"` // 私钥文件路径
CertPath string `mapstructure:"cert_path"` // 证书文件路径(可选)
}
// MySQLConfig MySQL配置
type MySQLConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Database string `mapstructure:"database"`
Charset string `mapstructure:"charset"`
MaxOpenConns int `mapstructure:"max_open_conns"`
MaxIdleConns int `mapstructure:"max_idle_conns"`
ConnMaxLifetime string `mapstructure:"conn_max_lifetime"` // 如 "3600s"
}
// MongoDBConfig MongoDB配置
type MongoDBConfig struct {
URI string `mapstructure:"uri"` // 连接字符串,如: mongodb://admin:password@localhost:27017
Database string `mapstructure:"database"` // 数据库名
Timeout string `mapstructure:"timeout"` // 连接超时,如 "10s"
}
// SchedulerConfig 调度器配置
type SchedulerConfig struct {
TickInterval string `mapstructure:"tick_interval"` // 时间轮刻度间隔,如 "100ms"
SlotNum int `mapstructure:"slot_num"` // 槽位数量
}
// AdminConfig 管理员配置
type AdminConfig struct {
JWTSecret string `mapstructure:"jwt_secret"` // JWT密钥
JWTExpiresIn string `mapstructure:"jwt_expires_in"` // JWT过期时间如 "24h"
}
// LoadConfig 加载配置文件
// 支持从多个位置查找配置文件(优先级从高到低):
// 1. 命令行参数指定的路径
// 2. /app/data/config/config.yaml容器内挂载路径
// 3. ./data/config/config.yaml开发环境
// 4. ./config.yaml项目根目录兼容旧配置
func LoadConfig(configFile string) (*Config, error) {
v := viper.New()
v.SetConfigType("yaml")
// 如果指定了配置文件且文件存在,直接使用
if configFile != "" && configFile != "config.yaml" {
if _, err := os.Stat(configFile); err == nil {
v.SetConfigFile(configFile)
}
}
// 如果还没有设置配置文件,尝试从多个位置查找
if v.ConfigFileUsed() == "" {
configPaths := []string{
"/app/data/config/config.yaml", // 容器内挂载路径
"./data/config/config.yaml", // 开发环境
"./config.yaml", // 项目根目录(兼容旧配置)
}
for _, path := range configPaths {
if _, err := os.Stat(path); err == nil {
v.SetConfigFile(path)
break
}
}
}
// 如果仍然没有找到配置文件,使用默认路径
if v.ConfigFileUsed() == "" {
if configFile != "" {
v.SetConfigFile(configFile)
} else {
v.SetConfigFile("config.yaml")
}
}
// 设置默认值
setDefaults(v)
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("读取配置文件失败: %w (尝试的路径: %s)", err, v.ConfigFileUsed())
}
var config Config
if err := v.Unmarshal(&config); err != nil {
return nil, fmt.Errorf("解析配置文件失败: %w", err)
}
// 处理证书路径:如果是绝对路径,直接验证;如果是相对路径,尝试从多个位置查找
if config.Wechat.PrivateKeyPath != "" {
if filepath.IsAbs(config.Wechat.PrivateKeyPath) {
// 绝对路径:直接验证文件是否存在
if _, err := os.Stat(config.Wechat.PrivateKeyPath); err != nil {
// 如果绝对路径不存在,尝试从其他位置查找
// 提取相对路径部分(去掉 /app/data/storage/ 或 storage/ 前缀)
relPath := config.Wechat.PrivateKeyPath
if strings.HasPrefix(relPath, "/app/data/storage/") {
relPath = strings.TrimPrefix(relPath, "/app/data/storage/")
} else if strings.HasPrefix(relPath, "storage/") {
relPath = strings.TrimPrefix(relPath, "storage/")
}
// 尝试从多个位置查找
certBasePaths := []string{
"/app/data/storage", // 容器内挂载路径
"./deploy/storage", // 部署目录
"./data/storage", // 开发环境
"./storage", // 项目根目录(兼容旧配置)
}
for _, basePath := range certBasePaths {
fullPath := filepath.Join(basePath, relPath)
if _, err := os.Stat(fullPath); err == nil {
config.Wechat.PrivateKeyPath = fullPath
if config.Wechat.CertPath != "" {
certRelPath := config.Wechat.CertPath
if strings.HasPrefix(certRelPath, "/app/data/storage/") {
certRelPath = strings.TrimPrefix(certRelPath, "/app/data/storage/")
} else if strings.HasPrefix(certRelPath, "storage/") {
certRelPath = strings.TrimPrefix(certRelPath, "storage/")
}
config.Wechat.CertPath = filepath.Join(basePath, certRelPath)
}
break
}
}
}
} else {
// 相对路径:尝试从多个位置查找
certBasePaths := []string{
"/app/data/storage", // 容器内挂载路径
"./deploy/storage", // 部署目录
"./data/storage", // 开发环境
"./storage", // 项目根目录(兼容旧配置)
}
// 提取相对路径部分(去掉 storage/ 前缀)
relPath := config.Wechat.PrivateKeyPath
if strings.HasPrefix(relPath, "storage/") {
relPath = strings.TrimPrefix(relPath, "storage/")
}
for _, basePath := range certBasePaths {
fullPath := filepath.Join(basePath, relPath)
if _, err := os.Stat(fullPath); err == nil {
config.Wechat.PrivateKeyPath = fullPath
if config.Wechat.CertPath != "" {
certRelPath := config.Wechat.CertPath
if strings.HasPrefix(certRelPath, "storage/") {
certRelPath = strings.TrimPrefix(certRelPath, "storage/")
}
config.Wechat.CertPath = filepath.Join(basePath, certRelPath)
}
break
}
}
}
}
return &config, nil
}
// setDefaults 设置默认配置值
func setDefaults(v *viper.Viper) {
v.SetDefault("service.name", "dd_fiber_api")
v.SetDefault("service.version", "1.0.0")
v.SetDefault("service.host", "0.0.0.0")
v.SetDefault("service.admin_port", 8080)
v.SetDefault("service.api_port", 8081)
v.SetDefault("redis.host", "localhost")
v.SetDefault("redis.port", 6379)
v.SetDefault("redis.password", "")
v.SetDefault("redis.db", 0)
v.SetDefault("redis.pool_size", 10)
v.SetDefault("redis.min_idle_conns", 5)
v.SetDefault("mysql.host", "localhost")
v.SetDefault("mysql.port", 3306)
v.SetDefault("mysql.username", "root")
v.SetDefault("mysql.password", "")
v.SetDefault("mysql.database", "")
v.SetDefault("mysql.charset", "utf8mb4")
v.SetDefault("mysql.max_open_conns", 100)
v.SetDefault("mysql.max_idle_conns", 10)
v.SetDefault("mysql.conn_max_lifetime", "3600s")
v.SetDefault("scheduler.tick_interval", "100ms")
v.SetDefault("scheduler.slot_num", 3600)
v.SetDefault("admin.jwt_secret", "your-secret-key-change-in-production")
v.SetDefault("admin.jwt_expires_in", "24h")
}