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

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

@@ -4,9 +4,10 @@ import (
"context"
"fmt"
"log"
"os"
"time"
"git.toowon.com/jimmy/go-common/factory"
"github.com/redis/go-redis/v9"
)
func main() {
@@ -16,80 +17,148 @@ func main() {
log.Fatal("Failed to create factory:", err)
}
// 直接获取数据库对象(已初始化,可直接使用)
ctx := context.Background()
// ========== 日志记录(黑盒模式,推荐) ==========
fac.LogInfo("应用启动")
fac.LogDebug("调试信息: %s", "test")
fac.LogWarn("警告信息")
fac.LogError("错误信息: %v", fmt.Errorf("test error"))
// 带字段的日志
fac.LogInfof(map[string]interface{}{
"user_id": 123,
"ip": "192.168.1.1",
}, "用户登录成功")
fac.LogErrorf(map[string]interface{}{
"error_code": 1001,
"user_id": 123,
}, "登录失败: %v", fmt.Errorf("invalid password"))
// ========== 邮件发送(黑盒模式,推荐) ==========
err = fac.SendEmail(
[]string{"user@example.com"},
"验证码",
"您的验证码是123456",
)
if err != nil {
fac.LogError("发送邮件失败: %v", err)
} else {
fac.LogInfo("邮件发送成功")
}
// HTML邮件
err = fac.SendEmail(
[]string{"user@example.com"},
"欢迎",
"纯文本内容",
"<h1>HTML内容</h1>",
)
if err != nil {
fac.LogError("发送HTML邮件失败: %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)
}
// 指定模板代码
resp, err = fac.SendSMS(
[]string{"13800138000"},
map[string]string{"code": "123456"},
"SMS_123456789", // 模板代码
)
if err != nil {
fac.LogError("发送短信失败: %v", err)
}
// ========== 文件上传黑盒模式推荐自动选择OSS或MinIO ==========
file, err := os.Open("test.jpg")
if err == nil {
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)
}
}
// ========== 获取文件URL黑盒模式 ==========
// 永久有效
url, err := fac.GetFileURL("images/test.jpg", 0)
if err != nil {
fac.LogError("获取文件URL失败: %v", err)
} else {
fac.LogInfo("文件URL: %s", url)
}
// 临时访问URL1小时后过期
url, err = fac.GetFileURL("images/test.jpg", 3600)
if err != nil {
fac.LogError("获取临时URL失败: %v", err)
} else {
fac.LogInfo("临时URL: %s", url)
}
// ========== Redis操作黑盒模式推荐 ==========
// 设置值(不过期)
err = fac.RedisSet(ctx, "user:123", "value")
if err != nil {
fac.LogError("Redis设置失败: %v", err)
}
// 设置值(带过期时间)
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)
}
// 删除键
err = fac.RedisDelete(ctx, "user:123", "user:456")
if err != nil {
fac.LogError("Redis删除失败: %v", err)
}
// 检查键是否存在
exists, err := fac.RedisExists(ctx, "user:123")
if err != nil {
fac.LogError("Redis检查失败: %v", err)
} else {
fac.LogInfo("键是否存在: %v", exists)
}
// ========== 数据库操作 ==========
db, err := fac.GetDatabase()
if err != nil {
log.Printf("Database not available: %v", err)
fac.LogError("数据库连接失败: %v", err)
} else {
// 直接使用数据库
// 直接使用GORM
var count int64
if err := db.Table("users").Count(&count).Error; err != nil {
log.Printf("Failed to count users: %v", err)
fac.LogError("查询用户数量失败: %v", err)
} else {
fmt.Printf("User count: %d\n", count)
fac.LogInfo("用户数量: %d", count)
}
}
// 直接获取Redis客户端已初始化可直接使用
redisClient, err := fac.GetRedisClient()
if err != nil {
log.Printf("Redis not available: %v", err)
} else {
// 直接使用Redis客户端
ctx := context.Background()
val, err := redisClient.Get(ctx, "test_key").Result()
if err != nil && err != redis.Nil {
log.Printf("Redis error: %v", err)
} else if err == redis.Nil {
fmt.Println("Redis key not found")
} else {
fmt.Printf("Redis value: %s\n", val)
}
}
// 获取邮件客户端(已初始化,可直接使用)
emailClient, err := fac.GetEmailClient()
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)
} else {
fmt.Println("Email sent successfully")
}
}
// 获取短信客户端(已初始化,可直接使用)
smsClient, err := fac.GetSMSClient()
if err != nil {
log.Printf("SMS client not available: %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 {
fmt.Printf("SMS sent: %s\n", resp.RequestID)
}
}
// 获取日志记录器(已初始化,可直接使用)
logger, err := fac.GetLogger()
if err != nil {
log.Printf("Logger not available: %v", err)
} else {
logger.Info("Application started")
logger.Debug("Debug message")
logger.Warn("Warning message")
logger.Error("Error message")
}
fac.LogInfo("示例执行完成")
}

