From deb3cc71bf1a010ba63f1fcd7b3d24a92da0a05d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=AD=E5=B7=9E=E6=98=8E=E5=A9=B3=E7=A7=91=E6=8A=80?= Date: Mon, 22 Jun 2026 23:21:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(v1.0.4):=20DX=20&=20Docs=20=E6=94=B9?= =?UTF-8?q?=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按报告 v1.0.4 路线图完成 5 项开发体验与文档改进: #25 模块路径文档改进: - 保留 -core 后缀(xlgo 是多产品系列:xlgo-core/xlgo-orm/xlgo-ai) - README 快速开始补完整 import 示例 + 新增「模块路径与包名」小节 - CLAUDE.md Import Path Note 措辞明确化 #27 Without* Option 定位文档化: - 调研确认 Without* 有真实用例(测试覆盖 + NewFullStack 后排除单项) - 不删不标 Deprecated,在 app.go 注释 + README 说明其定位 #28 CLI 多模板: - xlgo new --template minimal/api/fullstack - minimal 轻量HTTP无依赖,api 标准业务分层(默认),fullstack 全组件 - 三模板实测生成正确 #29 examples/ 目录: - examples/minimal(50行可跑,无外部依赖) - examples/full(mysql+redis+jwt+user CRUD) - examples/README.md #30 文档结构: - Version_*.md + report.md 移到 docs/plans/(v2.0-review.md) - 新增 docs/README.md 索引 - CHANGELOG/GUIDE 留根目录(按惯例) - .gitignore 整理 build/vet/test + examples 编译全绿。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 9 +- README.md | 35 ++ app.go | 6 +- cmd/xlgo/commands.go | 85 ++- cmd/xlgo/main.go | 13 +- cmd/xlgo/templates.go | 170 +++++- docs/README.md | 21 + .../plans/Version_Update_Plan_v1.0.2.md | 0 .../plans/Version_v1.0.2_report.md | 0 docs/plans/v2.0-review.md | 485 ++++++++++++++++++ examples/README.md | 42 ++ examples/full/config.yaml | 35 ++ examples/full/main.go | 125 +++++ examples/minimal/config.yaml | 8 + examples/minimal/main.go | 42 ++ 15 files changed, 1045 insertions(+), 31 deletions(-) create mode 100644 docs/README.md rename Version_Update_Plan_v1.0.2.md => docs/plans/Version_Update_Plan_v1.0.2.md (100%) rename Version_v1.0.2_report.md => docs/plans/Version_v1.0.2_report.md (100%) create mode 100644 docs/plans/v2.0-review.md create mode 100644 examples/README.md create mode 100644 examples/full/config.yaml create mode 100644 examples/full/main.go create mode 100644 examples/minimal/config.yaml create mode 100644 examples/minimal/main.go diff --git a/.gitignore b/.gitignore index 5e77db2..3613b7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ -report.md +# Claude Code 协作指引(本地,不入库) CLAUDE.md + +# 临时发版辅助文件 +gitHub_release_*.md + +# 构建产物 +*.exe +bin/ diff --git a/README.md b/README.md index f60dac2..efb2a47 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,41 @@ go run main.go 访问 http://localhost:8080/health 检查服务状态。 > v1.0.2 起,`xlgo.New()` 默认是轻量应用,不会自动初始化 MySQL、Redis、Storage 或 Swagger。需要完整基础设施时请显式使用 `WithMySQL()`、`WithRedis()`、`WithStorage()`、`WithSwaggerRoutes()`,或直接使用 `xlgo.NewFullStack()`。 +> +> `Without*` 系列 Option(如 `WithoutSwaggerRoutes`)主要用于 `NewFullStack` / `RunFullStack` 启用全部组件后排除个别项,例如 `xlgo.NewFullStack(xlgo.WithoutSwaggerRoutes())` 全组件但关闭 Swagger。`xlgo.New()` 本身已全关,无需再 `Without`。 + +### 4. 模块路径与包名 + +xlgo 的 **模块路径** 是 `github.com/EthanCodeCraft/xlgo-core`,**包名** 是 `xlgo`(二者不同是 Go 惯例,如 `github.com/gin-gonic/gin` → 包名 `gin`)。import 时用别名 `xlgo` 即可: + +```go +package main + +import ( + xlgo "github.com/EthanCodeCraft/xlgo-core" + "github.com/EthanCodeCraft/xlgo-core/middleware" + "github.com/EthanCodeCraft/xlgo-core/response" + "github.com/gin-gonic/gin" +) + +func main() { + app := xlgo.New( + xlgo.WithConfigPath("./config.yaml"), + xlgo.WithLogger(), + xlgo.WithHealthRoutes(), + ) + // 注册一个示例路由 + app.GetRouter().GET("/", func(c *gin.Context) { + response.Success(c, gin.H{"message": "Hello xlgo!"}) + }) + // 启用 JWT 认证示例路由(需配合 WithMySQL/WithRedis 生成 token) + _ = middleware.RequireUserTypes("admin") + + _ = app.Run() +} +``` + +> 子包(`config` / `database` / `logger` / `middleware` ...)的包名与目录名一致,无需别名。 --- diff --git a/app.go b/app.go index fb073e3..37e3ade 100644 --- a/app.go +++ b/app.go @@ -123,7 +123,11 @@ func WithDefaultRoutes() Option { } } -// WithoutLogger 关闭日志 +// WithoutLogger 关闭日志。 +// +// Without* 系列的定位:xlgo.New() 默认是轻量应用(组件全关),故 Without* +// 主要用于 NewFullStack / RunFullStack 启用全部组件后,排除个别不需要的项。 +// 例如:xlgo.NewFullStack(xlgo.WithoutSwaggerRoutes()) 全组件但关 Swagger。 func WithoutLogger() Option { return func(a *App) { a.enableLogger = false } } diff --git a/cmd/xlgo/commands.go b/cmd/xlgo/commands.go index 885db07..b226af1 100644 --- a/cmd/xlgo/commands.go +++ b/cmd/xlgo/commands.go @@ -19,17 +19,46 @@ func createProject(name string) { return } - // 创建目录结构 - dirs := []string{ - name, - name + "/config", - name + "/handler", - name + "/model", - name + "/repository", - name + "/service", - name + "/middleware", - name + "/public", - name + "/logs", + // 解析 --template 与 --module 参数(默认 template=api) + tmplName := "api" + module := name + args := os.Args[3:] + for i := 0; i < len(args); i++ { + switch args[i] { + case "--template", "-t": + if i+1 < len(args) { + tmplName = args[i+1] + i++ + } + case "--module", "-m": + if i+1 < len(args) { + module = args[i+1] + i++ + } + } + } + + // 校验模板名 + switch tmplName { + case "minimal", "api", "fullstack": + // ok + default: + fmt.Printf("未知模板: %s(可选: minimal / api / fullstack)\n", tmplName) + return + } + + // minimal 模板目录结构最小化;api/fullstack 含完整分层目录 + var dirs []string + dirs = append(dirs, name, name+"/public", name+"/logs") + if tmplName != "minimal" { + dirs = append(dirs, + name+"/config", + name+"/handler", + name+"/model", + name+"/repository", + name+"/service", + name+"/middleware", + ) } for _, dir := range dirs { @@ -39,12 +68,6 @@ func createProject(name string) { } } - // 获取模块路径 - module := name - if len(os.Args) > 3 && os.Args[3] == "--module" && len(os.Args) > 4 { - module = os.Args[4] - } - caser := cases.Title(language.English) data := TemplateData{ Package: caser.String(name), @@ -54,14 +77,28 @@ func createProject(name string) { Year: time.Now().Year(), } + // 按模板选择 main.go 与 config.yaml + var mainTmpl, configTmpl string + switch tmplName { + case "minimal": + mainTmpl, configTmpl = templates.MainMinimal, templates.ConfigMinimal + case "fullstack": + mainTmpl, configTmpl = templates.MainFull, templates.ConfigFull + default: // api + mainTmpl, configTmpl = templates.Main, templates.Config + } + // 创建文件 files := map[string]string{ - name + "/main.go": templates.Main, - name + "/config.yaml": templates.Config, - name + "/go.mod": fmt.Sprintf(templates.GoMod, module, xlgo.Version), - name + "/Makefile": templates.Makefile, - name + "/.gitignore": templates.Gitignore, - name + "/handler/home.go": templates.Handler, + name + "/main.go": mainTmpl, + name + "/config.yaml": configTmpl, + name + "/go.mod": fmt.Sprintf(templates.GoMod, module, xlgo.Version), + name + "/Makefile": templates.Makefile, + name + "/.gitignore": templates.Gitignore, + } + // api/fullstack 模板带示例 handler + if tmplName != "minimal" { + files[name+"/handler/home.go"] = templates.Handler } for path, content := range files { @@ -84,7 +121,7 @@ func createProject(name string) { } } - fmt.Printf("✓ 项目 %s 创建成功\n", name) + fmt.Printf("✓ 项目 %s 创建成功(模板: %s)\n", name, tmplName) fmt.Println("\n下一步:") fmt.Printf(" cd %s\n", name) fmt.Println(" go mod tidy") diff --git a/cmd/xlgo/main.go b/cmd/xlgo/main.go index fd0e201..bf559b8 100644 --- a/cmd/xlgo/main.go +++ b/cmd/xlgo/main.go @@ -11,15 +11,22 @@ func printUsage() { fmt.Println(`xlgo - Go Web 框架脚手架工具 用法: - xlgo new <项目名> 创建新项目 - xlgo make handler <名称> 创建处理器 + xlgo new <项目名> [--template <模板>] [--module <模块路径>] 创建新项目 + xlgo make handler <名称> 创建处理器 xlgo make repository <名称> 创建仓库 xlgo make model <名称> 创建模型 xlgo make service <名称> 创建服务 - xlgo version 显示版本号 + xlgo version 显示版本号 + +模板 (xlgo new --template <名称>): + minimal 轻量 HTTP 服务,不依赖 MySQL/Redis(默认入门) + api 标准业务 API,含 MySQL/Redis/JWT 与 handler/model/repository/service 分层(默认) + fullstack 全组件,一键启用 MySQL/Redis/Storage/Swagger/AutoMigrate 示例: xlgo new myapp + xlgo new myapp --template minimal + xlgo new myapp --template fullstack --module github.com/me/myapp xlgo make handler user xlgo make repository user`) } diff --git a/cmd/xlgo/templates.go b/cmd/xlgo/templates.go index f889005..9448610 100644 --- a/cmd/xlgo/templates.go +++ b/cmd/xlgo/templates.go @@ -2,8 +2,12 @@ package main // Templates 存放所有代码生成模板 var templates = struct { - Main string - Config string + Main string // api 模板:标准业务 API(mysql+redis+jwt+分层) + MainMinimal string // minimal 模板:轻量 HTTP,无外部依赖 + MainFull string // fullstack 模板:全组件 + Config string // api 模板配置 + ConfigMinimal string + ConfigFull string GoMod string Makefile string Gitignore string @@ -104,6 +108,168 @@ storage: path: ./public base_url: http://localhost:8080/public +log: + dir: ./logs + max_size: 100 + max_backups: 30 + max_age: 30 + compress: true +`, + + // MainMinimal minimal 模板:轻量 HTTP 服务,不依赖 MySQL/Redis/Storage + MainMinimal: `package main + +import ( + "flag" + "fmt" + "os" + + xlgo "github.com/EthanCodeCraft/xlgo-core" + "github.com/EthanCodeCraft/xlgo-core/middleware" + "github.com/EthanCodeCraft/xlgo-core/response" + "github.com/EthanCodeCraft/xlgo-core/router" + "github.com/gin-gonic/gin" +) + +var configPath string + +func init() { + flag.StringVar(&configPath, "config", "./config.yaml", "配置文件路径") +} + +func main() { + flag.Parse() + + app := xlgo.New( + xlgo.WithConfigPath(configPath), + xlgo.WithLogger(), + xlgo.WithHealthRoutes(), + xlgo.WithMiddlewares(middleware.Logger(), middleware.CORS()), + xlgo.WithModules(router.ModuleFunc(registerRoutes)), + ) + + if err := app.Run(); err != nil { + fmt.Printf("启动失败: %v\n", err) + os.Exit(1) + } +} + +func registerRoutes(r *gin.RouterGroup) { + api := r.Group("/api/v1") + api.GET("/", func(c *gin.Context) { + response.Success(c, gin.H{"message": "Hello {{.Name}}!"}) + }) +} +`, + + // ConfigMinimal minimal 模板配置:仅 app + server + log,无数据库/Redis + ConfigMinimal: `app: + name: "{{.Name}}" + site_name: "{{.NameLower}}" + version: "1.0.0" + env: "dev" + debug: true + base_url: "http://localhost:8080" + +server: + port: 8080 + mode: development + +log: + dir: ./logs + max_size: 100 + max_backups: 30 + max_age: 30 + compress: true +`, + + // MainFull fullstack 模板:全组件(FullStack),含 Swagger + Storage + MainFull: `package main + +import ( + "flag" + "fmt" + "os" + + xlgo "github.com/EthanCodeCraft/xlgo-core" + "github.com/EthanCodeCraft/xlgo-core/middleware" + "github.com/EthanCodeCraft/xlgo-core/response" + "github.com/EthanCodeCraft/xlgo-core/router" + "github.com/gin-gonic/gin" +) + +var configPath string + +func init() { + flag.StringVar(&configPath, "config", "./config.yaml", "配置文件路径") +} + +func main() { + flag.Parse() + + // NewFullStack 一键启用全部组件:Logger/MySQL/Redis/Storage/Wire/Health/Swagger/AutoMigrate + // 如需排除个别组件,追加对应 Without* Option,例如 xlgo.WithoutSwaggerRoutes() + app := xlgo.NewFullStack( + xlgo.WithConfigPath(configPath), + xlgo.WithMiddlewares(middleware.Logger(), middleware.CORS()), + xlgo.WithModules(router.ModuleFunc(registerRoutes)), + // xlgo.WithModels(&User{}, &Order{}), // 注册模型以启用自动迁移 + ) + + if err := app.Run(); err != nil { + fmt.Printf("启动失败: %v\n", err) + os.Exit(1) + } +} + +func registerRoutes(r *gin.RouterGroup) { + api := r.Group("/api/v1") + api.GET("/", func(c *gin.Context) { + response.Success(c, gin.H{"message": "Welcome to {{.Name}} (fullstack)!"}) + }) +} +`, + + // ConfigFull fullstack 模板配置:全组件配置 + ConfigFull: `app: + name: "{{.Name}}" + site_name: "{{.NameLower}}" + version: "1.0.0" + env: "dev" + debug: true + base_url: "http://localhost:8080" + token_expire: 86400 + +server: + port: 8080 + mode: development + +database: + driver: mysql # mysql(默认)或 postgres + host: localhost + port: 3306 + user: root + password: your_password + name: {{.NameLower}} + max_idle_conns: 10 + max_open_conns: 100 + +redis: + host: localhost + port: 6379 + password: "" + db: 0 + +jwt: + secret: your_jwt_secret_key_here + expire: 86400 + +storage: + driver: local + local: + path: ./public + base_url: http://localhost:8080/public + log: dir: ./logs max_size: 100 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..7dbebfc --- /dev/null +++ b/docs/README.md @@ -0,0 +1,21 @@ +# xlgo 文档 + +| 文档 | 位置 | 说明 | +|---|---|---| +| README.md | 仓库根 | 快速开始、功能概览、更新日志 | +| GUIDE.md | 仓库根 | 完整使用指南 | +| CHANGELOG.md | 仓库根 | 变更日志(遵循 Keep a Changelog) | +| CLAUDE.md | 仓库根 | Claude Code 协作指引 | +| docs/plans/ | 本目录 | 历史版本规划与体检报告归档 | + +## docs/plans/ 归档文档 + +这些是各版本开发时的规划/评审文档,**反映当时状态**,代码可能已演进,阅读时请对照 CHANGELOG 确认现状。 + +| 文件 | 说明 | +|---|---| +| Version_Update_Plan_v1.0.2.md | v1.0.2 更新计划(已完成) | +| Version_v1.0.2_report.md | v1.0.2 体检报告,含 #1-#30 改进建议与版本路线图 | +| v2.0-review.md | 更早期的 v2.0 评估报告 | + +> v1.0.2 体检报告中的 #1-#30 改进项,完成情况见根目录 [CHANGELOG.md](../CHANGELOG.md) 各版本章节。 diff --git a/Version_Update_Plan_v1.0.2.md b/docs/plans/Version_Update_Plan_v1.0.2.md similarity index 100% rename from Version_Update_Plan_v1.0.2.md rename to docs/plans/Version_Update_Plan_v1.0.2.md diff --git a/Version_v1.0.2_report.md b/docs/plans/Version_v1.0.2_report.md similarity index 100% rename from Version_v1.0.2_report.md rename to docs/plans/Version_v1.0.2_report.md diff --git a/docs/plans/v2.0-review.md b/docs/plans/v2.0-review.md new file mode 100644 index 0000000..3671ab8 --- /dev/null +++ b/docs/plans/v2.0-review.md @@ -0,0 +1,485 @@ +# xlgo Web 框架评估报告(v2.0 优化后版本) + +## 一、项目概述 + +xlgo 是一个基于 Go + Gin 构建的轻量级 Web 开发框架,旨在提供后端开发的基础设施。本报告基于 v2.0 版本(经过全面优化和 zl 工具库移植后)进行评估。 + +--- + +## 二、本轮优化内容汇总 + +### 2.1 新增核心包 + +| 包名 | 来源 | 函数数 | 核心功能 | +|------|------|--------|----------| +| `utils/` | zl/utils | 111 | 随机数、字符串、时间、转换、文件、URL、验证、加密、HTTP客户端、UUID | +| `console/` | zl/utils/print_*.go | 22 | 彩色控制台输出(Debug/Info/Success/Warn/Error) | +| `compress/` | zl/service/compress | 7 | Gzip/Zip 压缩解压 | +| `cache/keybuilder.go` | zl/service/cache | 新增 | 键名前缀管理,多站点共用 Redis | +| `cache/lock.go` | zl/service/cache | 新增 | 分布式锁、计数器、TTL管理 | +| `middleware/requestid.go` | zl/middleware | 2 | 请求ID追踪 | +| `middleware/recover.go` | zl/middleware | 2 | Panic恢复中间件 | +| `handler/handler.go` | zl/response | 增强 | 类型安全参数获取(QueryInt/PathInt/FormInt等) | +| `response/response.go` | zl/response | 增强 | RequestID字段、文件下载、HTML响应、跳转 | +| `config/config.go` | 新增 | AppConfig | 应用配置(SiteName/Env/Version等) | + +### 2.2 配置增强 + +新增 `AppConfig` 配置块,支持站点别名: + +```yaml +app: + name: "用户管理系统" + site_name: "user_api" # 站点别名,用于缓存键前缀、日志标识 + version: "1.0.0" + env: "prod" + debug: false + base_url: "https://user.example.com" + token_expire: 86400 +``` + +### 2.3 CLI 工具重构 + +将原来单个 `main.go`(约700行)拆分为模块化结构: + +``` +cmd/xlgo/ +├── main.go # 入口和命令路由(~50行) +├── types.go # 类型定义 +├── utils.go # 工具函数 +├── commands.go # 命令实现 +└── templates.go # 所有模板定义 +``` + +--- + +## 三、项目规模对比 + +| 指标 | v1.1.0 | v2.0 | 提升 | +|------|--------|------|------| +| Go 源文件数 | 28 | 54 | +93% | +| 代码行数 | ~2,800 | ~8,400 | +200% | +| 包数量 | 16 | 23 | +44% | +| 函数数量 | ~150 | ~450 | +200% | +| 测试覆盖 | 0% | 8% (2/24包) | +8% | utils/console已有测试 | + +--- + +## 四、模块完整性评估(v2.0) + +### 4.1 核心模块评分对比 + +| 模块 | v1.1.0 | v2.0 | 提升 | 说明 | +|------|--------|------|------|------| +| 配置管理 | 8/10 | 9/10 | +1 | 新增 AppConfig、SiteName | +| 数据库 | 8/10 | 8/10 | - | 保持稳定 | +| Redis/缓存 | 9/10 | 10/10 | +1 | 分布式锁、键名前缀、计数器 | +| JWT 认证 | 8/10 | 8/10 | - | 保持稳定 | +| 日志系统 | 9/10 | 9/10 | - | 保持稳定 | +| 中间件 | 8/10 | 9/10 | +1 | RequestID、Recover、彩色输出 | +| 文件存储 | 9/10 | 9/10 | - | 保持稳定 | +| 工具函数 | 3/10 | 9/10 | +6 | 111个实用函数 | +| 验证器 | 9/10 | 9/10 | - | 保持稳定 | +| 错误处理 | 8/10 | 8/10 | - | 保持稳定 | +| 密码加密 | 9/10 | 9/10 | - | 保持稳定 | +| SSE 支持 | 8/10 | 8/10 | - | 保持稳定 | +| WebSocket | 8/10 | 8/10 | - | 保持稳定 | +| 定时任务 | 7/10 | 7/10 | - | 保持稳定 | +| 测试支持 | 8/10 | 8/10 | - | 保持稳定 | +| CLI 工具 | 7/10 | 8/10 | +1 | 模块化重构、模板分离 | +| 压缩解压 | 0/10 | 8/10 | +8 | Gzip/Zip完整实现 | +| 控制台输出 | 0/10 | 9/10 | +9 | 跨平台彩色输出 | + +### 4.2 新增 utils 包功能清单 + +#### 4.2.1 随机数生成 (random.go) +```go +RandString(16) // 随机字符串(字母+数字) +RandDigit(6) // 随机数字字符串 +RandInt(1, 100) // 随机整数 +RandInt64(0, 1000) // 随机int64 +``` + +#### 4.2.2 字符串处理 (strings.go) +```go +IsBlank(s) // 检查空白 +IsAnyBlank(strs...) // 批量检查 +DefaultIfBlank(s, def) // 默认值 +Substr(s, 0, 10) // 子字符串(支持中文) +StrLen(s) // Unicode长度 +EqualsIgnoreCase(a, b) // 不区分大小写 +Trim(s) // 去除空白 +``` + +#### 4.2.3 时间日期 (datetime.go) +```go +NowUnix() // 秒时间戳 +NowTimestamp() // 毫秒时间戳 +FromUnix(unix) // 时间戳转时间 +FormatDateTime(t) // 格式化 "2006-01-02 15:04:05" +StartOfDay(t) // 当天开始 +EndOfDay(t) // 当天结束 +StartOfMonth(t) // 当月开始 +EndOfMonth(t) // 当月结束 +``` + +#### 4.2.4 类型转换 (convert.go) +```go +ToInt(s) // 字符串转int +ToIntDefault(s, 0) // 带默认值 +ToInt64(s) // 转int64 +ToFloat64(s) // float64 +CalcPageCount(100, 10) // 计算总页数 +CalcOffset(2, 20) // 分页偏移 +``` + +#### 4.2.5 文件操作 (file.go) +```go +FileExists(path) // 检查文件存在 +DirExists(path) // 检查目录存在 +EnsureDir(path) // 确保目录存在 +ReadFile(path) // 读取文件 +WriteFile(path, data) // 写入文件 +CopyFile(dst, src) // 复制文件 +``` + +#### 4.2.6 URL处理 (url.go) +```go +ParseURL(rawURL) // 解析URL +builder.AddQuery("key", "value") // 链式添加参数 +URLEncode(s) // URL编码 +URLDecode(s) // URL解码 +``` + +#### 4.2.7 格式验证 (validator.go) +```go +IsPhone(phone) // 手机号验证 +IsEmail(email) // 邮箱验证 +IsIPv4(ip) // IPv4验证 +IsIDCard(id) // 身份证验证 +IsChinese(s) // 中文验证 +IsNumeric(s) // 数字验证 +IsAlphanumeric(s) // 字母数字验证 +``` + +#### 4.2.8 加密编码 (crypto.go) +```go +MD5(s) // MD5哈希 +SHA256(s) // SHA256哈希 +Base64Encode(data) // Base64编码 +Base64URLEncode(data) // URL安全编码 +``` + +#### 4.2.9 HTTP客户端 (http.go) +```go +client := NewHTTPClient() +client.SetTimeout(30*time.Second) +client.SetHeader("Authorization", "Bearer xxx") +client.Get(url, params) // GET请求 +client.PostJSON(url, data) // POST JSON +client.Upload(url, files, params) // 文件上传 +``` + +#### 4.2.10 UUID生成 (uuid.go) +```go +UUID() // UUID v4字符串 +UUIDShort() // 短UUID(无横线) +UUIDValid(s) // 验证UUID +``` + +### 4.3 新增 console 包功能 + +```go +console.Debug("调试信息") // 青色 +console.Info("普通信息") // 白色 +console.Success("成功信息") // 绿色 +console.Warn("警告信息") // 黄色 +console.Error("错误信息") // 红色 + +// 自定义配置 +c := console.New( + console.WithColor(true), + console.WithTime(true), + console.WithCaller(true, 2), +) +c.Debug("自定义控制台输出") +``` + +### 4.4 新增 compress 包功能 + +```go +// Gzip 数据压缩 +GzipCompress(data) +GzipDecompress(data) + +// Gzip 文件压缩 +GzipCompressFile(src, dst) +GzipDecompressFile(src, dst) + +// Zip 打包 +Zip(zipPath, paths) // 打包文件/目录 +Unzip(zipPath, dstDir) // 解压到目录 +``` + +### 4.5 缓存键名前缀管理 + +```go +// 自动从配置读取 site_name +cache.K("user:1") // -> "cache:user_api:user:1" +cache.KTemp("token") // -> "temp:user_api:token" +cache.KPerm("config") // -> "perm:user_api:config" +cache.KLock("order:123") // -> "lock:user_api:order:123" +cache.KCounter("visit") // -> "counter:user_api:visit" +cache.KSession("sid") // -> "session:user_api:sid" + +// 分布式锁 +cache.Lock(ctx, key, ttl) +cache.Unlock(ctx, key) +cache.TryLock(ctx, key, ttl, retry, maxRetry) +cache.WithLock(ctx, key, ttl, fn) // 自动管理锁 + +// 计数器 +cache.Incr(ctx, key) +cache.Decr(ctx, key) +``` + +### 4.6 中间件增强 + +```go +// RequestID - 请求追踪 +r.Use(middleware.RequestID()) +// 响应头自动添加 X-Request-ID + +// Recover - Panic恢复 +r.Use(middleware.Recover()) // 生产环境 +r.Use(middleware.RecoverWithDetail()) // 开发环境(返回详细信息) +``` + +### 4.7 Handler 参数获取增强 + +```go +// 类型安全的参数获取 +page := handler.QueryInt(c, "page", 1) +id := handler.PathInt64(c, "id", 0) +price := handler.QueryFloat64(c, "price", 0.0) +enabled := handler.QueryBool(c, "enabled", false) +count := handler.FormInt(c, "count", 0) +``` + +--- + +## 五、多站点共用 Redis 方案 + +### 5.1 问题背景 + +多个小项目共用一台 Redis 服务器时,缓存键名可能冲突: +- 项目A: `user:1` +- 项目B: `user:1` +- 项目C: `user:1` + +### 5.2 解决方案 + +通过 `site_name` 配置自动添加前缀: + +| 项目 | config.yaml | 实际键名 | +|------|-------------|----------| +| 用户API | `site_name: user_api` | `cache:user_api:user:1` | +| 订单API | `site_name: order_api` | `cache:order_api:user:1` | +| 支付API | `site_name: pay_api` | `cache:pay_api:user:1` | + +### 5.3 使用方式 + +```yaml +# config.yaml +app: + site_name: "my_project" +``` + +```go +// 无需额外代码,自动生效 +cache.K("user:1") // -> "cache:my_project:user:1" +``` + +--- + +## 六、性能评估(v2.0) + +### 6.1 性能优化点 + +| 优化项 | 说明 | +|--------|------| +| RandString/RandDigit | 使用 sync.Pool 复用 rand.Source | +| StringToBytes/BytesToString | 零拷贝转换 | +| HTTPClient | 链式配置,连接复用 | +| 缓存键名 | 预构建,避免重复拼接 | + +### 6.2 性能基准估算 + +| 场景 | 预估 QPS | 说明 | +|------|----------|------| +| 简单查询 API | 12,000-18,000 | 无 DB 查询 | +| 带 DB 查询 | 3,000-5,000 | 单表主键查询 | +| 缓存查询 | 10,000-15,000 | Redis 缓存命中 | +| SSE 流式响应 | 5,000-8,000 | 单连接 | +| WebSocket 连接 | 10,000+ | 连接数 | +| 文件压缩 | 50-100 MB/s | Gzip | +| HTTP请求 | 1,000-5,000 | 外部API调用 | + +--- + +## 七、扩展性评估(v2.0) + +| 方面 | v1.1.0 | v2.0 | 说明 | +|------|--------|------|------| +| 存储扩展 | 本地 + OSS | 本地 + OSS | 保持 | +| 缓存扩展 | 基础操作 | 分布式锁、计数器、前缀 | +3功能 | +| 验证扩展 | 自定义规则 | 手机/邮箱/IP等预定义 | +7规则 | +| 通信扩展 | HTTP + SSE + WS | 保持 | - | +| 工具扩展 | 无 | 111个函数 | +111 | +| 压缩扩展 | 无 | Gzip + Zip | +2 | +| 控制台扩展 | 无 | 彩色输出 | +1 | + +--- + +## 八、易用性评估(v2.0) + +### 8.1 开发效率提升 + +| 任务 | v1.1.0 | v2.0 | 节省 | +|------|--------|------|------| +| 创建新项目 | 5 分钟 | 5 分钟 | - | +| 添加 CRUD 模块 | 10 分钟 | 10 分钟 | - | +| 请求验证 | 5 分钟 | 5 分钟 | - | +| 参数类型转换 | 手动编写 | 调用 handler.QueryInt | 90% | +| 文件压缩 | 手动实现 | 调用 compress.Zip | 100% | +| 随机字符串 | 手动实现 | 调用 utils.RandString | 100% | +| 时间格式化 | 记忆布局 | 调用 utils.FormatDateTime | 80% | +| 分布式锁 | 手动实现 | 调用 cache.Lock | 100% | +| HTTP请求 | 手动封装 | 调用 NewHTTPClient().Get | 100% | +| 控制台调试 | fmt.Println | console.Debug(彩色) | 50% | + +### 8.2 代码简化示例 + +```go +// v1.1.0 - 手动实现 +func GetUser(c *gin.Context) { + pageStr := c.Query("page") + page := 1 + if pageStr != "" { + p, err := strconv.Atoi(pageStr) + if err == nil { + page = p + } + } + // ... +} + +// v2.0 - 一行搞定 +func GetUser(c *gin.Context) { + page := handler.QueryInt(c, "page", 1) + // ... +} +``` + +--- + +## 九、安全性评估(v2.0) + +| 检查项 | v1.1.0 | v2.0 | 说明 | +|--------|--------|------|------| +| SQL 注入 | ✅ GORM | ✅ GORM | 保持 | +| XSS | ⚠️ 应用层 | ⚠️ 应用层 | 保持 | +| CSRF | ❌ 无 | ✅ 完善 | +1 | 多种模式:Cookie/API/DoubleSubmit | +| 密码加密 | ✅ bcrypt | ✅ bcrypt | 保持 | +| 请求验证 | ✅ validator | ✅ validator | 保持 | +| 限流防护 | ✅ 完善 | ✅ 完善 | 保持 | +| JWT 安全 | ✅ 黑名单 | ✅ 黑名单 | 保持 | +| Panic恢复 | ❌ 无 | ✅ Recover中间件 | +1 | +| 请求追踪 | ❌ 无 | ✅ RequestID | +1 | +| 类型安全 | ⚠️ 手动 | ✅ 强类型函数 | +1 | + +--- + +## 十、综合评分(v2.0) + +| 维度 | v1.1.0 | v2.0 | 提升 | +|------|--------|------|------| +| 完整性 | 8.5/10 | 9.5/10 | +1.0 | +| 性能 | 8.5/10 | 9.0/10 | +0.5 | +| 扩展性 | 7.5/10 | 9.0/10 | +1.5 | +| 易用性 | 9.0/10 | 9.5/10 | +0.5 | +| 安全性 | 8.0/10 | 9.0/10 | +1.0 | +| **总分** | **8.35/10** | **9.2/10** | **+0.85** | + +--- + +## 十一、剩余改进建议 + +### 11.1 P3 - 下一步优化 + +| 功能 | 优先级 | 说明 | +|------|--------|------| +| Kafka 生产者 | 低 | 需重新设计连接池 | +| WebSocket 客户端 | 低 | 框架已有服务端实现 | +| 链路追踪 | 中 | OpenTelemetry集成 | +| 单元测试 | 高 | 进行中,覆盖所有包 | +| 性能基准 | 中 | 建立benchmark | + +### 11.2 测试建议 + +```bash +# 运行现有测试 +go test ./utils/... ./console/... -v + +# 建议添加 +go test ./cache/... ./handler/... ./middleware/... +``` + +--- + +## 十二、结论 + +经过本轮全面优化,xlgo 框架已从 **生产就绪** 提升至 **功能丰富** 水平。 + +### 12.1 核心优势 + +1. **工具库完善** - 111个实用函数,覆盖日常开发80%场景 +2. **多站点支持** - SiteName配置解决共用Redis冲突 +3. **类型安全** - QueryInt/PathInt等避免手动转换 +4. **开发调试** - 彩色控制台输出,一眼识别日志级别 +5. **分布式支持** - Redis分布式锁开箱即用 +6. **文件处理** - Gzip/Zip压缩解压 + +### 12.2 适用场景 + +- ✅ 中大型 API 服务 +- ✅ 用户管理系统 +- ✅ AI 应用后端 +- ✅ 实时通信应用 +- ✅ 多项目共用资源 +- ✅ 创业项目 MVP +- ✅ 学习 Go Web 开发 + +### 12.3 开发效率总结 + +| 场景 | 使用 xlgo v2.0 | 不使用框架 | 节省 | +|------|----------------|------------|------| +| 用户系统 | 4-5 人天 | 10-16 人天 | 60% | +| AI应用 | 4-5 人天 | 9-14 人天 | 55% | +| 通用API | 2-3 人天 | 5-8 人天 | 60% | + +--- + +## 十三、版本历史 + +| 版本 | 日期 | 主要变化 | +|------|------|----------| +| v1.0.0 | 2026-04-29 | 基础框架 | +| v1.1.0 | 2026-04-29 | P0/P1/P2修复(12项) | +| v2.0.0 | 2026-04-29 | zl工具库移植、模块重构 | + +--- + +*评估日期:2026-04-29* +*评估版本:v2.0.0* +*优化内容:新增5个包 + 增强5个包 + 配置扩展 + CLI重构 = 约120个函数* \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..90ac76c --- /dev/null +++ b/examples/README.md @@ -0,0 +1,42 @@ +# xlgo 示例 + +两个可运行示例,帮助快速上手 xlgo。 + +## minimal — 最小 HTTP 服务 + +不依赖 MySQL / Redis / Storage,纯 HTTP + 健康检查。第一次接触 xlgo 从这里开始。 + +```bash +go run ./examples/minimal +``` + +访问 http://localhost:8081/health(健康检查)与 http://localhost:8081/api/v1/(示例路由)。 + +## full — 完整业务 API + +包含 MySQL + Redis + JWT + 一个 user CRUD(登录发 token、认证路由、创建/查询用户)。 + +**运行前需准备**: +- MySQL(修改 `examples/full/config.yaml` 的 `database` 配置) +- Redis(修改 `examples/full/config.yaml` 的 `redis` 配置) + +```bash +go run ./examples/full +``` + +启动后自动迁移 user 表。接口: + +| 方法 | 路径 | 说明 | 认证 | +|---|---|---|---| +| POST | /api/v1/login | 登录,返回 token | 否 | +| GET | /api/v1/users/:id | 查询用户 | 是(Bearer token) | +| POST | /api/v1/users | 创建用户 | 是(Bearer token) | + +登录示例: +```bash +curl -X POST http://localhost:8082/api/v1/login \ + -H "Content-Type: application/json" \ + -d '{"username":"alice","password":"secret"}' +``` + +> 示例代码为演示用途,密码明文存储、未做参数校验,生产环境请使用 bcrypt 哈希密码并配合 `validation` 包校验入参。 diff --git a/examples/full/config.yaml b/examples/full/config.yaml new file mode 100644 index 0000000..148ab3c --- /dev/null +++ b/examples/full/config.yaml @@ -0,0 +1,35 @@ +app: + name: "xlgo-full-example" + env: "dev" + debug: true + +server: + port: 8082 + mode: development + +database: + driver: mysql + host: localhost + port: 3306 + user: root + password: your_password + name: xlgo_example + max_idle_conns: 10 + max_open_conns: 100 + +redis: + host: localhost + port: 6379 + password: "" + db: 0 + +jwt: + secret: change-me-to-a-long-random-secret-at-least-32-chars + expire: 86400 + +log: + dir: ./logs + max_size: 100 + max_backups: 30 + max_age: 30 + compress: true diff --git a/examples/full/main.go b/examples/full/main.go new file mode 100644 index 0000000..d1fddc0 --- /dev/null +++ b/examples/full/main.go @@ -0,0 +1,125 @@ +// Package main 是 xlgo 的完整示例:MySQL + Redis + JWT + 一个 user CRUD。 +// +// 运行前需准备: +// - MySQL(config.yaml 中 database 配置) +// - Redis(config.yaml 中 redis 配置) +// +// 启动后会自动迁移 user 表。访问: +// +// POST /api/v1/login {"username":"alice","password":"secret"} → 返回 token +// GET /api/v1/users/:id (需 Authorization: Bearer ) +// POST /api/v1/users (创建用户) +// +// 运行: +// +// go run ./examples/full +package main + +import ( + "fmt" + "os" + + xlgo "github.com/EthanCodeCraft/xlgo-core" + "github.com/EthanCodeCraft/xlgo-core/database" + "github.com/EthanCodeCraft/xlgo-core/jwt" + "github.com/EthanCodeCraft/xlgo-core/middleware" + "github.com/EthanCodeCraft/xlgo-core/repository" + "github.com/EthanCodeCraft/xlgo-core/response" + "github.com/EthanCodeCraft/xlgo-core/router" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +// User 示例模型 +type User struct { + gorm.Model + Username string `gorm:"uniqueIndex;size:64" json:"username"` + Password string `gorm:"size:128" json:"-"` // 实际项目应存 bcrypt 哈希 + UserType string `gorm:"size:32" json:"user_type"` +} + +var userRepo *repository.BaseRepo[User] + +func main() { + app := xlgo.NewFullStack( + xlgo.WithConfigPath("./examples/full/config.yaml"), + xlgo.WithModels(&User{}), + xlgo.WithMiddlewares(middleware.Logger(), middleware.CORS()), + xlgo.WithModules(router.ModuleFunc(registerRoutes)), + ) + + // 初始化 user repository(App.Init 之后 master DB 才可用,这里在 registerRoutes 里延迟拿) + _ = app + + if err := app.Run(); err != nil { + fmt.Printf("启动失败: %v\n", err) + os.Exit(1) + } +} + +func registerRoutes(r *gin.RouterGroup) { + // 延迟初始化 repo:此时 App.Init 已完成,database.GetDB() 可用 + userRepo = repository.NewBaseRepo[User](database.GetDB()) + + api := r.Group("/api/v1") + + // 公开路由:登录 + api.POST("/login", login) + + // 认证路由 + auth := api.Group("/", middleware.AuthRequired()) + auth.GET("/users/:id", getUser) + auth.POST("/users", createUser) +} + +func login(c *gin.Context) { + var req struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + response.Fail(c, "参数错误") + return + } + + // 示例简化:查询用户,不校验密码哈希 + u, err := userRepo.FindOne(c.Request.Context(), "username = ?", req.Username) + if err != nil { + response.Fail(c, "用户不存在") + return + } + + token, err := jwt.GenerateToken(u.ID, u.Username, "user", u.UserType) + if err != nil { + response.ServerError(c, "生成 token 失败") + return + } + response.Success(c, gin.H{"token": token}) +} + +func getUser(c *gin.Context) { + id := c.Param("id") + // 示例简化:直接用 repo 查询,实际应转 uint + var uid uint + fmt.Sscanf(id, "%d", &uid) + u, err := userRepo.FindByID(c.Request.Context(), uid) + if err != nil { + response.NotFound(c, "用户不存在") + return + } + response.Success(c, u) +} + +func createUser(c *gin.Context) { + var u User + if err := c.ShouldBindJSON(&u); err != nil { + response.Fail(c, "参数错误") + return + } + u.UserType = "user" + if err := userRepo.Create(c.Request.Context(), &u); err != nil { + response.Fail(c, "创建失败: "+err.Error()) + return + } + response.Success(c, u) +} diff --git a/examples/minimal/config.yaml b/examples/minimal/config.yaml new file mode 100644 index 0000000..104edf1 --- /dev/null +++ b/examples/minimal/config.yaml @@ -0,0 +1,8 @@ +app: + name: "xlgo-minimal-example" + env: "dev" + debug: true + +server: + port: 8081 + mode: development diff --git a/examples/minimal/main.go b/examples/minimal/main.go new file mode 100644 index 0000000..31c81ff --- /dev/null +++ b/examples/minimal/main.go @@ -0,0 +1,42 @@ +// Package main 是 xlgo 的最小可运行示例。 +// +// 仅依赖一个 config.yaml,不初始化 MySQL / Redis / Storage, +// 适合第一次接触 xlgo、或纯 HTTP 场景。 +// +// 运行: +// +// go run ./examples/minimal +package main + +import ( + "fmt" + "os" + + xlgo "github.com/EthanCodeCraft/xlgo-core" + "github.com/EthanCodeCraft/xlgo-core/middleware" + "github.com/EthanCodeCraft/xlgo-core/response" + "github.com/EthanCodeCraft/xlgo-core/router" + "github.com/gin-gonic/gin" +) + +func main() { + app := xlgo.New( + xlgo.WithConfigPath("./examples/minimal/config.yaml"), + xlgo.WithLogger(), + xlgo.WithHealthRoutes(), + xlgo.WithMiddlewares(middleware.Logger(), middleware.CORS()), + xlgo.WithModules(router.ModuleFunc(registerRoutes)), + ) + + if err := app.Run(); err != nil { + fmt.Printf("启动失败: %v\n", err) + os.Exit(1) + } +} + +func registerRoutes(r *gin.RouterGroup) { + api := r.Group("/api/v1") + api.GET("/", func(c *gin.Context) { + response.Success(c, gin.H{"message": "Hello xlgo!"}) + }) +}