# GoCommon 业务项目对接手册 模块路径:`git.toowon.com/jimmy/go-common` > 目标架构一步到位,不考虑向后兼容。 --- ## 1. 原则 | 原则 | 说明 | |------|------| | Factory 只是入口 | 启动初始化一次,getter 取模块对象 | | 能力在模块自身 | `log.Info()`、`db.Find()`、`store.Upload()`,不在 Factory 透传 | | 按需配置 | 只用到的模块写进 `config.json` | | 不重写基础设施 | 连接池、HTTP 出参、中间件链由本库提供 | | 业务只管业务 | Service 返回数据;出参用 `http.Handler`;迁移只写 SQL | 登录鉴权、参数校验、出站 HTTP Client 等由**业务项目自行实现**,不在本库范围。 --- ## 2. 安装 ```bash go env -w GOPRIVATE=git.toowon.com go env -w GONOPROXY=git.toowon.com go env -w GONOSUMDB=git.toowon.com git config --global url."git@git.toowon.com:".insteadOf "https://git.toowon.com/" go get git.toowon.com/jimmy/go-common@v1.2.0 ``` 配置示例:[`config/example.json`](./config/example.json)。 --- ## 3. 推荐结构 ```text your-project/ ├── config.json ├── cmd/server/main.go # 复制 templates/server/main.go ├── cmd/migrate/main.go # 复制 templates/migrate/main.go ├── migrations/ ├── locales/ # 可选 ├── internal/handler/ ├── internal/service/ └── go.mod ``` --- ## 4. 启动(只做一次) ```bash cp templates/server/main.go cmd/server/main.go ``` 模板默认已包含:`MustInit` + `buildOptions`(Option 挂载点)+ `MustLogger` + `Warmup` + `MiddlewareChain` + `NewHandler` + `Close`。 复制后主要改三处: | 函数 | 用途 | |------|------| | `buildOptions()` | `WithLogger` / `WithStorage` 等自定义注入 | | `setupMiddleware()` | `chain.Append(业务鉴权…)` | | `registerRoutes()` | 注册业务路由 | ```bash go run ./cmd/server -config config.json -addr :8080 ``` --- ## 5. 获取模块对象 连接由 Factory lazy 缓存。业务侧不要自行 `gorm.Open` / `redis.NewClient`。 | 模块 | API | 说明 | |------|-----|------| | 配置 | `Config()` | 原始配置 | | 数据库 | `Database()` / `MustDatabase()` | `*gorm.DB` | | Redis | `Redis()` / `MustRedis()` | `*redis.Client` | | 日志 | `Logger()` / `MustLogger()` | 退出用 `Close()` | | 存储 | `Storage()` / `MustStorage()` | Upload / GetURL | | 邮件 | `Email()` / `MustEmail()` | SendEmail / SendEmailAsync | | 短信 | `SMS()` / `MustSMS()` | SendSMS / SendSMSAsync | | Excel | `Excel()` | 每次新建导出器 | | 国际化 | `I18n()` / `MustI18n()` | 一般由 `NewHandler` 注入 | | MCP 工具 | `MCP()` / `MustMCP()` | `ListTools` / `CallTool`(见 §8) | | 图形验证码 | `Captcha()` / `MustCaptcha()` | `Generate` / `Verify` / 登录防暴力(见 §8) | | HTTP 出参 | `NewHandler(w, r)` | `Success` / `Error` / `ErrorData` | | 中间件 | `MiddlewareChain()` | `Append` / `ThenFunc` | | 迁移 | `Migrator(dir)` | Up / Down / Status | | 生命周期 | `Close()` / `Warmup(...)` | 收口 / 启动预热 | `MustXxx` 仅用于 **main / 启动注入**(失败 panic)。 ```go func NewUserService(app *factory.Factory) *UserService { return &UserService{db: app.MustDatabase(), rds: app.MustRedis()} } ``` --- ## 6. HTTP 统一出参 | 层级 | 职责 | |------|------| | Service | 返回数据或 error | | Handler | 解析、调 Service、`h.Success` / `h.Error` | | `http.Handler` | 统一 JSON 信封、i18n、timestamp | 响应格式(HTTP 恒 200): ```json { "code": 0, "message": "success", "timestamp": 1704067200, "data": {} } ``` 分页 `data`:`{ "list", "total", "page", "pageSize" }`。 ```go h := app.NewHandler(w, r) // 自动注入已配置的 i18n var req ListUserRequest if err := h.ParseJSON(&req); err != nil { h.Error("common.invalid_request") return } users, total, err := svc.List(h.Pagination().GetPage(), h.Pagination().GetPageSize()) if err != nil { h.Error("user.list_failed") return } h.SuccessPage(users, total) ``` 请求头:`Accept-Language`(语种)、`X-Timezone`(默认 `Asia/Shanghai`)。 --- ## 7. 数据库迁移 ```bash cp templates/migrate/main.go cmd/migrate/main.go go build -o bin/migrate cmd/migrate/main.go ./bin/migrate up|status|down ``` 模板默认走 Factory:`MustInit` + 日志 + `Warmup(Database)` + `Migrator` + `Close`;同样可用 `buildOptions()` 注入自定义 DB/Logger。 ```sql -- migrations/20240101000001_create_users.sql CREATE TABLE users (id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(255) NOT NULL); -- migrations/20240101000001_create_users.down.sql DROP TABLE IF EXISTS users; ``` --- ## 8. 模块速查 ```go // 日志 app.MustLogger().Info("服务启动", nil) // 存储 store, _ := app.Storage() _ = store.Upload(ctx, "images/a.jpg", reader, "image/jpeg") // 邮件 / 短信(HTTP 通知用 Async;验证码用同步) app.MustEmail().SendEmailAsync(ctx, []string{"a@b.com"}, "主题", "正文") app.MustSMS().SendSMSAsync(ctx, []string{"13800138000"}, map[string]string{"code": "123456"}) // Excel app.Excel().ExportToFile("users.xlsx", "用户列表", columns, users) // i18n(locales 目录;出参经 NewHandler 自动用) app.MustI18n().GetMessage("zh-CN", "user.not_found") // tools(不经 Factory) tools.Now() tools.MD5("text") ``` ### MCP 工具(Model Context Protocol)Client 本库只做 **MCP Client**:按配置连接外部 MCP server(容器/服务),发现工具、调用工具; **不含路由/抽参/编排逻辑**——用哪个工具、怎么填参数、怎么组合答复,属于业务侧的 Prompt/Agent 编排,需要业务项目自行实现(可参考已有 RAG 项目里的 ToolOrchestrator 写法)。 ```go m := app.MustMCP() // config.mcp 未配置或 enabled=false 时 m.Enabled() 为 false tools, _ := m.ListTools(ctx) // 聚合全部已启用 server 的工具;FQName = "{toolNamePrefix}__{原始工具名}" res, _ := m.CallTool(ctx, "word__create_document", map[string]any{"title": "周报"}) // 可选:把 HTTP 层的 request id 传入,调用日志会附带该字段(不传不影响功能) ctx = mcp.WithRequestID(ctx, requestID) ``` `config.json` 中 `mcp.servers[]` 一条 = 一个独立 MCP server(自带多个工具),新增能力 = 加一条配置,不改代码;`allowedTools` 留空表示开放该 server 全部工具。完整字段见 [`config/example.json`](./config/example.json) 的 `mcp` 段。 ### 图形验证码(Captcha) 可选模块:`config.captcha.enabled=false`(或未配置)时 `Captcha().Enabled()` 为 false,**不影响 middleware**;业务 handler 按需调用即可。 ```go cap := app.MustCaptcha() // enabled=true 时需配置 redis // 1. 生成(注册/改手机号等发短信前) result, _ := cap.Generate(ctx, captcha.SceneRegister) // 返回 result.ID + result.ImageBase64 给前端展示 // 2. 校验(一次性消费) if err := cap.Verify(ctx, captcha.VerifyRequest{ Scene: captcha.SceneRegister, ID: captchaID, Answer: userInput, }); err != nil { h.Error("captcha.invalid") return } // 验证通过后再 app.MustSMS().SendSMS(...) // 3. 登录防暴力(失败 N 次后要求验证码,M 次后锁定) key := username // 或 username+IP,由业务决定 if need, _ := cap.LoginNeedCaptcha(ctx, key); need { // 要求前端传 captchaId + captchaCode,并 Verify } status, err := cap.RecordLoginFailure(ctx, key) if status.Locked { h.Error("captcha.login_locked"); return } // 登录成功后 _ = cap.ClearLoginFailures(ctx, key) ``` 场景常量:`SceneRegister` / `SceneChangePhone` / `SceneLogin` / `SceneSendSMS`。 `scenes.register.mode=always` 表示该场景始终要求图形验证码;登录场景由 `login.showAfterFailures` 控制「失败后才要求」。 完整配置见 [`config/example.json`](./config/example.json) 的 `captcha` 段。 --- ## 9. 最小 config.json ```json { "database": { "type": "mysql", "host": "localhost", "port": 3306, "user": "root", "password": "password", "database": "mydb" }, "logger": { "level": "info", "output": "both", "filePath": "./logs/app.log", "async": true }, "i18n": { "defaultLang": "zh-CN", "localesDir": "locales" } } ``` 完整字段见 [`config/example.json`](./config/example.json)。 --- ## 10. 反模式 - Handler 内重复 `Init` - Service 拼 HTTP JSON / 自定义 Response - 自行 `gorm.Open` / `redis.NewClient` - Factory 透传(`LogInfo`、`Success`、`Now` 等) - 请求路径滥用 `MustXxx` - 把 MCP 工具路由/抽参/Prompt 编排逻辑塞进本库(应在业务侧基于 `mcp.Manager` 自行实现) --- ## 11. 故障排除 | 问题 | 处理 | |------|------| | 无法下载模块 | 检查 `GOPRIVATE` 与 SSH insteadOf | | 依赖异常 | `go clean -modcache && go mod tidy` | | getter 报 config is nil | 补配置段,或不调用该模块 | | 迁移找不到文件 | 检查 `-dir` 与 SQL 命名 | 版本发布见 [VERSION.md](./VERSION.md)。