View File

@@ -1,101 +0,0 @@
package main
import (
"log"
"net/http"
"git.toowon.com/jimmy/go-common/http"
)
// 用户结构
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// 获取用户列表
func GetUserList(w http.ResponseWriter, r *http.Request) {
// 获取分页参数
page, pageSize := http.GetPaginationParams(r)
// 获取查询参数
keyword := http.GetQuery(r, "keyword", "")
// 模拟查询数据
users := []User{
{ID: 1, Name: "User1", Email: "user1@example.com"},
{ID: 2, Name: "User2", Email: "user2@example.com"},
}
total := int64(100)
// 返回分页响应
http.SuccessPage(w, users, total, page, pageSize)
}
// 创建用户
func CreateUser(w http.ResponseWriter, r *http.Request) {
// 解析请求体
var req struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := http.ParseJSON(r, &req); err != nil {
http.BadRequest(w, "请求参数解析失败")
return
}
// 参数验证
if req.Name == "" {
http.Error(w, 1001, "用户名不能为空")
return
}
// 模拟创建用户
user := User{
ID: 1,
Name: req.Name,
Email: req.Email,
}
// 返回成功响应
http.SuccessWithMessage(w, "创建成功", user)
}
// 获取用户详情
func GetUser(w http.ResponseWriter, r *http.Request) {
// 获取查询参数
id := http.GetQueryInt64(r, "id", 0)
if id == 0 {
http.BadRequest(w, "用户ID不能为空")
return
}
// 模拟查询用户
if id == 1 {
user := User{ID: 1, Name: "User1", Email: "user1@example.com"}
http.Success(w, user)
} else {
http.Error(w, 1002, "用户不存在")
}
}
func main() {
http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
GetUserList(w, r)
case http.MethodPost:
CreateUser(w, r)
default:
http.NotFound(w, "方法不支持")
}
})
http.HandleFunc("/user", GetUser)
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}

View File

@@ -0,0 +1,109 @@
package main
import (
"log"
"net/http"
commonhttp "git.toowon.com/jimmy/go-common/http"
)
// 用户结构
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// 获取用户列表使用Handler黑盒模式
func GetUserList(h *commonhttp.Handler) {
// 获取分页参数(简洁方式)
pagination := h.ParsePaginationRequest()
page := pagination.GetPage()
pageSize := pagination.GetSize()
// 获取查询参数(简洁方式)
_ = h.GetQuery("keyword", "") // 示例:获取查询参数
// 模拟查询数据
users := []User{
{ID: 1, Name: "User1", Email: "user1@example.com"},
{ID: 2, Name: "User2", Email: "user2@example.com"},
}
total := int64(100)
// 返回分页响应(简洁方式)
h.SuccessPage(users, total, page, pageSize)
}
// 创建用户使用Handler黑盒模式
func CreateUser(h *commonhttp.Handler) {
// 解析请求体(简洁方式)
var req struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := h.ParseJSON(&req); err != nil {
h.WriteJSON(http.StatusBadRequest, 400, "请求参数解析失败", nil)
return
}
// 参数验证
if req.Name == "" {
h.Error(1001, "用户名不能为空")
return
}
// 模拟创建用户
user := User{
ID: 1,
Name: req.Name,
Email: req.Email,
}
// 返回成功响应(简洁方式)
h.SuccessWithMessage("创建成功", user)
}
// 获取用户详情使用Handler黑盒模式
func GetUser(h *commonhttp.Handler) {
// 获取查询参数(简洁方式)
id := h.GetQueryInt64("id", 0)
if id == 0 {
h.WriteJSON(http.StatusBadRequest, 400, "用户ID不能为空", nil)
return
}
// 模拟查询用户
if id == 1 {
user := User{ID: 1, Name: "User1", Email: "user1@example.com"}
h.Success(user)
} else {
h.Error(1002, "用户不存在")
}
}
func main() {
// 方式1使用HandleFunc包装器推荐最简洁
http.HandleFunc("/users", commonhttp.HandleFunc(func(h *commonhttp.Handler) {
switch h.Request().Method {
case http.MethodGet:
GetUserList(h)
case http.MethodPost:
CreateUser(h)
default:
h.WriteJSON(http.StatusMethodNotAllowed, 405, "方法不支持", nil)
}
}))
// 方式2手动创建Handler需要更多控制时
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
GetUser(h)
})
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}

