temp
This commit is contained in:
307
internal/config/README.md
Normal file
307
internal/config/README.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# 🔧 TYAPI 配置系统文档
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [配置策略概述](#配置策略概述)
|
||||
- [文件结构](#文件结构)
|
||||
- [配置加载流程](#配置加载流程)
|
||||
- [环境配置](#环境配置)
|
||||
- [配置验证](#配置验证)
|
||||
- [使用指南](#使用指南)
|
||||
- [最佳实践](#最佳实践)
|
||||
- [故障排除](#故障排除)
|
||||
|
||||
## 🎯 配置策略概述
|
||||
|
||||
TYAPI 采用**分层配置策略**,支持多环境部署和灵活的配置管理:
|
||||
|
||||
```
|
||||
📁 配置层次结构
|
||||
├── 📄 config.yaml (基础配置模板)
|
||||
└── 📁 configs/
|
||||
├── 📄 env.development.yaml (开发环境覆盖)
|
||||
├── 📄 env.production.yaml (生产环境覆盖)
|
||||
└── 📄 env.testing.yaml (测试环境覆盖)
|
||||
```
|
||||
|
||||
### 配置加载优先级(从高到低)
|
||||
|
||||
1. **环境变量** - 用于敏感信息和运行时覆盖
|
||||
2. **环境特定配置文件** - `configs/env.{environment}.yaml`
|
||||
3. **基础配置文件** - `config.yaml`
|
||||
4. **默认值** - 代码中的默认配置
|
||||
|
||||
## 📁 文件结构
|
||||
|
||||
### 基础配置文件
|
||||
|
||||
- **位置**: `config.yaml`
|
||||
- **作用**: 包含所有默认配置值,作为配置模板
|
||||
- **特点**:
|
||||
- 包含完整的配置结构
|
||||
- 提供合理的默认值
|
||||
- 作为所有环境的基础配置
|
||||
|
||||
### 环境配置文件
|
||||
|
||||
- **位置**: `configs/env.{environment}.yaml`
|
||||
- **支持的环境**: `development`, `production`, `testing`
|
||||
- **特点**:
|
||||
- 只包含需要覆盖的配置项
|
||||
- 继承基础配置的所有默认值
|
||||
- 支持嵌套配置的深度合并
|
||||
|
||||
## 🔄 配置加载流程
|
||||
|
||||
### 1. 环境检测
|
||||
|
||||
```go
|
||||
// 环境变量检测优先级
|
||||
CONFIG_ENV > ENV > APP_ENV > 默认值(development)
|
||||
```
|
||||
|
||||
### 2. 配置文件加载顺序
|
||||
|
||||
1. 读取基础配置文件 `config.yaml`
|
||||
2. 查找环境配置文件 `configs/env.{environment}.yaml`
|
||||
3. 合并环境配置到基础配置
|
||||
4. 应用环境变量覆盖
|
||||
5. 验证配置完整性
|
||||
6. 输出配置摘要
|
||||
|
||||
### 3. 配置合并策略
|
||||
|
||||
- **递归合并**: 支持嵌套配置的深度合并
|
||||
- **覆盖机制**: 环境配置覆盖基础配置
|
||||
- **环境变量**: 最终覆盖任何配置项
|
||||
|
||||
## 🌍 环境配置
|
||||
|
||||
### 开发环境 (development)
|
||||
|
||||
```yaml
|
||||
# configs/env.development.yaml
|
||||
app:
|
||||
env: development
|
||||
|
||||
database:
|
||||
password: Pg9mX4kL8nW2rT5y
|
||||
|
||||
jwt:
|
||||
secret: JwT8xR4mN9vP2sL7kH3oB6yC1zA5uF0qE9tW
|
||||
```
|
||||
|
||||
### 生产环境 (production)
|
||||
|
||||
```yaml
|
||||
# configs/env.production.yaml
|
||||
app:
|
||||
env: production
|
||||
|
||||
server:
|
||||
mode: release
|
||||
|
||||
database:
|
||||
sslmode: require
|
||||
|
||||
logger:
|
||||
level: warn
|
||||
format: json
|
||||
```
|
||||
|
||||
### 测试环境 (testing)
|
||||
|
||||
```yaml
|
||||
# configs/env.testing.yaml
|
||||
app:
|
||||
env: testing
|
||||
|
||||
server:
|
||||
mode: test
|
||||
|
||||
database:
|
||||
password: test_password
|
||||
name: tyapi_test
|
||||
|
||||
redis:
|
||||
db: 15
|
||||
|
||||
logger:
|
||||
level: debug
|
||||
|
||||
jwt:
|
||||
secret: test-jwt-secret-key-for-testing-only
|
||||
```
|
||||
|
||||
## ✅ 配置验证
|
||||
|
||||
### 验证项目
|
||||
|
||||
- **数据库配置**: 主机、用户名、数据库名不能为空
|
||||
- **JWT 配置**: 生产环境必须设置安全的 JWT 密钥
|
||||
- **服务器配置**: 超时时间必须大于 0
|
||||
- **连接池配置**: 最大空闲连接数不能大于最大连接数
|
||||
|
||||
### 验证失败处理
|
||||
|
||||
- 配置验证失败时,应用无法启动
|
||||
- 提供详细的中文错误信息
|
||||
- 帮助快速定位配置问题
|
||||
|
||||
## 📖 使用指南
|
||||
|
||||
### 1. 启动应用
|
||||
|
||||
```bash
|
||||
# 使用默认环境 (development)
|
||||
go run cmd/api/main.go
|
||||
|
||||
# 指定环境
|
||||
CONFIG_ENV=production go run cmd/api/main.go
|
||||
ENV=testing go run cmd/api/main.go
|
||||
APP_ENV=production go run cmd/api/main.go
|
||||
```
|
||||
|
||||
### 2. 添加新的配置项
|
||||
|
||||
1. 在 `config.yaml` 中添加默认值
|
||||
2. 在 `internal/config/config.go` 中定义对应的结构体字段
|
||||
3. 在环境配置文件中覆盖特定值(如需要)
|
||||
|
||||
### 3. 环境变量覆盖
|
||||
|
||||
```bash
|
||||
# 覆盖数据库密码
|
||||
export DATABASE_PASSWORD="your-secure-password"
|
||||
|
||||
# 覆盖JWT密钥
|
||||
export JWT_SECRET="your-super-secret-jwt-key"
|
||||
|
||||
# 覆盖服务器端口
|
||||
export SERVER_PORT="9090"
|
||||
```
|
||||
|
||||
### 4. 添加新的环境
|
||||
|
||||
1. 创建 `configs/env.{new_env}.yaml` 文件
|
||||
2. 在 `getEnvironment()` 函数中添加环境验证
|
||||
3. 配置相应的环境特定设置
|
||||
|
||||
## 🏆 最佳实践
|
||||
|
||||
### 1. 配置文件管理
|
||||
|
||||
- ✅ **基础配置**: 在 `config.yaml` 中设置合理的默认值
|
||||
- ✅ **环境配置**: 只在环境文件中覆盖必要的配置项
|
||||
- ✅ **敏感信息**: 通过环境变量注入,不要写在配置文件中
|
||||
- ✅ **版本控制**: 将配置文件纳入版本控制,但排除敏感信息
|
||||
|
||||
### 2. 环境变量使用
|
||||
|
||||
- ✅ **生产环境**: 所有敏感信息都通过环境变量注入
|
||||
- ✅ **开发环境**: 可以使用配置文件中的默认值
|
||||
- ✅ **测试环境**: 使用独立的测试配置
|
||||
|
||||
### 3. 配置验证
|
||||
|
||||
- ✅ **启动验证**: 应用启动时验证所有必要配置
|
||||
- ✅ **类型检查**: 确保配置值的类型正确
|
||||
- ✅ **逻辑验证**: 验证配置项之间的逻辑关系
|
||||
|
||||
### 4. 日志和监控
|
||||
|
||||
- ✅ **配置摘要**: 启动时输出关键配置信息
|
||||
- ✅ **环境标识**: 明确显示当前运行环境
|
||||
- ✅ **配置变更**: 记录重要的配置变更
|
||||
|
||||
## 🔧 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
#### 1. 配置文件未找到
|
||||
|
||||
```
|
||||
❌ 错误: 未找到 config.yaml 文件,请确保配置文件存在
|
||||
```
|
||||
|
||||
**解决方案**: 确保项目根目录下存在 `config.yaml` 文件
|
||||
|
||||
#### 2. 环境配置文件未找到
|
||||
|
||||
```
|
||||
ℹ️ 未找到环境配置文件 configs/env.development.yaml,将使用基础配置
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
|
||||
- 检查环境变量设置是否正确
|
||||
- 确认 `configs/env.{environment}.yaml` 文件存在
|
||||
|
||||
#### 3. 配置验证失败
|
||||
|
||||
```
|
||||
❌ 错误: 配置验证失败: 数据库主机地址不能为空
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
|
||||
- 检查 `config.yaml` 中的数据库配置
|
||||
- 确认环境配置文件中的覆盖值正确
|
||||
|
||||
#### 4. JWT 密钥安全问题
|
||||
|
||||
```
|
||||
❌ 错误: 生产环境必须设置安全的JWT密钥
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
|
||||
- 通过环境变量设置安全的 JWT 密钥
|
||||
- 不要使用默认的测试密钥
|
||||
|
||||
### 调试技巧
|
||||
|
||||
#### 1. 查看配置摘要
|
||||
|
||||
启动时查看配置摘要输出,确认:
|
||||
|
||||
- 当前运行环境
|
||||
- 使用的配置文件
|
||||
- 关键配置值
|
||||
|
||||
#### 2. 环境变量检查
|
||||
|
||||
```bash
|
||||
# 检查环境变量
|
||||
echo $CONFIG_ENV
|
||||
echo $ENV
|
||||
echo $APP_ENV
|
||||
```
|
||||
|
||||
#### 3. 配置文件语法检查
|
||||
|
||||
```bash
|
||||
# 检查YAML语法
|
||||
yamllint config.yaml
|
||||
yamllint configs/env.development.yaml
|
||||
```
|
||||
|
||||
## 📚 相关文件
|
||||
|
||||
- `internal/config/config.go` - 配置结构体定义
|
||||
- `internal/config/loader.go` - 配置加载逻辑
|
||||
- `config.yaml` - 基础配置文件
|
||||
- `configs/env.*.yaml` - 环境特定配置文件
|
||||
|
||||
## 🔄 更新日志
|
||||
|
||||
### v1.0.0
|
||||
|
||||
- 实现基础的分层配置策略
|
||||
- 支持多环境配置
|
||||
- 添加配置验证机制
|
||||
- 实现环境变量覆盖功能
|
||||
|
||||
---
|
||||
|
||||
**注意**: 本配置系统遵循中文规范,所有面向用户的错误信息和日志都使用中文。
|
||||
@@ -19,6 +19,7 @@ type Config struct {
|
||||
Resilience ResilienceConfig `mapstructure:"resilience"`
|
||||
Development DevelopmentConfig `mapstructure:"development"`
|
||||
App AppConfig `mapstructure:"app"`
|
||||
WechatWork WechatWorkConfig `mapstructure:"wechat_work"`
|
||||
}
|
||||
|
||||
// ServerConfig HTTP服务器配置
|
||||
@@ -187,3 +188,9 @@ func (a AppConfig) IsDevelopment() bool {
|
||||
func (a AppConfig) IsStaging() bool {
|
||||
return a.Env == "staging"
|
||||
}
|
||||
|
||||
// WechatWorkConfig 企业微信配置
|
||||
type WechatWorkConfig struct {
|
||||
WebhookURL string `mapstructure:"webhook_url"`
|
||||
Secret string `mapstructure:"secret"`
|
||||
}
|
||||
|
||||
@@ -51,13 +51,11 @@ func LoadConfig() (*Config, error) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("⚠️ 未找到环境配置文件 env.%s.yaml\n", env)
|
||||
fmt.Printf("ℹ️ 未找到环境配置文件 configs/env.%s.yaml,将使用基础配置\n", env)
|
||||
}
|
||||
|
||||
// 4️⃣ 设置环境变量前缀和自动读取
|
||||
baseConfig.SetEnvPrefix("")
|
||||
baseConfig.AutomaticEnv()
|
||||
baseConfig.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
// 4️⃣ 手动处理环境变量覆盖,避免空值覆盖配置文件
|
||||
// overrideWithEnvVars(baseConfig)
|
||||
|
||||
// 5️⃣ 解析配置到结构体
|
||||
var config Config
|
||||
@@ -99,19 +97,10 @@ func mergeConfigs(baseConfig *viper.Viper, overrideSettings map[string]interface
|
||||
|
||||
// findEnvConfigFile 查找环境特定的配置文件
|
||||
func findEnvConfigFile(env string) string {
|
||||
// 尝试查找的配置文件路径
|
||||
// 只查找 configs 目录下的环境配置文件
|
||||
possiblePaths := []string{
|
||||
fmt.Sprintf("configs/env.%s.yaml", env),
|
||||
fmt.Sprintf("configs/env.%s.yml", env),
|
||||
fmt.Sprintf("configs/env.%s", env),
|
||||
fmt.Sprintf("env.%s.yaml", env),
|
||||
fmt.Sprintf("env.%s.yml", env),
|
||||
fmt.Sprintf("env.%s", env),
|
||||
}
|
||||
|
||||
// 如果有自定义环境文件路径
|
||||
if customEnvFile := os.Getenv("ENV_FILE"); customEnvFile != "" {
|
||||
possiblePaths = append([]string{customEnvFile}, possiblePaths...)
|
||||
}
|
||||
|
||||
for _, path := range possiblePaths {
|
||||
@@ -165,7 +154,8 @@ func getEnvironment() string {
|
||||
func printConfigSummary(config *Config, env string) {
|
||||
fmt.Printf("\n🔧 配置摘要:\n")
|
||||
fmt.Printf(" 🌍 环境: %s\n", env)
|
||||
fmt.Printf(" 📄 配置模板: config.yaml\n")
|
||||
fmt.Printf(" 📄 基础配置: config.yaml\n")
|
||||
fmt.Printf(" 📁 环境配置: configs/env.%s.yaml\n", env)
|
||||
fmt.Printf(" 📱 应用名称: %s\n", config.App.Name)
|
||||
fmt.Printf(" 🔖 版本: %s\n", config.App.Version)
|
||||
fmt.Printf(" 🌐 服务端口: %s\n", config.Server.Port)
|
||||
@@ -244,6 +234,26 @@ func ParseDuration(s string) time.Duration {
|
||||
return d
|
||||
}
|
||||
|
||||
// overrideWithEnvVars 手动处理环境变量覆盖,避免空值覆盖配置文件
|
||||
func overrideWithEnvVars(config *viper.Viper) {
|
||||
// 定义需要环境变量覆盖的敏感配置项
|
||||
sensitiveConfigs := map[string]string{
|
||||
"database.password": "DATABASE_PASSWORD",
|
||||
"jwt.secret": "JWT_SECRET",
|
||||
"redis.password": "REDIS_PASSWORD",
|
||||
"wechat_work.webhook_url": "WECHAT_WORK_WEBHOOK_URL",
|
||||
"wechat_work.secret": "WECHAT_WORK_SECRET",
|
||||
}
|
||||
|
||||
// 只覆盖明确设置的环境变量
|
||||
for configKey, envKey := range sensitiveConfigs {
|
||||
if envValue := os.Getenv(envKey); envValue != "" {
|
||||
config.Set(configKey, envValue)
|
||||
fmt.Printf("🔐 已从环境变量覆盖配置: %s\n", configKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SplitAndTrim 分割字符串并去除空格
|
||||
func SplitAndTrim(s, sep string) []string {
|
||||
parts := strings.Split(s, sep)
|
||||
|
||||
Reference in New Issue
Block a user