first commit

This commit is contained in:
2026-10-25 20:27:59 +08:00
commit 332b7dcd08
27 changed files with 1708 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
// Package config 负责加载与解析应用配置。
// 优先级:默认值 < 配置文件 < 环境变量。
package config
import (
"fmt"
"os"
"strings"
"github.com/spf13/viper"
)
// Config 是应用的顶层配置。
type Config struct {
Server ServerConfig `mapstructure:"server"`
MySQL MySQLConfig `mapstructure:"mysql"`
Logging LoggingConfig `mapstructure:"logging"`
Admin AdminConfig `mapstructure:"admin"`
}
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"`
ParseTime string `mapstructure:"parse_time"`
Loc string `mapstructure:"loc"`
// 连接池参数。
MaxIdleConns int `mapstructure:"max_idle_conns"`
MaxOpenConns int `mapstructure:"max_open_conns"`
ConnMaxLifetime int `mapstructure:"conn_max_lifetime"` // 单位:秒
}
// DSN 根据各独立字段拼装 MySQL 连接串。
// 优先级:显式字段 < 环境变量;缺省项使用合理默认值。
func (d MySQLConfig) DSN() string {
charset := d.Charset
if charset == "" {
charset = "utf8mb4"
}
parseTime := d.ParseTime
if parseTime == "" {
parseTime = "True"
}
loc := d.Loc
if loc == "" {
loc = "Local"
}
return fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%s&loc=%s",
d.User, d.Password, d.Host, d.Port, d.Database, charset, parseTime, loc,
)
}
type LoggingConfig struct {
Level string `mapstructure:"level"`
Format string `mapstructure:"format"`
}
// AdminConfig 后台管理相关配置。
type AdminConfig struct {
JWTSecret string `mapstructure:"jwt_secret"`
TokenTTL string `mapstructure:"token_ttl"`
}
// Load 从 configPath 加载配置(为空则默认 configs/config.yaml)。
// 配置文件中使用 ${ENV} 占位符的字段会被对应环境变量替换,
// 敏感信息(dsn/secret/token)据此注入,禁止硬编码。
func Load(configPath string) (*Config, error) {
if configPath == "" {
configPath = "configs/config.yaml"
}
raw, err := os.ReadFile(configPath)
if err != nil {
return nil, fmt.Errorf("read config %s: %w", configPath, err)
}
// 先展开 ${ENV} 占位符,再交给 viper 解析。
expanded := os.ExpandEnv(string(raw))
v := viper.New()
v.SetConfigType("yaml")
if err := v.ReadConfig(strings.NewReader(expanded)); err != nil {
return nil, fmt.Errorf("parse config: %w", err)
}
var cfg Config
if err := v.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("unmarshal config: %w", err)
}
return &cfg, nil
}