feat(*): go 后端项目脚手架

This commit is contained in:
2025-11-15 18:20:30 +08:00
commit 5bb025c1aa
39 changed files with 2459 additions and 0 deletions

130
pkg/config/config.go Normal file
View File

@@ -0,0 +1,130 @@
package config
import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"sync"
"github.com/go-yaml/yaml"
"github.com/spf13/viper"
"github.com/zhilv666/navsite/pkg/consts"
)
var (
globalViper *viper.Viper
globalViperOnce sync.Once
configMu sync.RWMutex
cachedConfig *Config
)
// NewConfig 初始化配置
func NewConfig() *Config {
globalViperOnce.Do(func() {
globalViper = viper.New()
})
configPath := loadConfigFile()
if configPath == "" {
log.Println("⚠️ 未找到配置文件,使用默认配置并导出为 config.yaml")
cachedConfig = DefaultConfig()
exportConfig(cachedConfig, "yaml")
return cachedConfig
}
conf, err := parseConfig()
if err != nil {
log.Printf("⚠️ 解析配置失败,使用默认配置并导出为 config.yaml: %v", err)
conf = DefaultConfig()
exportConfig(conf, "yaml")
}
cachedConfig = conf
return conf
}
// loadConfigFile 加载配置文件
func loadConfigFile() string {
files := []string{
fmt.Sprintf("%s.yaml", consts.ConfigName),
fmt.Sprintf("%s.yml", consts.ConfigName),
fmt.Sprintf("%s.json", consts.ConfigName),
}
for _, file := range files {
if _, err := os.Stat(file); err == nil {
ext := filepath.Ext(file)[1:]
globalViper.SetConfigFile(file)
globalViper.SetConfigType(ext)
globalViper.AddConfigPath(".")
if err := globalViper.ReadInConfig(); err != nil {
log.Printf("⚠️ 读取配置文件 %s 失败: %v", file, err)
continue
}
log.Printf("✅ 成功加载配置文件: %s", file)
return file
}
}
return ""
}
// parseConfig 将配置解析为结构体
func parseConfig() (*Config, error) {
configMu.Lock()
defer configMu.Unlock()
var conf Config
if err := globalViper.Unmarshal(&conf); err != nil {
return nil, err
}
return &conf, nil
}
// exportConfig 导出配置文件(默认保存为 config.yaml
func exportConfig(conf *Config, format string) {
var (
data []byte
err error
)
switch format {
case "json":
data, err = json.MarshalIndent(conf, "", " ")
case "yaml", "yml", "":
format = "yaml"
data, err = yaml.Marshal(conf)
default:
log.Printf("⚠️ 不支持的导出格式: %s", format)
return
}
if err != nil {
log.Printf("⚠️ 导出 %s 格式配置失败: %v", format, err)
return
}
filename := fmt.Sprintf("%s.%s", consts.ConfigName, format)
if err := os.WriteFile(filename, data, 0644); err != nil {
log.Printf("⚠️ 写入配置文件 %s 失败: %v", filename, err)
} else {
log.Printf("✅ 已导出配置文件: %s", filename)
}
log.Printf("📋 当前 %s 配置:\n%s", format, data)
}
// GetConfig 获取当前配置(线程安全)
func GetConfig() *Config {
configMu.RLock()
defer configMu.RUnlock()
if cachedConfig == nil {
return DefaultConfig()
}
conf := *cachedConfig
return &conf
}

76
pkg/config/types.go Normal file
View File

@@ -0,0 +1,76 @@
package config
import (
"path/filepath"
"time"
"github.com/zhilv666/navsite/pkg/common"
)
type Server struct {
Debug bool `mapstructure:"debug"`
Port int `mapstructure:"port"`
}
type Log struct {
Level string `mapstructure:"level"`
Filepath string `mapstructure:"filepath"`
MaxSizeMB int `mapstructure:"max_size_mb"` // 单个日志文件最大(MB)
MaxAgeDay int `mapstructure:"max_age_day"` // 日志文件最大保存天数
Backups int `mapstructure:"backups"` // 保留的旧文件个数
Compress bool `mapstructure:"compress"` // 是否压缩
}
type Database struct {
Driver string `mapstructure:"driver"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
DbName string `mapstructure:"db_name"`
SqliteDbPath string `mapstructure:"sqlite_db_path"`
}
type JWT struct {
SecretKey string `mapstructure:"secret_key"`
ExpireDurationHour time.Duration `mapstructure:"expire_duration_hour"`
}
type Config struct {
Server Server `mapstructure:"server"`
Log Log `mapstructure:"log"`
Database Database `mapstructure:"database"`
JWT JWT `mapstructure:"jwt"`
}
func DefaultConfig() *Config {
logPath := filepath.Join("data", "log.log")
dbPath := filepath.Join("data", "sqlite.db")
return &Config{
Server: Server{
Debug: true,
Port: 8080,
},
Log: Log{
Level: "debug",
Filepath: logPath,
MaxSizeMB: 10,
MaxAgeDay: 7,
Backups: 3,
Compress: true,
},
Database: Database{
Driver: "sqlite",
User: "",
Password: "",
Host: "",
Port: 0,
DbName: "",
SqliteDbPath: dbPath,
},
JWT: JWT{
SecretKey: common.Rand(16),
ExpireDurationHour: 24,
},
}
}