Files
go-common/docs/factory.md

19 KiB
Raw Permalink Blame History

工厂工具文档

概述

工厂工具提供了从配置直接创建已初始化客户端对象的功能,并提供了黑盒模式的便捷方法,让调用方无需关心底层实现细节,大大降低业务复杂度。

功能特性

  • 黑盒模式:提供直接调用的方法,无需获取客户端对象
  • 延迟初始化:所有客户端在首次使用时才创建
  • 自动选择存储类型OSS/MinIO根据配置自动选择
  • 统一接口:所有操作通过工厂方法调用
  • 向后兼容:保留 GetXXX() 方法,需要时可获取对象

方法分类总览

🌟 推荐使用:黑盒方法(一行代码搞定)

外部项目直接调用,无需获取内部对象:

功能 方法 示例
中间件 GetMiddlewareChain() chain := fac.GetMiddlewareChain()
日志 LogInfo(), LogError() fac.LogInfo("用户登录")
Redis RedisSet(), RedisGet() fac.RedisSet(ctx, "key", "val", time.Hour)
邮件 SendEmail() fac.SendEmail(to, subject, body)
短信 SendSMS() fac.SendSMS(phones, params)
存储 UploadFile(), GetFileURL() fac.UploadFile(ctx, key, file)
日期时间 Now(), ParseDateTime(), FormatDateTime() fac.Now("Asia/Shanghai")
金额计算 YuanToCents(), CentsToYuan(), FormatYuan() fac.YuanToCents(100.5)
版本信息 GetVersion() fac.GetVersion()
HTTP响应 Success(), Error(), SuccessPage() fac.Success(w, data)
HTTP请求 ParseJSON(), GetQuery(), GetTimezone() fac.ParseJSON(r, &req)

🔧 高级功能Get方法仅在必要时使用

返回客户端对象,用于复杂操作:

方法 返回类型 使用场景
GetDatabase() *gorm.DB 数据库复杂查询、事务、关联查询等
GetRedisClient() *redis.Client Hash、List、Set、ZSet、Pub/Sub等高级操作
GetLogger() *logger.Logger Close()、设置全局logger等

使用方法

1. 创建工厂(推荐)

import "git.toowon.com/jimmy/go-common/factory"

// 方式1直接从配置文件创建最推荐
fac, err := factory.NewFactoryFromFile("./config.json")
if err != nil {
    log.Fatal(err)
}

// 方式2从配置对象创建
cfg, _ := config.LoadFromFile("./config.json")
fac := factory.NewFactory(cfg)

2. 日志记录(黑盒模式,推荐)

// 简单日志
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. 邮件发送(黑盒模式,推荐)

// 简单邮件
err := fac.SendEmail(
    []string{"user@example.com"},
    "验证码",
    "您的验证码是123456",
)

// HTML邮件
err := fac.SendEmail(
    []string{"user@example.com"},
    "验证码",
    "纯文本内容",
    "<h1>HTML内容</h1>",
)

4. 短信发送(黑盒模式,推荐)

// 使用配置中的模板代码
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. 文件上传和查看(黑盒模式,推荐)

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操作黑盒模式推荐

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. 数据库操作(黑盒模式)

// 获取数据库对象(已初始化,黑盒模式)
db, err := fac.GetDatabase()
if err != nil {
    log.Fatal(err)
}

// 直接使用GORM无需自己实现创建逻辑
var users []User
db.Find(&users)
db.Create(&user)

8. 日期时间操作(黑盒模式)

// 获取当前时间
now := fac.Now("Asia/Shanghai")

// 解析时间
t, _ := fac.ParseDateTime("2024-01-01 12:00:00", "Asia/Shanghai")

// 格式化时间
str := fac.FormatDateTime(now)

// 时间计算
tomorrow := fac.AddDays(now, 1)
startOfDay := fac.StartOfDay(now, "Asia/Shanghai")
endOfDay := fac.EndOfDay(now, "Asia/Shanghai")

// Unix时间戳
unix := fac.ToUnix(now)
t2 := fac.FromUnix(unix, "Asia/Shanghai")

9. 金额计算(黑盒模式)

// 元转分
cents := fac.YuanToCents(100.5)  // 10050

// 分转元
yuan := fac.CentsToYuan(10050)  // 100.5

// 格式化显示
str := fac.FormatYuan(10050)  // "100.50"

10. 版本信息(黑盒模式)

version := fac.GetVersion()
fmt.Println("当前版本:", version)

11. HTTP响应黑盒模式

import "net/http"

// 成功响应
fac.Success(w, data)
fac.Success(w, data, "操作成功")

// 分页响应
fac.SuccessPage(w, users, total, page, pageSize)

// 错误响应
fac.Error(w, 1001, "用户不存在")
fac.SystemError(w, "系统错误")

12. HTTP请求解析黑盒模式

import "net/http"

