Files
go-common/factory/factory.go
2026-01-30 21:40:21 +08:00

1688 lines
53 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package factory
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"git.toowon.com/jimmy/go-common/config"
"git.toowon.com/jimmy/go-common/email"
"git.toowon.com/jimmy/go-common/excel"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/i18n"
"git.toowon.com/jimmy/go-common/logger"
"git.toowon.com/jimmy/go-common/middleware"
"git.toowon.com/jimmy/go-common/migration"
"git.toowon.com/jimmy/go-common/sms"
"git.toowon.com/jimmy/go-common/storage"
"git.toowon.com/jimmy/go-common/tools"
"github.com/redis/go-redis/v9"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// ========== HTTP响应结构体暴露给外部项目使用 ==========
// Response 标准响应结构(暴露给外部项目使用)
// 外部项目可以直接使用 factory.Response 创建响应对象
//
// 示例:
//
// response := factory.Response{
// Code: 0,
// Message: "success",
// Data: data,
// }
type Response = commonhttp.Response
// PageResponse 分页响应结构(暴露给外部项目使用)
// 外部项目可以直接使用 factory.PageResponse 创建分页响应对象
type PageResponse = commonhttp.PageResponse
// PageData 分页数据(暴露给外部项目使用)
// 外部项目可以直接使用 factory.PageData 创建分页数据对象
//
// 示例:
//
// pageData := &factory.PageData{
// List: users,
// Total: 100,
// Page: 1,
// PageSize: 20,
// }
// fac.Success(w, pageData)
type PageData = commonhttp.PageData
// ========== HTTP请求结构体暴露给外部项目使用 ==========
// PaginationRequest 分页请求结构(暴露给外部项目使用)
// 外部项目可以直接使用 factory.PaginationRequest 创建分页请求对象
//
// 示例:
//
// type ListUserRequest struct {
// Keyword string `json:"keyword"`
// factory.PaginationRequest // 嵌入分页请求结构
// }
type PaginationRequest = commonhttp.PaginationRequest
// ========== Time工具结构体暴露给外部项目使用 ==========
// TimeInfo 详细时间信息结构(暴露给外部项目使用)
// 外部项目可以直接使用 factory.TimeInfo 创建时间信息对象
type TimeInfo = tools.TimeInfo
// Factory 工厂类 - 黑盒模式设计
//
// 核心理念:
//
// 外部项目只需传递一个配置文件路径,即可直接使用所有功能,
// 无需关心内部实现细节。
//
// 推荐使用的黑盒方法:
// - GetMiddlewareChain():获取配置好的中间件链
// - LogInfo(), LogError():记录日志
// - RedisSet(), RedisGet()操作Redis
// - SendEmail(), SendSMS():发送邮件和短信
// - UploadFile(), GetFileURL():文件上传和访问
//
// 需要获取客户端对象的场景(高级功能):
// - GetDatabase()数据库操作GORM已经是很好的抽象
// - GetRedisClient()Redis高级操作Hash, List, Set, ZSet等
// - GetLogger()Logger高级功能Close等
//
// 使用示例:
//
// // 1. 创建工厂(传入配置文件路径)
// fac, _ := factory.NewFactoryFromFile("config.json")
//
// // 2. 直接使用黑盒方法(推荐)
// fac.LogInfo("用户登录成功")
// fac.RedisSet(ctx, "session:123", "data", time.Hour)
// fac.SendEmail([]string{"user@example.com"}, "主题", "内容")
// chain := fac.GetMiddlewareChain()
// chain.Append(yourAuthMiddleware) // 添加自定义中间件
//
// // 3. 获取客户端对象(仅在需要高级功能时)
// db, _ := fac.GetDatabase()
// db.Find(&users)
//
// Factory 工厂类,用于从配置创建各种客户端对象
type Factory struct {
cfg *config.Config
storage storage.Storage // 存储实例(延迟初始化)
logger *logger.Logger // 日志实例(延迟初始化)
email *email.Email // 邮件客户端(延迟初始化)
sms *sms.SMS // 短信客户端(延迟初始化)
db *gorm.DB // 数据库连接(延迟初始化)
redis *redis.Client // Redis客户端延迟初始化
i18n *i18n.I18n // 国际化工具(延迟初始化)
excel *excel.Excel // Excel导出器延迟初始化
}
// NewFactory 创建工厂实例
func NewFactory(cfg *config.Config) *Factory {
return &Factory{
cfg: cfg,
}
}
// NewFactoryFromFile 从配置文件创建工厂实例(便捷方法)
// filePath: 配置文件路径
func NewFactoryFromFile(filePath string) (*Factory, error) {
cfg, err := config.LoadFromFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to load config: %w", err)
}
return NewFactory(cfg), nil
}
// getEmailClient 获取邮件客户端(内部方法,延迟初始化)
func (f *Factory) getEmailClient() (*email.Email, error) {
if f.email != nil {
return f.email, nil
}
f.email = email.NewEmail(f.cfg)
return f.email, nil
}
// SendEmail 发送邮件(黑盒模式,推荐使用)
// 自动使用配置文件中的SMTP配置发送邮件
// to: 收件人列表
// subject: 邮件主题
// body: 邮件正文(纯文本)
// htmlBody: HTML正文可选如果设置了会优先使用
func (f *Factory) SendEmail(to []string, subject, body string, htmlBody ...string) error {
e, err := f.getEmailClient()
if err != nil {
return err
}
return e.SendEmail(to, subject, body, htmlBody...)
}
// getSMSClient 获取短信客户端(内部方法,延迟初始化)
func (f *Factory) getSMSClient() (*sms.SMS, error) {
if f.sms != nil {
return f.sms, nil
}
f.sms = sms.NewSMS(f.cfg)
return f.sms, nil
}
// SendSMS 发送短信(黑盒模式,推荐使用)
// 自动使用配置文件中的阿里云短信配置发送短信
// phoneNumbers: 手机号列表
// templateParam: 模板参数map或JSON字符串
// templateCode: 模板代码(可选,如果为空使用配置中的模板代码)
func (f *Factory) SendSMS(phoneNumbers []string, templateParam interface{}, templateCode ...string) (*sms.SendResponse, error) {
s, err := f.getSMSClient()
if err != nil {
return nil, err
}
return s.SendSMS(phoneNumbers, templateParam, templateCode...)
}
// getLogger 获取日志记录器(内部方法,延迟初始化)
func (f *Factory) getLogger() (*logger.Logger, error) {
if f.logger != nil {
return f.logger, nil
}
var l *logger.Logger
var err error
if f.cfg.Logger == nil {
// 如果没有配置,使用默认配置创建
l, err = logger.NewLogger(nil)
} else {
l, err = logger.NewLogger(f.cfg.Logger)
}
if err != nil {
return nil, fmt.Errorf("failed to create logger: %w", err)
}
f.logger = l
return l, nil
}
// GetLogger 获取日志记录器对象(不推荐直接使用)
//
// ⚠️ 不推荐直接使用此方法,推荐使用黑盒方法:
// - LogDebug, LogInfo, LogWarn, LogError记录简单日志
// - LogDebugf, LogInfof, LogWarnf, LogErrorf记录带字段的日志
//
// 仅在以下高级场景时使用:
// - 需要调用 Close() 方法关闭logger
// - 需要使用logger的其他高级功能
//
// 示例(不推荐):
//
// logger, _ := factory.GetLogger()
// defer logger.Close()
//
// 示例(推荐):
//
// factory.LogInfo("用户登录成功")
// factory.LogErrorf(map[string]interface{}{"user_id": 123}, "登录失败")
func (f *Factory) GetLogger() (*logger.Logger, error) {
return f.getLogger()
}
// LogDebug 记录调试日志(黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogDebug(message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[DEBUG] "+message+"\n", args...)
} else {
fmt.Printf("[DEBUG] %s\n", message)
}
return
}
if len(args) > 0 {
l.Debug(message, args...)
} else {
l.Debug(message)
}
}
// LogDebugf 记录调试日志(带字段,黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// fields: 日志字段
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogDebugf(fields map[string]interface{}, message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[DEBUG] "+message+"\n", args...)
} else {
fmt.Printf("[DEBUG] %s\n", message)
}
return
}
l.Debugf(fields, message, args...)
}
// LogInfo 记录信息日志(黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogInfo(message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[INFO] "+message+"\n", args...)
} else {
fmt.Printf("[INFO] %s\n", message)
}
return
}
if len(args) > 0 {
l.Info(message, args...)
} else {
l.Info(message)
}
}
// LogInfof 记录信息日志(带字段,黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// fields: 日志字段
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogInfof(fields map[string]interface{}, message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[INFO] "+message+"\n", args...)
} else {
fmt.Printf("[INFO] %s\n", message)
}
return
}
l.Infof(fields, message, args...)
}
// LogWarn 记录警告日志(黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogWarn(message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[WARN] "+message+"\n", args...)
} else {
fmt.Printf("[WARN] %s\n", message)
}
return
}
if len(args) > 0 {
l.Warn(message, args...)
} else {
l.Warn(message)
}
}
// LogWarnf 记录警告日志(带字段,黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// fields: 日志字段
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogWarnf(fields map[string]interface{}, message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[WARN] "+message+"\n", args...)
} else {
fmt.Printf("[WARN] %s\n", message)
}
return
}
l.Warnf(fields, message, args...)
}
// LogError 记录错误日志(黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogError(message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[ERROR] "+message+"\n", args...)
} else {
fmt.Printf("[ERROR] %s\n", message)
}
return
}
if len(args) > 0 {
l.Error(message, args...)
} else {
l.Error(message)
}
}
// LogErrorf 记录错误日志(带字段,黑盒模式,推荐使用)
// 自动使用配置文件中的logger配置
// fields: 日志字段
// message: 日志消息
// args: 格式化参数(可选)
func (f *Factory) LogErrorf(fields map[string]interface{}, message string, args ...interface{}) {
l, err := f.getLogger()
if err != nil {
// 如果日志初始化失败,使用标准输出
if len(args) > 0 {
fmt.Printf("[ERROR] "+message+"\n", args...)
} else {
fmt.Printf("[ERROR] %s\n", message)
}
return
}
l.Errorf(fields, message, args...)
}
// getDatabase 获取数据库连接对象(内部方法,延迟初始化)
func (f *Factory) getDatabase() (*gorm.DB, error) {
if f.db != nil {
return f.db, nil
}
if f.cfg.Database == nil {
return nil, fmt.Errorf("database config is nil")
}
// 获取DSN
dsn, err := f.cfg.GetDatabaseDSN()
if err != nil {
return nil, fmt.Errorf("failed to get DSN: %w", err)
}
// 根据数据库类型创建连接
var db *gorm.DB
switch f.cfg.Database.Type {
case "mysql":
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
case "postgres":
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
case "sqlite":
db, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{})
default:
return nil, fmt.Errorf("unsupported database type: %s", f.cfg.Database.Type)
}
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
// 配置连接池
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("failed to get sql.DB: %w", err)
}
if f.cfg.Database.MaxOpenConns > 0 {
sqlDB.SetMaxOpenConns(f.cfg.Database.MaxOpenConns)
}
if f.cfg.Database.MaxIdleConns > 0 {
sqlDB.SetMaxIdleConns(f.cfg.Database.MaxIdleConns)
}
if f.cfg.Database.ConnMaxLifetime > 0 {
sqlDB.SetConnMaxLifetime(time.Duration(f.cfg.Database.ConnMaxLifetime) * time.Second)
}
f.db = db
return db, nil
}
// GetDatabase 获取数据库连接对象(推荐使用)
// 返回已初始化的GORM数据库对象可直接使用
//
// 数据库操作保持使用 GORM 对象,因为:
// - 数据库操作非常复杂多样(查询、插入、更新、删除、事务等)
// - GORM 已经提供了很好的抽象和 API
// - 无需在 factory 中重复封装所有数据库方法
//
// 示例:
//
// db, _ := factory.GetDatabase()
// db.Find(&users)
// db.Create(&user)
// db.Transaction(func(tx *gorm.DB) error { ... })
func (f *Factory) GetDatabase() (*gorm.DB, error) {
return f.getDatabase()
}
// getRedisClient 获取Redis客户端对象内部方法延迟初始化
func (f *Factory) getRedisClient() (*redis.Client, error) {
if f.redis != nil {
return f.redis, nil
}
if f.cfg.Redis == nil {
return nil, fmt.Errorf("redis config is nil")
}
// 获取Redis地址
addr := f.cfg.GetRedisAddr()
if addr == "" {
return nil, fmt.Errorf("redis address is empty")
}
// 设置默认值
redisConfig := f.cfg.Redis
if redisConfig.PoolSize == 0 {
redisConfig.PoolSize = 10 // 默认连接池大小
}
if redisConfig.MinIdleConns == 0 {
redisConfig.MinIdleConns = 5 // 默认最小空闲连接数
}
if redisConfig.DialTimeout == 0 {
redisConfig.DialTimeout = 5 // 默认连接超时5秒
}
if redisConfig.ReadTimeout == 0 {
redisConfig.ReadTimeout = 3 // 默认读取超时3秒
}
if redisConfig.WriteTimeout == 0 {
redisConfig.WriteTimeout = 3 // 默认写入超时3秒
}
// 创建Redis客户端
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: redisConfig.Password,
DB: redisConfig.Database,
PoolSize: redisConfig.PoolSize,
MinIdleConns: redisConfig.MinIdleConns,
MaxRetries: redisConfig.MaxRetries,
DialTimeout: time.Duration(redisConfig.DialTimeout) * time.Second,
ReadTimeout: time.Duration(redisConfig.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(redisConfig.WriteTimeout) * time.Second,
})
// 测试连接
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(redisConfig.DialTimeout)*time.Second)
defer cancel()
_, err := client.Ping(ctx).Result()
if err != nil {
client.Close() // 连接失败时关闭客户端
return nil, fmt.Errorf("failed to connect to redis: %w", err)
}
f.redis = client
return client, nil
}
// GetRedisClient 获取Redis客户端对象高级功能时使用
// 返回已初始化的Redis客户端对象
//
// 推荐使用黑盒方法:
// - RedisGet, RedisSet, RedisDelete, RedisExists常用操作
//
// 仅在需要使用高级功能时获取客户端:
// - Hash 操作HSet, HGet, HGetAll 等)
// - List 操作LPush, RPush, LRange 等)
// - Set 操作SAdd, SMembers 等)
// - ZSet 操作ZAdd, ZRange 等)
// - 其他高级功能
//
// 示例(常用操作,推荐):
//
// factory.RedisSet(ctx, "key", "value", time.Hour)
// value, _ := factory.RedisGet(ctx, "key")
//
// 示例(高级功能):
//
// client, _ := factory.GetRedisClient()
// client.HSet(ctx, "user:1", "name", "Alice")
// client.LPush(ctx, "queue", "task1")
func (f *Factory) GetRedisClient() (*redis.Client, error) {
return f.getRedisClient()
}
// RedisGet 获取Redis值黑盒模式推荐使用
// 自动使用配置文件中的Redis配置
// key: Redis键
func (f *Factory) RedisGet(ctx context.Context, key string) (string, error) {
client, err := f.getRedisClient()
if err != nil {
return "", err
}
result, err := client.Get(ctx, key).Result()
if err == redis.Nil {
return "", nil // key不存在返回空字符串
}
if err != nil {
return "", fmt.Errorf("failed to get redis key: %w", err)
}
return result, nil
}
// RedisSet 设置Redis值黑盒模式推荐使用
// 自动使用配置文件中的Redis配置
// key: Redis键
// value: Redis值
// expiration: 过期时间可选0表示不过期
func (f *Factory) RedisSet(ctx context.Context, key string, value interface{}, expiration ...time.Duration) error {
client, err := f.getRedisClient()
if err != nil {
return err
}
var exp time.Duration
if len(expiration) > 0 {
exp = expiration[0]
}
err = client.Set(ctx, key, value, exp).Err()
if err != nil {
return fmt.Errorf("failed to set redis key: %w", err)
}
return nil
}
// RedisDelete 删除Redis键黑盒模式推荐使用
// 自动使用配置文件中的Redis配置
// keys: Redis键列表
func (f *Factory) RedisDelete(ctx context.Context, keys ...string) error {
if len(keys) == 0 {
return nil
}
client, err := f.getRedisClient()
if err != nil {
return err
}
err = client.Del(ctx, keys...).Err()
if err != nil {
return fmt.Errorf("failed to delete redis keys: %w", err)
}
return nil
}
// RedisExists 检查Redis键是否存在黑盒模式推荐使用
// 自动使用配置文件中的Redis配置
// key: Redis键
func (f *Factory) RedisExists(ctx context.Context, key string) (bool, error) {
client, err := f.getRedisClient()
if err != nil {
return false, err
}
count, err := client.Exists(ctx, key).Result()
if err != nil {
return false, fmt.Errorf("failed to check redis key existence: %w", err)
}
return count > 0, nil
}
// GetConfig 获取配置对象
func (f *Factory) GetConfig() *config.Config {
return f.cfg
}
// getStorage 获取存储实例(内部方法,延迟初始化)
func (f *Factory) getStorage() (storage.Storage, error) {
if f.storage != nil {
return f.storage, nil
}
// 根据配置自动选择存储类型
// 优先级Local > MinIO > OSS
var storageType storage.StorageType
if f.cfg.GetLocalStorage() != nil {
storageType = storage.StorageTypeLocal
} else if f.cfg.MinIO != nil {
storageType = storage.StorageTypeMinIO
} else if f.cfg.OSS != nil {
storageType = storage.StorageTypeOSS
} else {
return nil, fmt.Errorf("no storage config found (LocalStorage, OSS or MinIO)")
}
// 创建存储实例
s, err := storage.NewStorage(storageType, f.cfg)
if err != nil {
return nil, fmt.Errorf("failed to create storage: %w", err)
}
f.storage = s
return s, nil
}
// GetStorage 获取存储实例对象(高级功能时使用)
// 通常推荐使用黑盒方法:
// - UploadFile()
// - GetFileURL()
//
// 如需自定义上传/查看行为(例如 Delete/Exists/GetObject可使用此方法获取底层存储对象。
func (f *Factory) GetStorage() (storage.Storage, error) {
return f.getStorage()
}
// UploadFile 上传文件(黑盒模式,推荐使用)
// 自动根据配置选择存储类型OSS 或 MinIO无需关心内部实现
// ctx: 上下文
// objectKey: 对象键(文件路径)
// reader: 文件内容
// contentType: 文件类型(可选)
// 返回文件访问URL和错误
func (f *Factory) UploadFile(ctx context.Context, objectKey string, reader io.Reader, contentType ...string) (string, error) {
s, err := f.getStorage()
if err != nil {
return "", err
}
// 上传文件
err = s.Upload(ctx, objectKey, reader, contentType...)
if err != nil {
return "", fmt.Errorf("failed to upload file: %w", err)
}
// 获取文件URL
url, err := s.GetURL(objectKey, 0)
if err != nil {
return "", fmt.Errorf("failed to get file URL: %w", err)
}
return url, nil
}
// GetFileURL 获取文件访问URL黑盒模式推荐使用
// 自动根据配置选择存储类型返回文件的访问URL
// objectKey: 对象键(文件路径)
// expires: 过期时间0表示永久有效
func (f *Factory) GetFileURL(objectKey string, expires int64) (string, error) {
s, err := f.getStorage()
if err != nil {
return "", err
}
return s.GetURL(objectKey, expires)
}
// GetMiddlewareChain 获取配置好的中间件链(黑盒模式)
// 自动包含Recovery、Logging、RateLimit如果配置了、CORS如果配置了、Timezone
// 返回已配置好的中间件链,可以通过 Append() 方法添加自定义中间件
//
// 示例1直接使用
//
// chain := factory.GetMiddlewareChain()
// http.Handle("/api/users", chain.ThenFunc(handleUsers))
//
// 示例2添加自定义中间件
//
// chain := factory.GetMiddlewareChain()
// chain.Append(yourCustomMiddleware1, yourCustomMiddleware2)
// http.Handle("/api/users", chain.ThenFunc(handleUsers))
func (f *Factory) GetMiddlewareChain() *middleware.Chain {
var middlewares []func(http.Handler) http.Handler
// 1. Recovery 中间件必需防止panic导致服务崩溃
l, _ := f.getLogger() // 获取logger如果失败会使用默认logger
middlewares = append(middlewares, middleware.Recovery(&middleware.RecoveryConfig{
Logger: l,
}))
// 2. Logging 中间件(必需,记录所有请求)
middlewares = append(middlewares, middleware.Logging(&middleware.LoggingConfig{
Logger: l,
}))
// 3. RateLimit 中间件(如果配置了限流)
if f.cfg != nil && f.cfg.RateLimit != nil {
if f.cfg.RateLimit.Enable {
// 从配置创建限流中间件
limiter := middleware.NewTokenBucketLimiter(
f.cfg.RateLimit.Rate,
time.Duration(f.cfg.RateLimit.Period)*time.Second,
)
var keyFunc func(r *http.Request) string
if f.cfg.RateLimit.ByIP {
keyFunc = func(r *http.Request) string {
return middleware.GetClientIP(r)
}
} else if f.cfg.RateLimit.ByUserID {
keyFunc = func(r *http.Request) string {
return r.Header.Get("X-User-ID")
}
}
middlewares = append(middlewares, middleware.RateLimit(&middleware.RateLimitConfig{
Limiter: limiter,
KeyFunc: keyFunc,
}))
}
}
// 4. CORS 中间件(如果配置了)
if f.cfg != nil && f.cfg.CORS != nil {
corsConfig := &middleware.CORSConfig{
AllowedOrigins: f.cfg.CORS.AllowedOrigins,
AllowedMethods: f.cfg.CORS.AllowedMethods,
AllowedHeaders: f.cfg.CORS.AllowedHeaders,
ExposedHeaders: f.cfg.CORS.ExposedHeaders,
AllowCredentials: f.cfg.CORS.AllowCredentials,
MaxAge: f.cfg.CORS.MaxAge,
}
middlewares = append(middlewares, middleware.CORS(corsConfig))
}
// 5. Language 中间件(必需,处理语言)
middlewares = append(middlewares, middleware.Language)
// 6. Timezone 中间件(必需,处理时区)
middlewares = append(middlewares, middleware.Timezone)
return middleware.NewChain(middlewares...)
}
// RunMigrations 执行数据库迁移(黑盒模式,推荐使用)
// 自动发现并执行指定目录下的所有迁移文件
// migrationsDir: 迁移文件目录(如 "migrations" 或 "scripts/sql"
//
// 支持的文件命名格式:
// - 数字前缀: 01_init_schema.sql
// - 时间戳: 20240101000001_create_users.sql
// - 带.up后缀: 20240101000001_create_users.up.sql
//
// 示例:
//
// fac, _ := factory.NewFactoryFromFile("config.json")
// err := fac.RunMigrations("migrations")
// if err != nil {
// log.Fatal(err)
// }
func (f *Factory) RunMigrations(migrationsDir string) error {
// 获取数据库连接
db, err := f.getDatabase()
if err != nil {
return fmt.Errorf("failed to get database: %w", err)
}
// 创建迁移器(传入数据库类型,性能更好)
dbType := "mysql" // 默认值
if f.cfg.Database != nil && f.cfg.Database.Type != "" {
dbType = f.cfg.Database.Type
}
migrator := migration.NewMigratorWithType(db, dbType)
// 自动发现并加载迁移文件
migrations, err := migration.LoadMigrationsFromFiles(migrationsDir, "*.sql")
if err != nil {
return fmt.Errorf("failed to load migrations: %w", err)
}
if len(migrations) == 0 {
f.LogInfo("在目录 '%s' 中没有找到迁移文件", migrationsDir)
return nil
}
migrator.AddMigrations(migrations...)
// 执行迁移
if err := migrator.Up(); err != nil {
return fmt.Errorf("failed to run migrations: %w", err)
}
f.LogInfo("迁移执行成功: %d 个迁移文件", len(migrations))
return nil
}
// GetMigrationStatus 获取迁移状态(黑盒模式,推荐使用)
// migrationsDir: 迁移文件目录
// 返回迁移状态列表,包含版本、描述、是否已应用等信息
//
// 示例:
//
// fac, _ := factory.NewFactoryFromFile("config.json")
// status, err := fac.GetMigrationStatus("migrations")
// if err != nil {
// log.Fatal(err)
// }
// for _, s := range status {
// fmt.Printf("Version: %s, Applied: %v\n", s.Version, s.Applied)
// }
func (f *Factory) GetMigrationStatus(migrationsDir string) ([]migration.MigrationStatus, error) {
// 获取数据库连接
db, err := f.getDatabase()
if err != nil {
return nil, fmt.Errorf("failed to get database: %w", err)
}
// 创建迁移器(传入数据库类型,性能更好)
dbType := "mysql" // 默认值
if f.cfg.Database != nil && f.cfg.Database.Type != "" {
dbType = f.cfg.Database.Type
}
migrator := migration.NewMigratorWithType(db, dbType)
// 加载迁移文件
migrations, err := migration.LoadMigrationsFromFiles(migrationsDir, "*.sql")
if err != nil {
return nil, fmt.Errorf("failed to load migrations: %w", err)
}
migrator.AddMigrations(migrations...)
// 获取状态
status, err := migrator.Status()
if err != nil {
return nil, fmt.Errorf("failed to get migration status: %w", err)
}
return status, nil
}
// ========== HTTP响应方法黑盒模式推荐使用 ==========
//
// 这些方法直接调用 http 包的公共方法,保持低耦合。
// 推荐直接使用 factory.Success() 等方法,而不是通过 handler。
// Success 成功响应(黑盒模式,推荐使用)
// w: ResponseWriter
// data: 响应数据可以为nil
// message: 响应消息(可选),如果为空则使用默认消息 "success"
//
// 示例:
//
// fac, _ := factory.NewFactoryFromFile("config.json")
// http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
// fac.Success(w, user) // 只有数据
// fac.Success(w, user, "查询成功") // 数据+消息
// })
func (f *Factory) Success(w http.ResponseWriter, data interface{}, message ...string) {
commonhttp.Success(w, data, message...)
}
// SuccessPage 分页成功响应(黑盒模式,推荐使用)
// w: ResponseWriter
// list: 数据列表
// total: 总记录数
// page: 当前页码
// pageSize: 每页大小
// message: 响应消息(可选),如果为空则使用默认消息 "success"
func (f *Factory) SuccessPage(w http.ResponseWriter, list interface{}, total int64, page, pageSize int, message ...string) {
commonhttp.SuccessPage(w, list, total, page, pageSize, message...)
}
// Error 错误响应(黑盒模式,推荐使用)
// w: ResponseWriter
// r: HTTP请求用于获取语言信息和i18n处理
// code: 业务错误码如果message是消息代码此参数会被语言文件中的code覆盖
// message: 错误消息或消息代码如果i18n已初始化且message是消息代码格式会自动获取国际化消息和业务code
// args: 可选参数,用于格式化消息(类似 fmt.Sprintf仅在message是消息代码时使用
//
// 使用逻辑:
// 1. 如果i18n已初始化且message看起来是消息代码包含点号如 "user.not_found"
// 则从请求context中获取语言并尝试从语言文件中获取国际化消息和业务code
// 2. 如果获取到国际化消息使用语言文件中的code作为响应code使用国际化消息作为响应message
// 3. 如果未获取到或i18n未初始化使用传入的code和message
//
// 示例:
//
// // 方式1直接传入消息代码推荐自动国际化
// fac.Error(w, r, 0, "user.not_found")
// // 如果请求语言是 zh-CN且语言文件中 "user.not_found" 的 code 是 1001
// // 返回: {"code": 1001, "message": "用户不存在"}
// // 如果请求语言是 en-US返回: {"code": 1001, "message": "User not found"}
//
// // 方式2带参数的消息代码
// fac.Error(w, r, 0, "user.welcome", "Alice")
// // 如果消息内容是 "欢迎,%s",返回: {"code": 0, "message": "欢迎Alice"}
//
// // 方式3直接传入消息文本不使用国际化
// fac.Error(w, r, 500, "系统错误")
// // 返回: {"code": 500, "message": "系统错误"}
func (f *Factory) Error(w http.ResponseWriter, r *http.Request, code int, message string, args ...interface{}) {
// 判断message是否是消息代码简单判断包含点号
isMessageCode := strings.Contains(message, ".")
var finalCode int
var finalMessage string
if isMessageCode {
// 尝试从i18n获取国际化消息和业务code
if i, err := f.getI18n(); err == nil {
// i18n已初始化获取语言并查找消息
lang := f.GetLanguage(r)
if lang == "" {
lang = i.GetDefaultLang()
}
msgInfo := i.GetMessageInfo(lang, message, args...)
// 如果获取到了国际化消息不是返回code本身使用国际化消息和业务code
if msgInfo.Message != message {
finalCode = msgInfo.Code
finalMessage = msgInfo.Message
} else {
// 消息代码不存在使用传入的code和消息代码作为消息
finalCode = code
finalMessage = message
}
} else {
// i18n未初始化使用传入的code和消息代码作为消息
finalCode = code
finalMessage = message
}
} else {
// 不是消息代码格式使用传入的code和消息
finalCode = code
finalMessage = message
}
commonhttp.Error(w, finalCode, finalMessage)
}
// SystemError 系统错误响应返回HTTP 500黑盒模式推荐使用
// w: ResponseWriter
// message: 错误消息
func (f *Factory) SystemError(w http.ResponseWriter, message string) {
commonhttp.SystemError(w, message)
}
// ========== HTTP请求解析方法黑盒模式推荐使用 ==========
//
// 这些方法直接调用 http 包的公共方法,保持低耦合。
// 推荐直接使用 factory.ParseJSON()、factory.ConvertInt() 等方法。
// ParseJSON 解析JSON请求体黑盒模式推荐使用
// r: HTTP请求
// v: 目标结构体指针
//
// 示例:
//
// var req struct {
// Name string `json:"name"`
// }
// if err := fac.ParseJSON(r, &req); err != nil {
// fac.Error(w, 400, "请求参数解析失败")
// return
// }
func (f *Factory) ParseJSON(r *http.Request, v interface{}) error {
return commonhttp.ParseJSON(r, v)
}
// ParsePaginationRequest 从请求中解析分页参数(黑盒模式,推荐使用)
// r: HTTP请求
// 支持从查询参数和form表单中解析
// 优先级:查询参数 > form表单
//
// 示例:
//
// pagination := fac.ParsePaginationRequest(r)
// page := pagination.GetPage()
// pageSize := pagination.GetSize()
func (f *Factory) ParsePaginationRequest(r *http.Request) *PaginationRequest {
return commonhttp.ParsePaginationRequest(r)
}
// ConvertInt 将字符串转换为int类型黑盒模式推荐使用
// value: 待转换的字符串
// defaultValue: 转换失败或字符串为空时返回的默认值
func (f *Factory) ConvertInt(value string, defaultValue int) int {
return tools.ConvertInt(value, defaultValue)
}
// ConvertInt64 将字符串转换为int64类型黑盒模式推荐使用
// value: 待转换的字符串
// defaultValue: 转换失败或字符串为空时返回的默认值
func (f *Factory) ConvertInt64(value string, defaultValue int64) int64 {
return tools.ConvertInt64(value, defaultValue)
}
// ConvertUint64 将字符串转换为uint64类型黑盒模式推荐使用
// value: 待转换的字符串
// defaultValue: 转换失败或字符串为空时返回的默认值
func (f *Factory) ConvertUint64(value string, defaultValue uint64) uint64 {
return tools.ConvertUint64(value, defaultValue)
}
// ConvertUint32 将字符串转换为uint32类型黑盒模式推荐使用
// value: 待转换的字符串
// defaultValue: 转换失败或字符串为空时返回的默认值
func (f *Factory) ConvertUint32(value string, defaultValue uint32) uint32 {
return tools.ConvertUint32(value, defaultValue)
}
// ConvertBool 将字符串转换为bool类型黑盒模式推荐使用
// value: 待转换的字符串
// defaultValue: 转换失败或字符串为空时返回的默认值
func (f *Factory) ConvertBool(value string, defaultValue bool) bool {
return tools.ConvertBool(value, defaultValue)
}
// ConvertFloat64 将字符串转换为float64类型黑盒模式推荐使用
// value: 待转换的字符串
// defaultValue: 转换失败或字符串为空时返回的默认值
func (f *Factory) ConvertFloat64(value string, defaultValue float64) float64 {
return tools.ConvertFloat64(value, defaultValue)
}
// GetTimezone 从请求的context中获取时区黑盒模式推荐使用
// r: HTTP请求
// 如果使用了middleware.Timezone中间件可以从context中获取时区信息
// 如果未设置,返回默认时区 AsiaShanghai
func (f *Factory) GetTimezone(r *http.Request) string {
return commonhttp.GetTimezone(r)
}
// GetLanguage 从请求的context中获取语言黑盒模式推荐使用
// r: HTTP请求
// 如果使用了middleware.Language中间件可以从context中获取语言信息
// 如果未设置,返回默认语言 zh-CN
func (f *Factory) GetLanguage(r *http.Request) string {
return commonhttp.GetLanguage(r)
}
// ========== Tools工具方法黑盒模式推荐使用 ==========
//
// 这些方法直接调用 tools 包的公共方法,保持低耦合。
// factory 只负责方法暴露,具体业务在 tools 包中实现。
// ========== Version 版本工具 ==========
// GetVersion 获取版本号(黑盒模式,推荐使用)
// 优先从环境变量 DOCKER_TAG 或 VERSION 中读取
// 如果没有设置环境变量,则使用默认版本号
func (f *Factory) GetVersion() string {
return tools.GetVersion()
}
// ========== Money 金额工具 ==========
// GetMoneyCalculator 获取金额计算器(黑盒模式,推荐使用)
// 返回金额计算器实例,可用于金额计算操作
func (f *Factory) GetMoneyCalculator() *tools.MoneyCalculator {
return tools.NewMoneyCalculator()
}
// YuanToCents 元转分(黑盒模式,推荐使用)
func (f *Factory) YuanToCents(yuan float64) int64 {
return tools.YuanToCents(yuan)
}
// CentsToYuan 分转元(黑盒模式,推荐使用)
func (f *Factory) CentsToYuan(cents int64) float64 {
return tools.CentsToYuan(cents)
}
// FormatYuan 格式化显示金额分转元保留2位小数黑盒模式推荐使用
func (f *Factory) FormatYuan(cents int64) string {
return tools.FormatYuan(cents)
}
// ========== DateTime 日期时间工具 ==========
// Now 获取当前时间(使用指定时区)(黑盒模式,推荐使用)
func (f *Factory) Now(timezone ...string) time.Time {
return tools.Now(timezone...)
}
// ParseDateTime 解析日期时间字符串2006-01-02 15:04:05黑盒模式推荐使用
func (f *Factory) ParseDateTime(value string, timezone ...string) (time.Time, error) {
return tools.ParseDateTime(value, timezone...)
}
// ParseDate 解析日期字符串2006-01-02黑盒模式推荐使用
func (f *Factory) ParseDate(value string, timezone ...string) (time.Time, error) {
return tools.ParseDate(value, timezone...)
}
// FormatDateTime 格式化日期时间2006-01-02 15:04:05黑盒模式推荐使用
func (f *Factory) FormatDateTime(t time.Time, timezone ...string) string {
return tools.FormatDateTime(t, timezone...)
}
// FormatDate 格式化日期2006-01-02黑盒模式推荐使用
func (f *Factory) FormatDate(t time.Time, timezone ...string) string {
return tools.FormatDate(t, timezone...)
}
// FormatTime 格式化时间15:04:05黑盒模式推荐使用
func (f *Factory) FormatTime(t time.Time, timezone ...string) string {
return tools.FormatTime(t, timezone...)
}
// ToUnix 转换为Unix时间戳黑盒模式推荐使用
func (f *Factory) ToUnix(t time.Time) int64 {
return tools.ToUnix(t)
}
// FromUnix 从Unix时间戳创建时间黑盒模式推荐使用
func (f *Factory) FromUnix(sec int64, timezone ...string) time.Time {
return tools.FromUnix(sec, timezone...)
}
// ToUnixMilli 转换为Unix毫秒时间戳黑盒模式推荐使用
func (f *Factory) ToUnixMilli(t time.Time) int64 {
return tools.ToUnixMilli(t)
}
// FromUnixMilli 从Unix毫秒时间戳创建时间黑盒模式推荐使用
func (f *Factory) FromUnixMilli(msec int64, timezone ...string) time.Time {
return tools.FromUnixMilli(msec, timezone...)
}
// AddDays 添加天数(黑盒模式,推荐使用)
func (f *Factory) AddDays(t time.Time, days int) time.Time {
return tools.AddDays(t, days)
}
// AddMonths 添加月数(黑盒模式,推荐使用)
func (f *Factory) AddMonths(t time.Time, months int) time.Time {
return tools.AddMonths(t, months)
}
// AddYears 添加年数(黑盒模式,推荐使用)
func (f *Factory) AddYears(t time.Time, years int) time.Time {
return tools.AddYears(t, years)
}
// StartOfDay 获取一天的开始时间00:00:00黑盒模式推荐使用
func (f *Factory) StartOfDay(t time.Time, timezone ...string) time.Time {
return tools.StartOfDay(t, timezone...)
}
// EndOfDay 获取一天的结束时间23:59:59.999999999)(黑盒模式,推荐使用)
func (f *Factory) EndOfDay(t time.Time, timezone ...string) time.Time {
return tools.EndOfDay(t, timezone...)
}
// StartOfMonth 获取月份的开始时间(黑盒模式,推荐使用)
func (f *Factory) StartOfMonth(t time.Time, timezone ...string) time.Time {
return tools.StartOfMonth(t, timezone...)
}
// EndOfMonth 获取月份的结束时间(黑盒模式,推荐使用)
func (f *Factory) EndOfMonth(t time.Time, timezone ...string) time.Time {
return tools.EndOfMonth(t, timezone...)
}
// StartOfYear 获取年份的开始时间(黑盒模式,推荐使用)
func (f *Factory) StartOfYear(t time.Time, timezone ...string) time.Time {
return tools.StartOfYear(t, timezone...)
}
// EndOfYear 获取年份的结束时间(黑盒模式,推荐使用)
func (f *Factory) EndOfYear(t time.Time, timezone ...string) time.Time {
return tools.EndOfYear(t, timezone...)
}
// DiffDays 计算两个时间之间的天数差(黑盒模式,推荐使用)
func (f *Factory) DiffDays(t1, t2 time.Time) int {
return tools.DiffDays(t1, t2)
}
// DiffHours 计算两个时间之间的小时差(黑盒模式,推荐使用)
func (f *Factory) DiffHours(t1, t2 time.Time) int64 {
return tools.DiffHours(t1, t2)
}
// DiffMinutes 计算两个时间之间的分钟差(黑盒模式,推荐使用)
func (f *Factory) DiffMinutes(t1, t2 time.Time) int64 {
return tools.DiffMinutes(t1, t2)
}
// DiffSeconds 计算两个时间之间的秒数差(黑盒模式,推荐使用)
func (f *Factory) DiffSeconds(t1, t2 time.Time) int64 {
return tools.DiffSeconds(t1, t2)
}
// ========== Time 时间工具(黑盒模式,推荐使用) ==========
//
// 这些方法提供基础时间操作、时间戳、时间判断等功能。
// 与 DateTime 工具的区别:
// - DateTime: 专注于时区相关、格式化、解析、UTC转换
// - Time: 专注于基础时间操作、时间戳、时间判断、时间信息生成
// ========== 时间戳方法 ==========
// GetTimestamp 获取当前时间戳(秒)(黑盒模式,推荐使用)
func (f *Factory) GetTimestamp() int64 {
return tools.GetTimestamp()
}
// GetMillisTimestamp 获取当前时间戳(毫秒)(黑盒模式,推荐使用)
func (f *Factory) GetMillisTimestamp() int64 {
return tools.GetMillisTimestamp()
}
// GetUTCTimestamp 获取UTC时间戳黑盒模式推荐使用
func (f *Factory) GetUTCTimestamp() int64 {
return tools.GetUTCTimestamp()
}
// GetUTCTimestampFromTime 从指定时间获取UTC时间戳黑盒模式推荐使用
func (f *Factory) GetUTCTimestampFromTime(t time.Time) int64 {
return tools.GetUTCTimestampFromTime(t)
}
// ========== 格式化方法(自定义格式) ==========
// FormatTimeWithLayout 格式化时间(自定义格式)(黑盒模式,推荐使用)
// layout: 时间格式,如 "2006-01-02 15:04:05",如果为空则使用默认格式
func (f *Factory) FormatTimeWithLayout(t time.Time, layout string) string {
return tools.FormatTimeWithLayout(t, layout)
}
// FormatTimeUTC 格式化时间为UTC字符串ISO 8601格式黑盒模式推荐使用
func (f *Factory) FormatTimeUTC(t time.Time) string {
return tools.FormatTimeUTC(t)
}
// GetCurrentTime 获取当前时间字符串(黑盒模式,推荐使用)
// 使用默认格式 "2006-01-02 15:04:05"
func (f *Factory) GetCurrentTime() string {
return tools.GetCurrentTime()
}
// ========== 解析方法(自定义格式) ==========
// ParseTime 解析时间字符串(自定义格式)(黑盒模式,推荐使用)
// timeStr: 时间字符串
// layout: 时间格式,如 "2006-01-02 15:04:05",如果为空则使用默认格式
func (f *Factory) ParseTime(timeStr, layout string) (time.Time, error) {
return tools.ParseTime(timeStr, layout)
}
// ========== 时间计算(补充 DateTime 的 Add 系列) ==========
// AddHours 增加小时数(黑盒模式,推荐使用)
func (f *Factory) AddHours(t time.Time, hours int) time.Time {
return tools.AddHours(t, hours)
}
// AddMinutes 增加分钟数(黑盒模式,推荐使用)
func (f *Factory) AddMinutes(t time.Time, minutes int) time.Time {
return tools.AddMinutes(t, minutes)
}
// ========== 时间范围(周相关) ==========
// GetBeginOfWeek 获取某周的开始时间(周一)(黑盒模式,推荐使用)
func (f *Factory) GetBeginOfWeek(t time.Time) time.Time {
return tools.GetBeginOfWeek(t)
}
// GetEndOfWeek 获取某周的结束时间(周日)(黑盒模式,推荐使用)
func (f *Factory) GetEndOfWeek(t time.Time) time.Time {
return tools.GetEndOfWeek(t)
}
// ========== 时间判断 ==========
// IsToday 判断是否为今天(黑盒模式,推荐使用)
func (f *Factory) IsToday(t time.Time) bool {
return tools.IsToday(t)
}
// IsYesterday 判断是否为昨天(黑盒模式,推荐使用)
func (f *Factory) IsYesterday(t time.Time) bool {
return tools.IsYesterday(t)
}
// IsTomorrow 判断是否为明天(黑盒模式,推荐使用)
func (f *Factory) IsTomorrow(t time.Time) bool {
return tools.IsTomorrow(t)
}
// ========== 时间信息生成 ==========
// GenerateTimeInfoWithTimezone 生成详细时间信息(指定时区)(黑盒模式,推荐使用)
// 返回包含UTC时间、本地时间、时间戳、时区信息等的完整时间信息结构
func (f *Factory) GenerateTimeInfoWithTimezone(t time.Time, timezone string) TimeInfo {
return tools.GenerateTimeInfoWithTimezone(t, timezone)
}
// ========== Crypto 加密工具(黑盒模式,推荐使用) ==========
//
// 这些方法提供密码加密、哈希计算、随机字符串生成等功能。
// ========== 密码加密 ==========
// HashPassword 使用bcrypt加密密码黑盒模式推荐使用
// password: 原始密码
// 返回: 加密后的密码哈希值
func (f *Factory) HashPassword(password string) (string, error) {
return tools.HashPassword(password)
}
// CheckPassword 验证密码(黑盒模式,推荐使用)
// password: 原始密码
// hash: 加密后的密码哈希值
// 返回: 密码是否正确
func (f *Factory) CheckPassword(password, hash string) bool {
return tools.CheckPassword(password, hash)
}
// ========== 哈希计算 ==========
// MD5 计算MD5哈希值黑盒模式推荐使用
// text: 要计算哈希的文本
// 返回: MD5哈希值十六进制字符串
func (f *Factory) MD5(text string) string {
return tools.MD5(text)
}
// SHA256 计算SHA256哈希值黑盒模式推荐使用
// text: 要计算哈希的文本
// 返回: SHA256哈希值十六进制字符串
func (f *Factory) SHA256(text string) string {
return tools.SHA256(text)
}
// ========== 随机字符串生成 ==========
// GenerateRandomString 生成指定长度的随机字符串(黑盒模式,推荐使用)
// length: 字符串长度
// 返回: 随机字符串(包含大小写字母和数字)
func (f *Factory) GenerateRandomString(length int) string {
return tools.GenerateRandomString(length)
}
// GenerateRandomNumber 生成指定长度的随机数字字符串(黑盒模式,推荐使用)
// length: 字符串长度
// 返回: 随机数字字符串
func (f *Factory) GenerateRandomNumber(length int) string {
return tools.GenerateRandomNumber(length)
}
// ========== 业务相关随机码生成 ==========
// GenerateSMSCode 生成短信验证码(黑盒模式,推荐使用)
// 返回: 6位数字验证码
func (f *Factory) GenerateSMSCode() string {
return tools.GenerateSMSCode()
}
// GenerateOrderNo 生成订单号(黑盒模式,推荐使用)
// prefix: 订单号前缀
// 返回: 订单号(格式:前缀+时间戳+6位随机数
func (f *Factory) GenerateOrderNo(prefix string) string {
return tools.GenerateOrderNo(prefix)
}
// GeneratePaymentNo 生成支付单号(黑盒模式,推荐使用)
// 返回: 支付单号格式PAY+时间戳+6位随机数
func (f *Factory) GeneratePaymentNo() string {
return tools.GeneratePaymentNo()
}
// GenerateRefundNo 生成退款单号(黑盒模式,推荐使用)
// 返回: 退款单号格式RF+时间戳+6位随机数
func (f *Factory) GenerateRefundNo() string {
return tools.GenerateRefundNo()
}
// GenerateTransferNo 生成调拨单号(黑盒模式,推荐使用)
// 返回: 调拨单号格式TF+时间戳+6位随机数
func (f *Factory) GenerateTransferNo() string {
return tools.GenerateTransferNo()
}
// ========== I18n 国际化工具(黑盒模式,推荐使用) ==========
//
// 这些方法提供多语言内容管理功能,支持从文件加载语言内容,通过语言代码和消息代码获取对应语言的内容。
// getI18n 获取国际化工具实例(内部方法,延迟初始化)
func (f *Factory) getI18n() (*i18n.I18n, error) {
if f.i18n != nil {
return f.i18n, nil
}
// 如果没有配置,返回错误
return nil, fmt.Errorf("i18n not initialized, please call InitI18n first")
}
// InitI18n 初始化国际化工具(黑盒模式,推荐使用)
// defaultLang: 默认语言代码(如 "zh-CN", "en-US"
// 初始化后可以调用 LoadI18nFromDir 或 LoadI18nFromFile 加载语言文件
//
// 示例:
//
// fac, _ := factory.NewFactoryFromFile("config.json")
// fac.InitI18n("zh-CN")
// fac.LoadI18nFromDir("locales")
func (f *Factory) InitI18n(defaultLang string) {
f.i18n = i18n.NewI18n(defaultLang)
}
// LoadI18nFromDir 从目录加载多个语言文件(黑盒模式,推荐使用)
// dirPath: 语言文件目录路径
// 文件命名规则:{语言代码}.json如 zh-CN.json, en-US.json
//
// 文件格式示例zh-CN.json
//
// {
// "user.not_found": "用户不存在",
// "user.login_success": "登录成功",
// "user.welcome": "欢迎,%s"
// }
//
// 示例:
//
// fac.InitI18n("zh-CN")
// fac.LoadI18nFromDir("locales")
func (f *Factory) LoadI18nFromDir(dirPath string) error {
i, err := f.getI18n()
if err != nil {
return err
}
return i.LoadFromDir(dirPath)
}
// LoadI18nFromFile 从单个语言文件加载内容(黑盒模式,推荐使用)
// filePath: 语言文件路径JSON格式
// lang: 语言代码(如 "zh-CN", "en-US"
//
// 示例:
//
// fac.InitI18n("zh-CN")
// fac.LoadI18nFromFile("locales/zh-CN.json", "zh-CN")
// fac.LoadI18nFromFile("locales/en-US.json", "en-US")
func (f *Factory) LoadI18nFromFile(filePath, lang string) error {
i, err := f.getI18n()
if err != nil {
return err
}
return i.LoadFromFile(filePath, lang)
}
// GetMessage 获取指定语言和代码的消息内容(黑盒模式,推荐使用)
// lang: 语言代码(如 "zh-CN", "en-US"
// code: 消息代码(如 "user.not_found"
// args: 可选参数,用于格式化消息(类似 fmt.Sprintf
//
// 返回逻辑:
// 1. 如果指定语言存在该code返回对应内容
// 2. 如果指定语言不存在,尝试使用默认语言
// 3. 如果默认语言也不存在返回code本身作为fallback
//
// 示例:
//
// // 简单消息
// msg := fac.GetMessage("zh-CN", "user.not_found")
// // 返回: "用户不存在"
//
// // 带参数的消息
// msg := fac.GetMessage("zh-CN", "user.welcome", "Alice")
// // 如果消息内容是 "欢迎,%s",返回: "欢迎Alice"
func (f *Factory) GetMessage(lang, code string, args ...interface{}) string {
i, err := f.getI18n()
if err != nil {
// 如果未初始化返回code本身
return code
}
return i.GetMessage(lang, code, args...)
}
// GetI18n 获取国际化工具对象(高级功能时使用)
// 返回已初始化的国际化工具对象
//
// 推荐使用黑盒方法:
// - GetMessage():获取消息内容
// - LoadI18nFromDir():加载语言文件目录
// - LoadI18nFromFile():加载单个语言文件
//
// 仅在需要使用高级功能时获取对象:
// - HasLang():检查语言是否存在
// - GetSupportedLangs():获取所有支持的语言
// - ReloadFromFile():重新加载语言文件
// - SetDefaultLang():动态设置默认语言
//
// 示例(常用操作,推荐):
//
// fac.InitI18n("zh-CN")
// fac.LoadI18nFromDir("locales")
// msg := fac.GetMessage("zh-CN", "user.not_found")
//
// 示例(高级功能):
//
// i18n, _ := fac.GetI18n()
// langs := i18n.GetSupportedLangs()
// hasLang := i18n.HasLang("en-US")
func (f *Factory) GetI18n() (*i18n.I18n, error) {
return f.getI18n()
}
// ========== Excel 导出工具(黑盒模式,推荐使用) ==========
//
// 这些方法提供数据导出到Excel的功能支持结构体切片和自定义数据格式。
// getExcelClient 获取Excel导出器实例内部方法延迟初始化
func (f *Factory) getExcelClient() (*excel.Excel, error) {
if f.excel != nil {
return f.excel, nil
}
f.excel = excel.NewExcel()
return f.excel, nil
}
// GetExcel 获取Excel导出器对象高级功能时使用
// 返回已初始化的Excel导出器对象
//
// 推荐使用黑盒方法:
// - ExportToExcel()导出到Writer
// - ExportToExcelFile():导出到文件
//
// 仅在需要使用高级功能时获取对象:
// - 多工作表操作
// - 自定义样式
// - 图表、公式等高级功能
//
// 示例(常用操作,推荐):
//
// fac.ExportToExcel(w, "用户列表", columns, users)
//
// 示例(高级功能):
//
// excel, _ := fac.GetExcel()
// file := excel.GetFile()
// file.NewSheet("Sheet2")
func (f *Factory) GetExcel() (*excel.Excel, error) {
return f.getExcelClient()
}
// ExportColumn Excel导出列定义暴露给外部项目使用
// 外部项目可以直接使用 factory.ExportColumn 创建列定义
type ExportColumn = excel.ExportColumn
// ExportToExcel 导出数据到Writer黑盒模式推荐使用
// w: Writer对象如http.ResponseWriter
// sheetName: 工作表名称(可选,默认为"Sheet1"
// columns: 列定义
// data: 数据列表可以是结构体切片或实现了ExportData接口的对象
// 返回错误信息
//
// 示例1导出结构体切片
//
// type User struct {
// ID int `json:"id"`
// Name string `json:"name"`
// Email string `json:"email"`
// }
//
// users := []User{
// {ID: 1, Name: "Alice", Email: "alice@example.com"},
// {ID: 2, Name: "Bob", Email: "bob@example.com"},
// }
//
// columns := []factory.ExportColumn{
// {Header: "ID", Field: "ID"},
// {Header: "姓名", Field: "Name"},
// {Header: "邮箱", Field: "Email"},
// }
//
// fac.ExportToExcel(w, "用户列表", columns, users)
//
// 示例2使用格式化函数
//
// columns := []factory.ExportColumn{
// {Header: "ID", Field: "ID"},
// {Header: "姓名", Field: "Name"},
// {Header: "创建时间", Field: "CreatedAt", Format: excel.FormatDateTimeDefault},
// }
//
// fac.ExportToExcel(w, "用户列表", columns, users)
func (f *Factory) ExportToExcel(w io.Writer, sheetName string, columns []ExportColumn, data interface{}) error {
e, err := f.getExcelClient()
if err != nil {
return err
}
return e.ExportToWriter(w, sheetName, columns, data)
}
// ExportToExcelFile 导出数据到文件(黑盒模式,推荐使用)
// filePath: 文件路径
// sheetName: 工作表名称(可选,默认为"Sheet1"
// columns: 列定义
// data: 数据列表可以是结构体切片或实现了ExportData接口的对象
// 返回错误信息
//
// 示例:
//
// fac.ExportToExcelFile("users.xlsx", "用户列表", columns, users)
//
// 注意:此方法内部创建文件并调用 ExportToWriter确保行为与 ExportToExcel 一致
func (f *Factory) ExportToExcelFile(filePath string, sheetName string, columns []ExportColumn, data interface{}) error {
e, err := f.getExcelClient()
if err != nil {
return err
}
// 创建文件
file, err := os.Create(filePath)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer file.Close()
// 调用 ExportToWriter复用核心逻辑
return e.ExportToWriter(file, sheetName, columns, data)
}