View File

@@ -0,0 +1,61 @@
package main
import (
"log"
"net/http"
commonhttp "git.toowon.com/jimmy/go-common/http"
)
// ListUserRequest 用户列表请求(包含分页字段)
type ListUserRequest struct {
Keyword string `json:"keyword"`
commonhttp.PaginationRequest // 嵌入分页请求结构
}
// User 用户结构
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// 获取用户列表使用Handler和PaginationRequest
func GetUserList(h *commonhttp.Handler) {
var req ListUserRequest
// 方式1从JSON请求体解析分页字段会自动解析
if h.Request().Method == http.MethodPost {
if err := h.ParseJSON(&req); err != nil {
h.WriteJSON(http.StatusBadRequest, 400, "请求参数解析失败", nil)
return
}
} else {
// 方式2从查询参数解析分页
pagination := h.ParsePaginationRequest()
req.PaginationRequest = *pagination
req.Keyword = h.GetQuery("keyword", "")
}
// 使用分页方法
page := req.GetPage() // 获取页码默认1
size := req.GetSize() // 获取每页数量默认20最大100
_ = req.GetOffset() // 计算偏移量
// 模拟查询数据
users := []User{
{ID: 1, Name: "User1", Email: "user1@example.com"},
{ID: 2, Name: "User2", Email: "user2@example.com"},
}
total := int64(100)
// 返回分页响应
h.SuccessPage(users, total, page, size)
}
func main() {
http.HandleFunc("/users", commonhttp.HandleFunc(GetUserList))
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}

View File

@@ -5,7 +5,7 @@ import (
"net/http"
"git.toowon.com/jimmy/go-common/datetime"
"git.toowon.com/jimmy/go-common/http"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/middleware"
)
@@ -31,8 +31,11 @@ func main() {
middleware.Timezone,
)
// 定义处理器
handler := chain.ThenFunc(apiHandler)
// 定义处理器使用Handler模式
handler := chain.ThenFunc(func(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
apiHandler(h)
})
// 注册路由
http.Handle("/api", handler)
@@ -42,10 +45,10 @@ func main() {
log.Fatal(http.ListenAndServe(":8080", nil))
}
// apiHandler 处理API请求
func apiHandler(w http.ResponseWriter, r *http.Request) {
// 从context获取时区
timezone := http.GetTimezone(r)
// apiHandler 处理API请求使用Handler模式
func apiHandler(h *commonhttp.Handler) {
// 从Handler获取时区
timezone := h.GetTimezone()
// 使用时区进行时间处理
now := datetime.Now(timezone)
@@ -53,7 +56,7 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {
endOfDay := datetime.EndOfDay(now, timezone)
// 返回响应
http.Success(w, map[string]interface{}{
h.Success(map[string]interface{}{
"message": "Hello from API",
"timezone": timezone,
"currentTime": datetime.FormatDateTime(now),
@@ -61,4 +64,3 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {
"endOfDay": datetime.FormatDateTime(endOfDay),
})
}