|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+package config
|
|
|
2
|
+
|
|
|
3
|
+import (
|
|
|
4
|
+ "fmt"
|
|
|
5
|
+ "os"
|
|
|
6
|
+ "path/filepath"
|
|
|
7
|
+ "sync"
|
|
|
8
|
+
|
|
|
9
|
+ "gopkg.in/yaml.v2"
|
|
|
10
|
+)
|
|
|
11
|
+
|
|
|
12
|
+// Config 应用配置
|
|
|
13
|
+type Config struct {
|
|
|
14
|
+ Database DBConfig `yaml:"database"`
|
|
|
15
|
+ Auth Auth `yaml:"auth"`
|
|
|
16
|
+ Service Service `yaml:"service"`
|
|
|
17
|
+}
|
|
|
18
|
+
|
|
|
19
|
+// DBConfig 数据库配置
|
|
|
20
|
+type DBConfig struct {
|
|
|
21
|
+ Type string `yaml:"type"`
|
|
|
22
|
+ Host string `yaml:"host"`
|
|
|
23
|
+ Port int `yaml:"port"`
|
|
|
24
|
+ Username string `yaml:"username"`
|
|
|
25
|
+ Password string `yaml:"password"`
|
|
|
26
|
+ Database string `yaml:"database"`
|
|
|
27
|
+ MaxOpenConns int `yaml:"max_open_conns"`
|
|
|
28
|
+ MaxIdleConns int `yaml:"max_idle_conns"`
|
|
|
29
|
+ ConnMaxLifetime int `yaml:"conn_max_lifetime"` // 单位:秒
|
|
|
30
|
+}
|
|
|
31
|
+
|
|
|
32
|
+// Auth 认证配置
|
|
|
33
|
+type Auth struct {
|
|
|
34
|
+ Token string `yaml:"token"`
|
|
|
35
|
+}
|
|
|
36
|
+
|
|
|
37
|
+// Service 微服务配置
|
|
|
38
|
+type Service struct {
|
|
|
39
|
+ ReadTimeout int `yaml:"read_timeout"`
|
|
|
40
|
+ WriteTimeout int `yaml:"write_timeout"`
|
|
|
41
|
+ IdleTimeout int `yaml:"idle_timeout"`
|
|
|
42
|
+}
|
|
|
43
|
+
|
|
|
44
|
+// 导出接口,防止外部修改
|
|
|
45
|
+type IConfig interface {
|
|
|
46
|
+ GetDatabase() DBConfig
|
|
|
47
|
+ GetAuth() Auth
|
|
|
48
|
+ GetService() Service
|
|
|
49
|
+ IsDatabaseConfigured() bool
|
|
|
50
|
+}
|
|
|
51
|
+
|
|
|
52
|
+// 实现接口的具体类型
|
|
|
53
|
+type configWrapper struct {
|
|
|
54
|
+ config *Config
|
|
|
55
|
+}
|
|
|
56
|
+
|
|
|
57
|
+func (cw *configWrapper) GetDatabase() DBConfig {
|
|
|
58
|
+ return cw.config.Database
|
|
|
59
|
+}
|
|
|
60
|
+
|
|
|
61
|
+func (cw *configWrapper) GetAuth() Auth {
|
|
|
62
|
+ return cw.config.Auth
|
|
|
63
|
+}
|
|
|
64
|
+
|
|
|
65
|
+func (cw *configWrapper) GetService() Service {
|
|
|
66
|
+ return cw.config.Service
|
|
|
67
|
+}
|
|
|
68
|
+
|
|
|
69
|
+func (cw *configWrapper) IsDatabaseConfigured() bool {
|
|
|
70
|
+ return cw.config.Database.Type != "" &&
|
|
|
71
|
+ cw.config.Database.Host != "" &&
|
|
|
72
|
+ cw.config.Database.Port > 0 &&
|
|
|
73
|
+ cw.config.Database.Username != "" &&
|
|
|
74
|
+ cw.config.Database.Database != ""
|
|
|
75
|
+}
|
|
|
76
|
+
|
|
|
77
|
+func (cw *configWrapper) IsAuthConfigured() bool {
|
|
|
78
|
+ return cw.config.Auth.Token != ""
|
|
|
79
|
+}
|
|
|
80
|
+
|
|
|
81
|
+func (cw *configWrapper) IsServiceConfigured() bool {
|
|
|
82
|
+ return cw.config.Service.ReadTimeout > 0 &&
|
|
|
83
|
+ cw.config.Service.WriteTimeout > 0 &&
|
|
|
84
|
+ cw.config.Service.IdleTimeout > 0
|
|
|
85
|
+
|
|
|
86
|
+}
|
|
|
87
|
+
|
|
|
88
|
+var (
|
|
|
89
|
+ instance IConfig
|
|
|
90
|
+ once sync.Once
|
|
|
91
|
+ initErr error
|
|
|
92
|
+)
|
|
|
93
|
+
|
|
|
94
|
+// GetConfig 获取配置单例实例
|
|
|
95
|
+func GetConfig() IConfig {
|
|
|
96
|
+ once.Do(func() {
|
|
|
97
|
+ config, err := loadConfig()
|
|
|
98
|
+ if err != nil {
|
|
|
99
|
+ initErr = err
|
|
|
100
|
+ // 创建一个空的配置实例,避免nil指针
|
|
|
101
|
+ instance = &configWrapper{config: &Config{}}
|
|
|
102
|
+ return
|
|
|
103
|
+ }
|
|
|
104
|
+ instance = &configWrapper{config: config}
|
|
|
105
|
+ })
|
|
|
106
|
+ return instance
|
|
|
107
|
+}
|
|
|
108
|
+
|
|
|
109
|
+// GetInitError 获取初始化错误(如果有)
|
|
|
110
|
+func GetInitError() error {
|
|
|
111
|
+ return initErr
|
|
|
112
|
+}
|
|
|
113
|
+
|
|
|
114
|
+// loadConfig 加载配置文件
|
|
|
115
|
+func loadConfig() (*Config, error) {
|
|
|
116
|
+ configFile, err := findConfigFile()
|
|
|
117
|
+ if err != nil {
|
|
|
118
|
+ return nil, err
|
|
|
119
|
+ }
|
|
|
120
|
+
|
|
|
121
|
+ fmt.Printf("✅ Using config file: %s\n", configFile)
|
|
|
122
|
+
|
|
|
123
|
+ // 读取配置文件
|
|
|
124
|
+ data, err := os.ReadFile(configFile)
|
|
|
125
|
+ if err != nil {
|
|
|
126
|
+ return nil, fmt.Errorf("failed to read config file %s: %v", configFile, err)
|
|
|
127
|
+ }
|
|
|
128
|
+
|
|
|
129
|
+ var config Config
|
|
|
130
|
+ err = yaml.Unmarshal(data, &config)
|
|
|
131
|
+ if err != nil {
|
|
|
132
|
+ return nil, fmt.Errorf("failed to parse config file: %v", err)
|
|
|
133
|
+ }
|
|
|
134
|
+
|
|
|
135
|
+ return &config, nil
|
|
|
136
|
+}
|
|
|
137
|
+
|
|
|
138
|
+// findConfigFile 查找配置文件
|
|
|
139
|
+func findConfigFile() (string, error) {
|
|
|
140
|
+ // 1. 首先尝试可执行文件同目录
|
|
|
141
|
+ exePath, err := os.Executable()
|
|
|
142
|
+ if err == nil {
|
|
|
143
|
+ exeDir := filepath.Dir(exePath)
|
|
|
144
|
+ configFile := filepath.Join(exeDir, "db.yaml")
|
|
|
145
|
+ if _, err := os.Stat(configFile); err == nil {
|
|
|
146
|
+ return configFile, nil
|
|
|
147
|
+ }
|
|
|
148
|
+ }
|
|
|
149
|
+
|
|
|
150
|
+ // 2. 尝试环境变量指定的路径
|
|
|
151
|
+ envConfigPath := os.Getenv("DB_CONFIG_PATH")
|
|
|
152
|
+ if envConfigPath != "" {
|
|
|
153
|
+ if _, err := os.Stat(envConfigPath); err == nil {
|
|
|
154
|
+ return envConfigPath, nil
|
|
|
155
|
+ }
|
|
|
156
|
+ return "", fmt.Errorf("DB_CONFIG_PATH file not found: %s", envConfigPath)
|
|
|
157
|
+ }
|
|
|
158
|
+
|
|
|
159
|
+ // 3. 如果都没有找到,返回错误
|
|
|
160
|
+ exeDir := "unknown"
|
|
|
161
|
+ if exePath, err := os.Executable(); err == nil {
|
|
|
162
|
+ exeDir = filepath.Dir(exePath)
|
|
|
163
|
+ }
|
|
|
164
|
+
|
|
|
165
|
+ return "", fmt.Errorf(`No configuration file found!
|
|
|
166
|
+
|
|
|
167
|
+Tried locations:
|
|
|
168
|
+1. Executable directory: %s/db.yaml
|
|
|
169
|
+2. Environment variable: DB_CONFIG_PATH
|
|
|
170
|
+
|
|
|
171
|
+Solutions:
|
|
|
172
|
+- Place db.yaml in the same directory as the executable
|
|
|
173
|
+- Or set DB_CONFIG_PATH environment variable to config file path
|
|
|
174
|
+
|
|
|
175
|
+Example:
|
|
|
176
|
+ export DB_CONFIG_PATH=/path/to/your/db.yaml`, exeDir)
|
|
|
177
|
+}
|