260 lines
7.4 KiB
Markdown
260 lines
7.4 KiB
Markdown
# 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) |
|
||
| 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` 段。
|
||
|
||
---
|
||
|
||
## 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)。
|