// 解析JSON
var req UserRequest
fac.ParseJSON(r, &req)

// 获取查询参数
id := fac.GetQueryInt64(r, "id", 0)
keyword := fac.GetQuery(r, "keyword", "")

// 获取时区需要配合middleware.Timezone使用
timezone := fac.GetTimezone(r)

13. Redis操作获取客户端对象

import (
    "context"
    "github.com/redis/go-redis/v9"
)

ctx := context.Background()

// 获取Redis客户端对象已初始化黑盒模式
redisClient, err := fac.GetRedisClient()
if err != nil {
    log.Fatal(err)
}

// 直接使用Redis客户端无需自己实现创建逻辑
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)
}

// 使用高级功能如Hash操作
redisClient.HSet(ctx, "user:123", "name", "John")
name, _ := redisClient.HGet(ctx, "user:123", "name").Result()

完整示例

package main

import (
    "context"
    "log"
    "os"
    "time"

    "git.toowon.com/jimmy/go-common/factory"
)

func main() {
    // 创建工厂
    fac, err := factory.NewFactoryFromFile("./config.json")
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()

    // 日志记录(黑盒模式)
    fac.LogInfo("应用启动")
    fac.LogInfof(map[string]interface{}{
        "version": "1.0.0",
    }, "应用启动成功")

    // 邮件发送(黑盒模式)
    err = fac.SendEmail(
        []string{"user@example.com"},
        "欢迎",
        "欢迎使用我们的服务",
    )
    if err != nil {
        fac.LogError("发送邮件失败: %v", err)
    }

    // 短信发送(黑盒模式)
    resp, err := fac.SendSMS(
        []string{"13800138000"},
        map[string]string{"code": "123456"},
    )
    if err != nil {
        fac.LogError("发送短信失败: %v", err)
    } else {
        fac.LogInfo("短信发送成功: %s", resp.RequestID)
    }

    // 文件上传黑盒模式自动选择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

创建工厂实例。

参数:

  • cfg: 配置对象

返回: 工厂实例

NewFactoryFromFile(filePath string) (*Factory, error)

从配置文件直接创建工厂实例(便捷方法)。

参数:

  • filePath: 配置文件路径

返回: 工厂实例和错误信息

说明: 这是推荐的使用方式,一步完成配置加载和工厂创建。

日志方法(黑盒模式)

LogDebug(message string, args ...interface{})

记录调试日志。

LogDebugf(fields map[string]interface{}, message string, args ...interface{})

记录调试日志(带字段)。

LogInfo(message string, args ...interface{})

记录信息日志。

LogInfof(fields map[string]interface{}, message string, args ...interface{})

记录信息日志(带字段)。

LogWarn(message string, args ...interface{})

记录警告日志。

LogWarnf(fields map[string]interface{}, message string, args ...interface{})

记录警告日志(带字段)。

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)

获取数据库连接对象(已初始化)。

返回: 已初始化的GORM数据库对象和错误信息

说明:

  • 支持MySQL、PostgreSQL、SQLite
  • 自动配置连接池参数
  • 数据库时间统一使用UTC时区
  • 延迟初始化,首次调用时创建连接
  • 黑盒模式只需传递config对象无需自己实现创建逻辑

Redis方法

GetRedisClient() (*redis.Client, error)

获取Redis客户端对象已初始化

返回: 已初始化的Redis客户端对象和错误信息

说明:

  • 自动处理所有配置检查和连接测试
  • 自动设置默认值(连接池大小、超时时间等)
  • 连接失败时会自动关闭客户端并返回错误
  • 返回的客户端已通过Ping测试可直接使用
  • 黑盒模式只需传递config对象无需自己实现创建逻辑
  • 推荐使用 RedisGetRedisSetRedisDelete 等方法直接操作Redis
  • 如果需要使用Redis的高级功能如Hash、List、Set等可以使用此方法获取客户端对象

配置方法

GetConfig() *config.Config

获取配置对象。

返回: 配置对象

HTTP响应方法黑盒模式

Success(w http.ResponseWriter, data interface{}, message ...string)

发送成功响应。

参数:

  • w: HTTP响应写入器
  • data: 响应数据
  • message: 可选,成功消息(如果不提供,使用默认消息)

示例:

fac.Success(w, user)                    // 使用默认消息
fac.Success(w, user, "获取成功")        // 自定义消息

Error(w http.ResponseWriter, code int, message string)

发送错误响应。

参数:

  • w: HTTP响应写入器
  • code: 业务错误码
  • message: 错误消息

SystemError(w http.ResponseWriter, message string)

发送系统错误响应HTTP 500

参数:

  • w: HTTP响应写入器
  • message: 错误消息

SuccessPage(w http.ResponseWriter, data interface{}, total int64, page, pageSize int, message ...string)

发送分页成功响应。

