将工厂改成黑盒模式,降低用户使用成本

This commit is contained in:
2025-11-30 15:54:27 +08:00
parent 6323b49517
commit d454d8e143
18 changed files with 2075 additions and 1046 deletions

View File

@@ -2,204 +2,249 @@
## 概述
工厂工具提供了从配置直接创建已初始化客户端对象的功能,避免调用方重复实现创建逻辑
工厂工具提供了从配置直接创建已初始化客户端对象的功能,并提供了黑盒模式的便捷方法,让调用方无需关心底层实现细节,大大降低业务复杂度
## 功能特性
- 从配置直接创建已初始化的客户端对象
- 统一的工厂接口
- 避免调用方重复实现创建逻辑
- **黑盒模式**:提供直接调用的方法,无需获取客户端对象
- **延迟初始化**:所有客户端在首次使用时才创建
- **自动选择**存储类型OSS/MinIO根据配置自动选择
- **统一接口**:所有操作通过工厂方法调用
- **向后兼容**:保留 `GetXXX()` 方法,需要时可获取对象
## 使用方法
### 1. 从配置文件直接创建工厂(推荐)
### 1. 创建工厂(推荐)
```go
import "git.toowon.com/jimmy/go-common/factory"
// 直接传入配置文件路径,自动加载配置并创建工厂
// 方式1直接从配置文件创建最推荐
fac, err := factory.NewFactoryFromFile("./config.json")
if err != nil {
log.Fatal(err)
}
// 直接获取已初始化的对象
db, _ := fac.GetDatabase() // 直接获取数据库对象
logger, _ := fac.GetLogger() // 直接获取日志对象
emailClient, _ := fac.GetEmailClient() // 直接获取邮件客户端
```
### 2. 从配置对象创建工厂
```go
import (
"git.toowon.com/jimmy/go-common/config"
"git.toowon.com/jimmy/go-common/factory"
)
// 加载配置
cfg, err := config.LoadFromFile("./config.json")
if err != nil {
log.Fatal(err)
}
// 创建工厂实例
// 方式2从配置对象创建
cfg, _ := config.LoadFromFile("./config.json")
fac := factory.NewFactory(cfg)
```
### 3. 获取数据库对象(已初始化,推荐)
### 2. 日志记录(黑盒模式,推荐)
```go
// 直接获取已初始化的数据库对象(*gorm.DB
// 简单日志
fac.LogDebug("调试信息: %s", "test")
fac.LogInfo("用户登录成功")
fac.LogWarn("警告信息")
fac.LogError("错误信息: %v", err)
// 带字段的日志
fac.LogInfof(map[string]interface{}{
"user_id": 123,
"ip": "192.168.1.1",
}, "用户登录成功")
fac.LogErrorf(map[string]interface{}{
"error_code": 1001,
}, "登录失败: %v", err)
```
### 3. 邮件发送(黑盒模式,推荐)
```go
// 简单邮件
err := fac.SendEmail(
[]string{"user@example.com"},
"验证码",
"您的验证码是123456",
)
// HTML邮件
err := fac.SendEmail(
[]string{"user@example.com"},
"验证码",
"纯文本内容",
"<h1>HTML内容</h1>",
)
```
### 4. 短信发送(黑盒模式,推荐)
```go
// 使用配置中的模板代码
resp, err := fac.SendSMS(
[]string{"13800138000"},
map[string]string{"code": "123456"},
)
// 指定模板代码
resp, err := fac.SendSMS(
[]string{"13800138000"},
map[string]string{"code": "123456"},
"SMS_123456789", // 模板代码
)
```
### 5. 文件上传和查看(黑盒模式,推荐)
```go
import (
"context"
"os"
)
ctx := context.Background()
// 上传文件自动选择OSS或MinIO
file, _ := os.Open("test.jpg")
defer file.Close()
url, err := fac.UploadFile(ctx, "images/test.jpg", file, "image/jpeg")
if err != nil {
log.Fatal(err)
}
fmt.Println("文件URL:", url)
// 获取文件URL永久有效
url, _ := fac.GetFileURL("images/test.jpg", 0)
// 获取临时访问URL1小时后过期
url, _ := fac.GetFileURL("images/test.jpg", 3600)
```
### 6. Redis操作黑盒模式推荐
```go
import "context"
ctx := context.Background()
// 设置值(不过期)
err := fac.RedisSet(ctx, "user:123", "value")
// 设置值(带过期时间)
err := fac.RedisSet(ctx, "user:123", "value", time.Hour)
// 获取值
value, err := fac.RedisGet(ctx, "user:123")
// 删除键
err := fac.RedisDelete(ctx, "user:123", "user:456")
// 检查键是否存在
exists, err := fac.RedisExists(ctx, "user:123")
```
### 7. 数据库操作
数据库保持返回 GORM 对象,因为 GORM 已经提供了很好的抽象:
```go
// 获取数据库对象(延迟初始化)
db, err := fac.GetDatabase()
if err != nil {
log.Fatal(err)
}
// 直接使用,无需再创建连接
// 直接使用GORM
var users []User
db.Find(&users)
db.Create(&user)
```
### 4. 获取Redis客户端已初始化推荐
```go
import (
"context"
"github.com/redis/go-redis/v9"
)
// 直接获取已初始化的Redis客户端对象
redisClient, err := fac.GetRedisClient()
if err != nil {
log.Fatal(err)
}
// 直接使用,无需再创建连接
ctx := context.Background()
val, err := redisClient.Get(ctx, "key").Result()
if err != nil && err != redis.Nil {
log.Printf("Redis error: %v", err)
} else if err == redis.Nil {
fmt.Println("Key not found")
} else {
fmt.Printf("Value: %s\n", val)
}
```
### 5. 获取邮件客户端(已初始化)
```go
// 直接获取已初始化的邮件客户端
emailClient, err := fac.GetEmailClient()
if err != nil {
log.Fatal(err)
}
// 直接使用,无需再创建
err = emailClient.SendSimple(
[]string{"recipient@example.com"},
"主题",
"正文",
)
```
### 6. 获取短信客户端(已初始化)
```go
// 直接获取已初始化的短信客户端
smsClient, err := fac.GetSMSClient()
if err != nil {
log.Fatal(err)
}
// 直接使用,无需再创建
resp, err := smsClient.SendSimple(
[]string{"13800138000"},
map[string]string{"code": "123456"},
)
```
### 7. 获取日志记录器(已初始化)
```go
// 直接获取已初始化的日志记录器
logger, err := fac.GetLogger()
if err != nil {
log.Fatal(err)
}
// 直接使用,无需再创建
logger.Info("Application started")
logger.Error("Error occurred: %v", err)
```
### 8. 完整示例
## 完整示例
```go
package main
import (
"context"
"log"
"git.toowon.com/jimmy/go-common/config"
"os"
"time"
"git.toowon.com/jimmy/go-common/factory"
)
func main() {
// 加载配置
cfg, err := config.LoadFromFile("./config.json")
// 创建工厂
fac, err := factory.NewFactoryFromFile("./config.json")
if err != nil {
log.Fatal(err)
}
// 创建工厂
fac := factory.NewFactory(cfg)
ctx := context.Background()
// 获取邮件客户端(已初始化,可直接使用
emailClient, err := fac.GetEmailClient()
// 日志记录(黑盒模式
fac.LogInfo("应用启动")
fac.LogInfof(map[string]interface{}{
"version": "1.0.0",
}, "应用启动成功")
// 邮件发送(黑盒模式)
err = fac.SendEmail(
[]string{"user@example.com"},
"欢迎",
"欢迎使用我们的服务",
)
if err != nil {
log.Printf("Email client not available: %v", err)
} else {
// 直接使用
err = emailClient.SendSimple(
[]string{"recipient@example.com"},
"测试邮件",
"这是测试内容",
)
if err != nil {
log.Printf("Failed to send email: %v", err)
}
fac.LogError("发送邮件失败: %v", err)
}
// 获取短信客户端(已初始化,可直接使用
smsClient, err := fac.GetSMSClient()
// 短信发送(黑盒模式
resp, err := fac.SendSMS(
[]string{"13800138000"},
map[string]string{"code": "123456"},
)
if err != nil {
log.Printf("SMS client not available: %v", err)
fac.LogError("发送短信失败: %v", err)
} else {
// 直接使用
resp, err := smsClient.SendSimple(
[]string{"13800138000"},
map[string]string{"code": "123456"},
)
if err != nil {
log.Printf("Failed to send SMS: %v", err)
} else {
log.Printf("SMS sent: %s", resp.RequestID)
}
fac.LogInfo("短信发送成功: %s", resp.RequestID)
}
// 如果需要访问配置对象
cfgObj := fac.GetConfig()
dsn, _ := cfgObj.GetDatabaseDSN()
log.Printf("Database DSN: %s", dsn)
// 文件上传黑盒模式自动选择OSS或MinIO
file, _ := os.Open("test.jpg")
defer file.Close()
url, err := fac.UploadFile(ctx, "images/test.jpg", file, "image/jpeg")
if err != nil {
fac.LogError("上传文件失败: %v", err)
} else {
fac.LogInfo("文件上传成功: %s", url)
}
// Redis操作黑盒模式
err = fac.RedisSet(ctx, "user:123", "value", time.Hour)
if err != nil {
fac.LogError("Redis设置失败: %v", err)
}
value, err := fac.RedisGet(ctx, "user:123")
if err != nil {
fac.LogError("Redis获取失败: %v", err)
} else {
fac.LogInfo("Redis值: %s", value)
}
// 数据库操作
db, err := fac.GetDatabase()
if err != nil {
fac.LogError("数据库连接失败: %v", err)
} else {
var count int64
db.Table("users").Count(&count)
fac.LogInfo("用户数量: %d", count)
}
}
```
## API 参考
### NewFactory(cfg *config.Config) *Factory
### 工厂创建
#### NewFactory(cfg *config.Config) *Factory
创建工厂实例。
@@ -208,7 +253,7 @@ func main() {
**返回:** 工厂实例
### NewFactoryFromFile(filePath string) (*Factory, error)
#### NewFactoryFromFile(filePath string) (*Factory, error)
从配置文件直接创建工厂实例(便捷方法)。
@@ -219,31 +264,132 @@ func main() {
**说明:** 这是推荐的使用方式,一步完成配置加载和工厂创建。
### (f *Factory) GetEmailClient() (*email.Email, error)
### 日志方法(黑盒模式)
获取邮件客户端(已初始化)。
#### LogDebug(message string, args ...interface{})
**返回:** 已初始化的邮件客户端对象和错误信息
记录调试日志。
**说明:** 如果邮件配置为nil返回错误。
#### LogDebugf(fields map[string]interface{}, message string, args ...interface{})
### (f *Factory) GetSMSClient() (*sms.SMS, error)
记录调试日志(带字段)。
获取短信客户端(已初始化)。
#### LogInfo(message string, args ...interface{})
**返回:** 已初始化的短信客户端对象和错误信息
记录信息日志。
**说明:** 如果短信配置为nil返回错误。
#### LogInfof(fields map[string]interface{}, message string, args ...interface{})
### (f *Factory) GetLogger() (*logger.Logger, error)
记录信息日志(带字段)。
获取日志记录器(已初始化)。
#### LogWarn(message string, args ...interface{})
**返回:** 已初始化的日志记录器对象和错误信息
记录警告日志。
**说明:** 如果日志配置为nil会使用默认配置创建。
#### LogWarnf(fields map[string]interface{}, message string, args ...interface{})
### (f *Factory) GetDatabase() (*gorm.DB, error)
记录警告日志(带字段)。
#### LogError(message string, args ...interface{})
记录错误日志。
#### LogErrorf(fields map[string]interface{}, message string, args ...interface{})
记录错误日志(带字段)。
### 邮件方法(黑盒模式)
#### SendEmail(to []string, subject, body string, htmlBody ...string) error
发送邮件。
**参数:**
- `to`: 收件人列表
- `subject`: 邮件主题
- `body`: 邮件正文(纯文本)
- `htmlBody`: HTML正文可选如果设置了会优先使用
### 短信方法(黑盒模式)
#### SendSMS(phoneNumbers []string, templateParam interface{}, templateCode ...string) (*sms.SendResponse, error)
发送短信。
**参数:**
- `phoneNumbers`: 手机号列表
- `templateParam`: 模板参数map或JSON字符串
- `templateCode`: 模板代码(可选,如果为空使用配置中的模板代码)
### 存储方法(黑盒模式)
#### UploadFile(ctx context.Context, objectKey string, reader io.Reader, contentType ...string) (string, error)
上传文件。
**参数:**
- `ctx`: 上下文
- `objectKey`: 对象键(文件路径)
- `reader`: 文件内容
- `contentType`: 文件类型(可选)
**返回:** 文件访问URL和错误信息
**说明:** 自动根据配置选择OSS或MinIO优先级MinIO > OSS
#### GetFileURL(objectKey string, expires int64) (string, error)
获取文件访问URL。
**参数:**
- `objectKey`: 对象键
- `expires`: 过期时间0表示永久有效
**返回:** 文件访问URL和错误信息
### Redis方法黑盒模式
#### RedisGet(ctx context.Context, key string) (string, error)
获取Redis值。
**参数:**
- `ctx`: 上下文
- `key`: Redis键
**返回:** 值和错误信息key不存在时返回空字符串
#### RedisSet(ctx context.Context, key string, value interface{}, expiration ...time.Duration) error
设置Redis值。
**参数:**
- `ctx`: 上下文
- `key`: Redis键
- `value`: Redis值
- `expiration`: 过期时间可选0表示不过期
#### RedisDelete(ctx context.Context, keys ...string) error
删除Redis键。
**参数:**
- `ctx`: 上下文
- `keys`: Redis键列表
#### RedisExists(ctx context.Context, key string) (bool, error)
检查Redis键是否存在。
**参数:**
- `ctx`: 上下文
- `key`: Redis键
**返回:** 是否存在和错误信息
### 数据库方法
#### GetDatabase() (*gorm.DB, error)
获取数据库连接对象(已初始化)。
@@ -253,78 +399,35 @@ func main() {
- 支持MySQL、PostgreSQL、SQLite
- 自动配置连接池参数
- 数据库时间统一使用UTC时区
- 延迟初始化,首次调用时创建连接
### (f *Factory) GetRedisClient() (*redis.Client, error)
获取Redis客户端对象已初始化
**返回:** 已初始化的Redis客户端对象和错误信息
**说明:**
- 自动处理所有配置检查和连接测试
- 自动设置默认值(连接池大小、超时时间等)
- 连接失败时会自动关闭客户端并返回错误
- 返回的客户端已通过Ping测试可直接使用
### (f *Factory) GetRedisConfig() *config.RedisConfig
获取Redis配置用于创建Redis客户端
**返回:** Redis配置对象可能为nil
**说明:**
- 推荐使用 `GetRedisClient()` 方法直接获取已初始化的客户端
- 如果需要自定义创建Redis客户端可以使用此方法获取配置
### (f *Factory) GetConfig() *config.Config
#### GetConfig() *config.Config
获取配置对象。
**返回:** 配置对象
## 优势
## 设计优势
### 之前的方式(需要调用方实现)
### 优势总结
```go
// 调用方需要自己实现创建逻辑
cfg, _ := config.LoadFromFile("./config.json")
dsn, _ := cfg.GetDatabaseDSN()
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// 配置连接池
sqlDB, _ := db.DB()
sqlDB.SetMaxOpenConns(100)
// 才能使用
db.Find(&users)
```
### 使用工厂方式(直接获取已初始化对象)
```go
// 方式1直接从配置文件创建最推荐
fac, _ := factory.NewFactoryFromFile("./config.json")
db, _ := fac.GetDatabase() // 直接获取已初始化的数据库对象
// 直接使用
db.Find(&users)
// 方式2从配置对象创建
cfg, _ := config.LoadFromFile("./config.json")
fac := factory.NewFactory(cfg)
db, _ := fac.GetDatabase() // 直接获取已初始化的数据库对象
// 直接使用
db.Find(&users)
```
1. **降低复杂度**:调用方无需关心客户端对象的创建和管理
2. **延迟初始化**:所有客户端在首次使用时才创建,提高性能
3. **自动选择**:存储类型根据配置自动选择,无需手动指定
4. **统一接口**:所有操作通过工厂方法调用,接口统一
5. **容错处理**:日志初始化失败时自动回退到标准输出
6. **代码简洁**:只提供黑盒模式方法,保持代码简洁清晰
## 注意事项
1. **配置检查**工厂方法会自动检查配置是否存在如果配置为nil会返回错误
2. **错误处理**:所有Get方法都可能返回错误,需要正确处理
3. **配置对象**:可以通过`GetConfig()`方法访问原始配置对象,获取其他配置信息
2. **错误处理**:所有方法都可能返回错误,需要正确处理
3. **延迟初始化**:所有客户端在首次使用时才创建,首次调用可能稍慢
4. **存储选择**存储类型根据配置自动选择优先级MinIO > OSS
5. **数据库对象**数据库保持返回GORM对象因为GORM已经提供了很好的抽象
6. **黑盒模式**:所有功能都通过工厂方法直接调用,无需获取底层客户端对象
## 示例
完整示例请参考 `examples/factory_example.go`