Files
go-common/INTEGRATION.md

260 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# 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)
// i18nlocales 目录;出参经 NewHandler 自动用)
app.MustI18n().GetMessage("zh-CN", "user.not_found")
// tools不经 Factory
tools.Now()
tools.MD5("text")
```
### MCP 工具Model Context ProtocolClient
本库只做 **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)。