10 KiB
GoCommon 业务项目对接操作手册
本文档供引用 GoCommon 的业务项目使用。按本手册对接即可,无需重复沟通基础设施实现方式。
模块路径:git.toowon.com/jimmy/go-common
本文档只描述目标架构,重构时一步到位,不提供过渡期用法,不考虑向后兼容。
1. 对接原则
| 原则 | 说明 |
|---|---|
| Factory 只是入口 | 启动时初始化一次,通过 getter 获取各模块对象 |
| 能力在模块自身 | 使用 log.Info()、db.Find()、store.Upload(),不在 Factory 上堆透传方法 |
| 按需引用 | 用什么模块取什么对象;无状态工具直接 import tools |
| 不重写基础设施 | 数据库连接、Redis 客户端、统一 HTTP 出参、中间件链由 GoCommon 提供 |
| 业务只管业务 | Service 返回数据;Handler 交给 http.Handler 出参;Migration 只写 SQL 文件 |
2. 环境准备
2.1 配置私有模块
go env -w GOPRIVATE=git.toowon.com
go env -w GONOPROXY=git.toowon.com
go env -w GONOSUMDB=git.toowon.com
2.2 Git 认证(SSH 推荐)
git config --global url."git@git.toowon.com:".insteadOf "https://git.toowon.com/"
2.3 安装依赖
go get git.toowon.com/jimmy/go-common@v2.0.0
在 go.mod 中:
require git.toowon.com/jimmy/go-common v2.0.0
配置示例见 config/example.json。
3. 推荐项目结构
your-project/
├── config.json
├── cmd/
│ ├── server/main.go
│ └── migrate/main.go # 从 templates/migrate/main.go 复制
├── migrations/
│ └── 20240101000001_create_users.sql
├── locales/ # i18n(可选)
│ ├── zh-CN.json
│ └── en-US.json
├── internal/
│ ├── handler/
│ └── service/
└── go.mod
4. 启动初始化(只做一次)
package main
import (
"log"
"net/http"
"git.toowon.com/jimmy/go-common/factory"
)
func main() {
if err := factory.Init("config.json"); err != nil {
log.Fatal(err)
}
app := factory.Default()
chain := app.MiddlewareChain()
chain.Append(yourAuthMiddleware)
userSvc, err := NewUserService(app)
if err != nil {
log.Fatal(err)
}
http.Handle("/api/users", chain.ThenFunc(func(w http.ResponseWriter, r *http.Request) {
listUsers(w, r, userSvc, app)
}))
log.Fatal(http.ListenAndServe(":8080", nil))
}
禁止在每个 Handler 里 NewFromFile / Init。全局只初始化一次,通过 factory.Default() 或注入 *factory.Factory。
5. 获取模块对象(Factory getter)
连接与客户端由 Factory lazy 初始化并缓存。业务侧不要自行 gorm.Open / redis.NewClient。
| 模块 | API | 用法 |
|---|---|---|
| 配置 | app.Config() |
读原始配置 |
| 数据库 | app.Database() |
db.Find(&users)、db.Transaction(...) |
| Redis | app.Redis() |
rds.Set(ctx, key, val, ttl) |
| 日志 | app.Logger() |
log.Info(...),退出前 log.Close() |
| 存储 | app.Storage() |
store.Upload(ctx, key, reader) |
| 邮件 | app.Email() |
mail.SendEmail(...) |
| 短信 | app.SMS() |
sms.SendSMS(...) |
| Excel | app.Excel() |
ex.ExportToFile(...) |
| 国际化 | app.I18n() |
供 http.Handler 注入;或 i18n.GetMessage(...) |
| 中间件链 | app.MiddlewareChain() |
chain.Append(...).ThenFunc(...) |
| 迁移 | app.Migrator("migrations") |
m.Up() / m.Status() / m.Down() |
注入 Service 示例
type UserService struct {
db *gorm.DB
rds *redis.Client
}
func NewUserService(app *factory.Factory) (*UserService, error) {
db, err := app.Database()
if err != nil {
return nil, err
}
rds, err := app.Redis()
if err != nil {
return nil, err
}
return &UserService{db: db, rds: rds}, nil
}
func (s *UserService) List(page, size int) (users []User, total int64, err error) {
s.db.Model(&User{}).Count(&total)
err = s.db.Offset((page-1)*size).Limit(size).Find(&users).Error
return
}
config.json 未配置的模块,调用 getter 会返回错误——只配置使用的模块即可。
6. HTTP 统一出参
6.1 职责划分
| 层级 | 做什么 |
|---|---|
| Service | 返回 []User、total、业务 error |
| Handler | 解析请求、调 Service、通过 http.Handler 出参 |
http.Handler |
PageData → Response → JSON(结构 / 编码 / 语种 / 时间统一) |
禁止在 Service 拼 JSON,禁止在业务项目自定义 Response 结构,禁止经 Factory 出参(无 app.Success / app.Error)。
6.2 标准响应格式
{
"code": 0,
"message": "success",
"timestamp": 1704067200,
"data": {}
}
分页时 data:
{
"list": [],
"total": 100,
"page": 1,
"pageSize": 20
}
类型定义在 http 包:http.Response、http.PageData。
6.3 Handler 用法(唯一方式)
中间件链须包含 Language、Timezone(MiddlewareChain() 已默认组装)。
import commonhttp "git.toowon.com/jimmy/go-common/http"
func listUsers(w http.ResponseWriter, r *http.Request, svc *UserService, app *factory.Factory) {
i18n, _ := app.I18n()
h := commonhttp.NewHandler(w, r, commonhttp.WithI18n(i18n))
var req ListUserRequest
if err := h.ParseJSON(&req); err != nil {
h.Error("common.invalid_request")
return
}
p := h.Pagination()
users, total, err := svc.List(p.GetPage(), p.GetPageSize())
if err != nil {
h.Error("user.list_failed")
return
}
h.SuccessPage(users, total)
}
http.Handler 统一负责:
Content-Type: application/json; charset=utf-8- 从 context 读取语种、时区
- 消息码(如
user.not_found)经 i18n 转为文案与业务 code timestamp按统一时区策略写入
6.4 请求头约定
| Header | 用途 |
|---|---|
Accept-Language |
响应消息语种 |
X-Timezone |
时区(默认 Asia/Shanghai) |
7. 数据库迁移
执行框架已封装,业务只写 SQL 文件。
7.1 推荐:独立 migrate 命令(与 Web 解耦)
cp templates/migrate/main.go cmd/migrate/main.go
go build -o bin/migrate cmd/migrate/main.go
./bin/migrate up
./bin/migrate status
./bin/migrate down
./bin/migrate up -config config.json -dir migrations
-- 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;
独立 CLI 内部调用 migration.RunMigrationsFromConfigWithCommand,无需业务实现连接逻辑。
7.2 可选:经 Factory(开发 / 小项目)
m, err := app.Migrator("migrations")
if err != nil {
log.Fatal(err)
}
if err := m.Up(); err != nil {
log.Fatal(err)
}
7.3 Docker
command: sh -c "./bin/migrate up && ./bin/server"
volumes:
- ./config.json:/app/config.json:ro
- ./migrations:/app/migrations:ro
8. 各模块按需对接
8.1 日志
log, _ := app.Logger()
defer log.Close()
log.Info("服务启动")
8.2 存储
store, _ := app.Storage()
store.Upload(ctx, "images/a.jpg", fileReader, "image/jpeg")
url, _ := store.GetURL("images/a.jpg", 3600)
不经 Factory 时:storage.NewStorage(storage.StorageTypeLocal, cfg)。
8.3 邮件 / 短信
mail, _ := app.Email()
defer mail.Close()
// HTTP 通知类:异步,不阻塞请求
mail.SendEmailAsync(r.Context(), []string{"a@b.com"}, "主题", "正文")
// 验证码等需等待结果:同步
mail.SendEmail([]string{"a@b.com"}, "主题", "正文")
sms, _ := app.SMS()
defer sms.Close()
sms.SendSMSAsync(r.Context(), []string{"13800138000"}, map[string]string{"code": "123456"})
sms.SendSMS([]string{"13800138000"}, map[string]string{"code": "123456"})
8.4 Excel
ex := app.Excel()
ex.ExportToFile("users.xlsx", "用户列表", columns, users)
8.5 国际化
i18n, _ := app.I18n()
i18n.LoadFromDir("locales")
msg := i18n.GetMessage("zh-CN", "user.not_found")
HTTP 出参的 i18n 通过 NewHandler(..., WithI18n(i18n)) 注入,Handler 内用消息码调用 h.Error("user.not_found")。
8.6 无状态工具(不经 Factory)
import "git.toowon.com/jimmy/go-common/tools"
tools.Now()
tools.MD5("text")
tools.YuanToCents(100.5)
9. config.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。
10. 反模式(禁止)
- 每个 Handler 内
factory.Init/NewFromFile - 业务 Service 内写 HTTP JSON 响应
- 自行
gorm.Open/redis.NewClient - 使用 Factory 透传:
LogInfo、RedisSet、Success、Error、Now、MD5等 - 直接调用
http.Success(w, ...)包级函数(应使用http.Handler) - 在业务项目复制 GoCommon 的 Response / 中间件实现
11. 故障排除
无法下载模块:
go env -w GOPRIVATE=git.toowon.com
git config --global url."git@git.toowon.com:".insteadOf "https://git.toowon.com/"
go get git.toowon.com/jimmy/go-common@latest
依赖错误: go clean -modcache && go mod tidy
getter 报 config is nil: 补充 config.json 对应段,或不调用该 getter
迁移找不到文件: 检查 -dir 与 SQL 文件命名
12. 文档与版本
| 文档 | 用途 |
|---|---|
| 本文档 | 业务项目对接 |
| README.md | 库概述 |
| VERSION.md | 版本发布 |
版本升级见 VERSION.md。