参数:

  • w: HTTP响应写入器
  • data: 响应数据列表
  • total: 总记录数
  • page: 当前页码
  • pageSize: 每页大小
  • message: 可选,成功消息

HTTP请求方法黑盒模式

ParseJSON(r *http.Request, v interface{}) error

解析JSON请求体。

参数:

  • r: HTTP请求
  • v: 目标结构体指针

GetQuery(r *http.Request, key, defaultValue string) string

获取查询参数。

GetQueryInt(r *http.Request, key string, defaultValue int) int

获取查询整数参数。

GetQueryInt64(r *http.Request, key string, defaultValue int64) int64

获取查询64位整数参数。

GetFormValue(r *http.Request, key, defaultValue string) string

获取表单值。

GetTimezone(r *http.Request) string

从请求的context中获取时区需要配合middleware.Timezone使用

日期时间工具方法(黑盒模式)

Now(timezone ...string) time.Time

获取当前时间。

参数:

  • timezone: 可选,时区字符串(如 "Asia/Shanghai"),不指定则使用默认时区

ParseDateTime(value string, timezone ...string) (time.Time, error)

解析日期时间字符串格式2006-01-02 15:04:05

ParseDate(value string, timezone ...string) (time.Time, error)

解析日期字符串格式2006-01-02

FormatDateTime(t time.Time, timezone ...string) string

格式化日期时间格式2006-01-02 15:04:05

FormatDate(t time.Time, timezone ...string) string

格式化日期格式2006-01-02

FormatTime(t time.Time, timezone ...string) string

格式化时间格式15:04:05

ToUnix(t time.Time) int64

转换为Unix时间戳

FromUnix(sec int64, timezone ...string) time.Time

从Unix时间戳创建时间。

ToUnixMilli(t time.Time) int64

转换为Unix毫秒时间戳。

FromUnixMilli(msec int64, timezone ...string) time.Time

从Unix毫秒时间戳创建时间。

AddDays(t time.Time, days int) time.Time

添加天数。

AddMonths(t time.Time, months int) time.Time

添加月数。

AddYears(t time.Time, years int) time.Time

添加年数。

StartOfDay(t time.Time, timezone ...string) time.Time

获取一天的开始时间00:00:00

EndOfDay(t time.Time, timezone ...string) time.Time

获取一天的结束时间23:59:59.999999999)。

StartOfMonth(t time.Time, timezone ...string) time.Time

获取月份的开始时间。

EndOfMonth(t time.Time, timezone ...string) time.Time

获取月份的结束时间。

StartOfYear(t time.Time, timezone ...string) time.Time

获取年份的开始时间。

EndOfYear(t time.Time, timezone ...string) time.Time

获取年份的结束时间。

DiffDays(t1, t2 time.Time) int

计算两个时间之间的天数差。

DiffHours(t1, t2 time.Time) int64

计算两个时间之间的小时差。

DiffMinutes(t1, t2 time.Time) int64

计算两个时间之间的分钟差。

DiffSeconds(t1, t2 time.Time) int64

计算两个时间之间的秒数差。

金额工具方法(黑盒模式)

GetMoneyCalculator() *tools.MoneyCalculator

获取金额计算器实例。

YuanToCents(yuan float64) int64

元转分。

示例:

cents := fac.YuanToCents(100.5)  // 返回 10050

CentsToYuan(cents int64) float64

分转元。

示例:

yuan := fac.CentsToYuan(10050)  // 返回 100.5

FormatYuan(cents int64) string

格式化显示金额分转元保留2位小数

示例:

str := fac.FormatYuan(10050)  // 返回 "100.50"

版本工具方法(黑盒模式)

GetVersion() string

获取版本号。

说明:

  • 优先从环境变量 DOCKER_TAGVERSION 中读取
  • 如果没有设置环境变量,则使用默认版本号

设计优势

优势总结

  1. 降低复杂度:调用方无需关心客户端对象的创建和管理
  2. 延迟初始化:所有客户端在首次使用时才创建,提高性能
  3. 自动选择:存储类型根据配置自动选择,无需手动指定
  4. 统一接口:所有操作通过工厂方法调用,接口统一
  5. 容错处理:日志初始化失败时自动回退到标准输出
  6. 代码简洁:只提供黑盒模式方法,保持代码简洁清晰

注意事项

  1. 配置检查工厂方法会自动检查配置是否存在如果配置为nil会返回错误
  2. 错误处理:所有方法都可能返回错误,需要正确处理
  3. 延迟初始化:所有客户端在首次使用时才创建,首次调用可能稍慢
  4. 存储选择存储类型根据配置自动选择优先级MinIO > OSS
  5. 数据库对象数据库保持返回GORM对象因为GORM已经提供了很好的抽象
  6. 黑盒模式:所有功能都通过工厂方法直接调用,无需获取底层客户端对象

示例

完整示例请参考 examples/factory_example.go