479 lines
28 KiB
Markdown
479 lines
28 KiB
Markdown
# GoOmni
|
||
|
||
GoOmni 是面向多个业务项目复用的 Go 后端框架:用 `.api` 定义接口、请求和返回类型,生成项目目录、HTTP 接入代码及 OpenAPI,业务代码负责 Logic 和 Repository。也可以在已有项目中直接使用 `omni.New()`,或单独引入日志、存储等模块。
|
||
|
||
**从这份 README 开始接入。** 当前版本 **`v0.15.0-preview`**,新生成项目采用根目录入口和 `config / svc / logic / handler`。可以固定版本开展业务接入;文件维护边界和已有服务迁移步骤见 [项目指南](PROJECT.md),版本变化与验收见 [发布说明](RELEASE.md)。
|
||
|
||
Go **1.22+**。框架不依赖 Gin,默认使用标准库网络层,提供可切换的 native HTTP/1.1 和实验性 HTTP/2 适配器。
|
||
|
||
响应字段、业务码、登录方式、权限规则和数据库表结构由接入项目决定。文中的 `code/msg/data`、`200/9999` 都是示例配置,可以替换。
|
||
|
||
新增通用开发能力见 [业务开发指南](DEVELOPMENT.md):数组/null、严格请求策略、声明式上传、完整配置环境覆盖、资源生命周期、共享契约和升级工具。MySQL 直接使用 [官方适配器](database/mysql/README.md)。
|
||
|
||
后台组件可在 svc 登记 [分阶段关闭](lifecycle/README.md#后台组件分阶段关闭),先停止接收工作并等待收尾,再关闭共享资源;无需绑定队列或 ORM。
|
||
|
||
使用 GORM 的项目可以选择 [gormx 适配器](database/gormx/README.md),复用数据库初始化和连接池,并将 SQL 日志接入 logx。
|
||
|
||
## 选择接入方式
|
||
|
||
| 你的项目 | 从哪里开始 |
|
||
| --- | --- |
|
||
| 新建服务,先定义接口再开发 | [新项目:生成并运行](#新项目生成并运行) |
|
||
| 已有服务,逐步接入 HTTP 或基础模块 | [已有项目:按模块接入](#已有项目按模块接入) |
|
||
| 已有标准 Handler、动态 JSON、上传或自定义 ID 类型 | [迁移扩展指南](MIGRATION.md) |
|
||
| 想统一返回体、业务错误或三方回调 | [配置公共响应与独立协议](#配置公共响应与独立协议) |
|
||
| 要接日志、数据库、鉴权和存储 | [业务开发与依赖装配](#业务开发与依赖装配) |
|
||
| 要切换网络层、配置 TLS 和退出清理 | [配置与运行](#配置与运行) |
|
||
| 已接入,需要升级和检查生成代码 | [日常开发与升级](#日常开发与升级) |
|
||
|
||
## 准备环境与安装
|
||
|
||
module 地址是 `git.yuntuo.hk/k2safe/go-omni`。先确保本机 Go 和 Git 可用,并具有仓库读取权限;使用自己的 Git 凭据管理器,不把账号或 Token 写进 module 地址。
|
||
|
||
安装固定版本的 CLI,后面的命令继续使用同一终端。私有 module 加入 GOPRIVATE,保留已有配置:
|
||
|
||
```sh
|
||
export GOPRIVATE="$(go env GOPRIVATE),git.yuntuo.hk"
|
||
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.15.0-preview
|
||
export PATH="$(go env GOPATH)/bin:$PATH"
|
||
go-omni --help
|
||
```
|
||
|
||
如果设置过 GOBIN,将该目录加入 PATH。远程 CLI 默认引用自身版本的框架,生成的 go.mod 不需要本地 replace。开发者与 CI 使用同版 CLI;框架源码开发方式见 [CLI 文档](cmd/go-omni/README.md#版本与迁移)。
|
||
|
||
## 新项目:生成并运行
|
||
|
||
在准备存放业务项目的目录执行,`./hello` 必须是新目录或空目录;把 `example.com/hello` 换成自己的 Go module 名称。
|
||
|
||
```sh
|
||
go-omni api new --module example.com/hello ./hello
|
||
cd hello
|
||
go mod tidy
|
||
go run .
|
||
```
|
||
|
||
另开终端验证:
|
||
|
||
```sh
|
||
curl -i http://127.0.0.1:8080/api/greet/Alice
|
||
curl -i http://127.0.0.1:8080/health/ready
|
||
curl -sS http://127.0.0.1:8080/openapi.json
|
||
```
|
||
|
||
第一个接口返回 HTTP 200,业务数据为 `{"message":"Hello Alice"}`。初始模板未配置响应格式,使用默认公共体:
|
||
|
||
```json
|
||
{"code":"OK","message":"success","data":{"message":"Hello Alice"},"requestId":"本次请求编号"}
|
||
```
|
||
|
||
按 Ctrl+C 停止服务。进入项目目录即可 `go build .`;该示例不需要数据库或其他外部服务。
|
||
|
||
### 生成的项目放什么
|
||
|
||
```text
|
||
hello/
|
||
├── api/service.api # 维护接口定义,也可 import 其他 .api
|
||
├── main.go # 生成器维护:启动、路由注册、优雅退出
|
||
├── etc/config.yaml # 项目配置
|
||
├── internal/
|
||
│ ├── config/ # 配置读取
|
||
│ ├── svc/ # 数据库、日志、外部客户端等依赖
|
||
│ ├── types/*Gen.go # 按 .api 文件生成 DTO
|
||
│ ├── handler/ # 生成的绑定、响应策略、路由及文档
|
||
│ ├── logic/greet/ # 在这里实现业务逻辑
|
||
│ ├── repository/greet/ # 在这里实现数据访问
|
||
│ └── middleware/ # 声明鉴权等中间件后生成对应 hook
|
||
├── docs/openapi.json # 生成的 OpenAPI 3.0.3
|
||
└── .goOmni/apiManifest.json # 生成文件所有权与校验信息,一起提交 Git
|
||
```
|
||
|
||
修改 `api/service.api` 后重新生成,业务写在 `internal/logic`,数据访问写在 `internal/repository`,依赖在 `internal/svc` 装配。声明业务错误后还会生成 `internal/apierrors/errorsGen.go`。
|
||
|
||
`api new` 的问候接口可以直接运行;自定义接口新生成的 Logic 和鉴权 hook 返回 **501**,需要实现后才可用。生成器不会根据 HTTP 字段猜数据库表或业务规则。
|
||
|
||
### 从自己的接口定义生成
|
||
|
||
保存下面的完整示例为 `catalog.api`,再执行后面的生成命令。这里的响应格式、错误码和提示都由这个业务项目定义。
|
||
|
||
```text
|
||
syntax = "v1"
|
||
|
||
@app (defaultResponse: CommonResponse)
|
||
|
||
response CommonResponse (
|
||
body: {"code":"${code}", "msg":"${message}", "data":"${data}"}
|
||
codeType: int32
|
||
successCode: 200
|
||
errorCode: 9999
|
||
successMessage: "操作成功"
|
||
dataMode: always
|
||
codes: {"ITEM_NOT_FOUND":404001}
|
||
)
|
||
|
||
error ItemNotFound 404 "ITEM_NOT_FOUND" "记录不存在"
|
||
|
||
type GetItemReq {
|
||
ItemID string `path:"itemId" binding:"required"`
|
||
}
|
||
type ItemResp {
|
||
ItemID string `json:"itemId"`
|
||
Name string `json:"name"`
|
||
}
|
||
|
||
@server (
|
||
group: item
|
||
prefix: /api/v1
|
||
response: CommonResponse
|
||
)
|
||
service catalog-api {
|
||
@doc "查询商品"
|
||
@errors ItemNotFound
|
||
@handler getItem
|
||
get /items/:itemId (GetItemReq) returns (ItemResp)
|
||
}
|
||
```
|
||
|
||
```sh
|
||
go-omni api validate --api catalog.api
|
||
go-omni new --api catalog.api \
|
||
--module example.com/catalog ./catalog
|
||
cd catalog
|
||
go mod tidy
|
||
```
|
||
|
||
将生成的 `internal/logic/item/getItem.go` 替换为下面的演示实现。实际业务中换成 Repository 查询,Logic 返回业务数据或错误,不再手工包装公共体:
|
||
|
||
```go
|
||
package item
|
||
|
||
import (
|
||
"context"
|
||
|
||
"example.com/catalog/internal/apierrors"
|
||
"example.com/catalog/internal/types"
|
||
)
|
||
|
||
func (s *Logic) GetItem(ctx context.Context, req *types.GetItemReq) (*types.ItemResp, error) {
|
||
if req.ItemID != "1001" {
|
||
return nil, apierrors.ItemNotFound()
|
||
}
|
||
return &types.ItemResp{ItemID: req.ItemID, Name: "示例商品"}, nil
|
||
}
|
||
```
|
||
|
||
示例 import 对应上面的 `example.com/catalog`,使用其他 module 名称时同步替换。启动后,在另一个终端验证:
|
||
|
||
```sh
|
||
# catalog 项目目录中启动;先停掉前面的 hello 服务,释放 8080 端口。
|
||
go run .
|
||
# 另一个终端:
|
||
curl -i http://127.0.0.1:8080/api/v1/items/1001
|
||
curl -i http://127.0.0.1:8080/api/v1/items/missing
|
||
```
|
||
|
||
成功时得到 `{"code":200,"msg":"操作成功","data":{"itemId":"1001","name":"示例商品"}}`;不存在时为 HTTP 404、`{"code":404001,"msg":"记录不存在","data":null}`。
|
||
|
||
需要 MySQL、文件存储或文件日志时,**首次生成**可以使用:
|
||
|
||
```sh
|
||
go-omni new --api catalog.api \
|
||
--module example.com/catalogFull --database mysql --storage local \
|
||
--log-output file --log-format json ./catalogFull
|
||
```
|
||
|
||
该命令在 `catalog.api` 所在目录执行,目标仍需是新目录。数据库迁移、表结构和 Repository 由项目维护,API 模板不自动建表;MySQL 使用结构化配置,密码通过 `GO_OMNI_DB_PASSWORD` 提供。存储还可选 `s3`、`r2`、`oss`,按生成项目的配置说明提供桶、端点和凭据。完整生成项目示例见 [managedService](examples/managedService/README.md)。
|
||
|
||
`go-omni new` 与 `go-omni api new` 统一生成契约项目。需要手写项目时,可直接引用框架包;生成器不再提供另一套 CRUD 启动目录。
|
||
|
||
## 已有项目:按模块接入
|
||
|
||
在业务项目的 **go.mod 所在目录**执行:
|
||
|
||
```sh
|
||
go get git.yuntuo.hk/k2safe/go-omni@v0.15.0-preview
|
||
```
|
||
|
||
已有项目可直接复用现有目录、Service、Repository 和数据库连接,先选择一个接口接入。也可以只用 `logx`、`storage` 等包,暂时保留原来的 HTTP 框架。希望入口和 HTTP 接入都交给生成器的项目,按 [已有服务接入步骤](PROJECT.md#已有服务接入步骤) 在新目录生成后迁入业务代码。
|
||
|
||
下面是可独立运行的 `main.go` 示例;迁入现有项目时,把路由调用接到自己的业务服务即可。
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"log"
|
||
"net/http"
|
||
|
||
omni "git.yuntuo.hk/k2safe/go-omni"
|
||
"git.yuntuo.hk/k2safe/go-omni/logx"
|
||
"git.yuntuo.hk/k2safe/go-omni/response"
|
||
)
|
||
|
||
func main() {
|
||
if err := run(); err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
}
|
||
|
||
func run() error {
|
||
closeLog, err := logx.Setup(logx.Config{
|
||
Service: "catalog", Level: "info", Format: "json", Output: "stdout",
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer closeLog()
|
||
|
||
app := omni.New(
|
||
omni.WithLogger(true),
|
||
omni.WithResponse(response.Config{
|
||
Body: map[string]any{
|
||
"code": "${code}", "msg": "${message}", "data": "${data}",
|
||
},
|
||
CodeType: "int32", SuccessCode: 200, ErrorCode: 9999,
|
||
SuccessMessage: "操作成功", DataMode: "always",
|
||
Codes: map[string]any{"ITEM_NOT_FOUND": 404001},
|
||
}),
|
||
)
|
||
app.GET("/api/v1/items/:itemId", func(c *omni.Context) error {
|
||
itemID := c.Param("itemId")
|
||
logx.InfoContext(c.Context(), "item.get", "itemId", itemID)
|
||
if itemID != "1001" {
|
||
return omni.NotFound("ITEM_NOT_FOUND", "记录不存在")
|
||
}
|
||
return c.OK(map[string]string{"itemId": itemID, "name": "示例商品"})
|
||
})
|
||
// 第三方协议可明确输出文本,不经过公共 JSON 包装。
|
||
app.GET("/ping", func(c *omni.Context) error {
|
||
return c.Text(http.StatusOK, "pong")
|
||
})
|
||
return app.Run("127.0.0.1:8080")
|
||
}
|
||
```
|
||
|
||
运行 `go run .`,访问 `/api/v1/items/1001`、`/api/v1/items/missing` 和 `/ping`,可分别验证成功、业务错误及纯文本输出。`App` 实现标准 `http.Handler`,也可挂进现有 `http.Server` 或父路由;这时监听、TLS、信号和资源关闭由现有入口负责,结束时调用 `app.Close()`。
|
||
|
||
现有 `http.HandlerFunc` 可直接通过 `app.POST("/legacy", httpx.WrapHandler(http.HandlerFunc(oldHandler)))` 接入;状态码与访问日志一致,下载和流式输出保持原协议。标准中间件、动态 JSON、上传/原文 body、手写绑定和自定义类型的完整用法见 [迁移扩展指南](MIGRATION.md)。
|
||
|
||
接入现有业务时,逐项确认:
|
||
|
||
- **协议**:保留既有 URL、HTTP 状态、字段、分页和空值语义。已有业务错误类型需要在项目内转换为 Omni 错误,或使用 `WithResponseEncoder` 适配原错误;设置字段模板不会自动识别其他库的错误。
|
||
- **鉴权**:把现有登录态和权限校验接入中间件,继续使用原来的授权范围和业务规则。原来依赖 `gin.Context` 等类型的 HTTP 代码需要适配;标准 `context.Context` 的业务服务可以继续复用。
|
||
- **数据类型**:ID、金额、日期等按项目协议转换。用 `.api` 的 `scalar` 显式关联项目自己的类型与 JSON/Text 转换方法,分别声明输入和输出协议;普通字段保持严格绑定,见 [自定义标量](MIGRATION.md#4-自定义标量项目定义行为框架连接调用)。
|
||
- **生成器**:它不会自动接管任意已有 Go 项目。希望引入 `.api` 时,先在独立新目录生成对照,再规划路由与生成目录的接入,不要用脚手架覆盖现有业务目录。
|
||
|
||
先通过一个接口的成功、参数错误、未登录、无权限及业务错误回归,再按模块迁移。框架不要求所有项目使用同一套业务目录或身份模型。
|
||
|
||
## 配置公共响应与独立协议
|
||
|
||
| 接口来源或需求 | 配置位置 |
|
||
| --- | --- |
|
||
| `.api` 生成的接口 | `response Name (...)` 定义,`@server (response: Name)` 引用 |
|
||
| 多个 `.api` 共用返回体 | 将声明放到 `common.api`,使用 `import "common.api"` |
|
||
| 某个生成接口用另一种 JSON 公共体 | 路由声明 `@response OtherResponse` |
|
||
| 只要业务 JSON、无成功包装 | 路由声明 `@response raw`;失败仍沿用组级格式 |
|
||
| 手写路由和应用级 404/405 | `omni.WithResponse(response.Config{...})`,或读取配置后传入 `WithConfig` |
|
||
| 成功和失败都要遵循特殊回调协议 | `HandleWithResponse` 绑定项目自己的 `response.Encoder` |
|
||
| 单次直接输出 JSON、文本、XML、字节或空响应 | `c.JSON` / `c.Text` / `c.XML` / `c.Bytes` / `c.NoContent` |
|
||
|
||
`${data}` 是业务返回值,`${message}` 是公开提示,`${code}` 是业务码;占位符保留原值类型,不是字符串拼接。还支持 `${requestId}`、`${ok}`、`${status}`,字段名和嵌套结构由项目决定。
|
||
|
||
- `codeType` 可以是 `string`、`int32`、`int64`;整数类型需要配置 `successCode` 和 `errorCode`。
|
||
- `codes` 把逻辑错误码映射成对外业务码;未单独映射的失败使用 `errorCode`。JSON 业务码不会改变 HTTP 状态,例如 `404001` 仍对应 HTTP 404。
|
||
- `successMessage` 设置成功提示;未设置 `errorMessage` 时沿用错误的公开提示。`messages` 可按逻辑错误码单独覆盖,优先级最高。全局 `errorMessage` 会替换未单独映射的所有失败提示,只有需要统一失败文案时才设置。
|
||
- `dataMode: always` 在无数据时输出 null;默认 `omitNil` 省略模板里相应对象字段。没有 `returns` 的 `.api` 路由默认 HTTP 204、无响应体,这与 `200 + data:null` 是不同的接口契约。
|
||
|
||
**生成接口的格式以 `.api` 为准**,修改后重新生成,运行时与 OpenAPI 一起更新;应用 YAML 不会覆盖它。404/405 没有匹配到某个 `.api` 接口,使用应用默认配置,需要统一时也配置应用默认值。
|
||
|
||
文本/XML 回调用 Go 编码器或直接输出,`.api` 可用 `@handlerMode manual` 与 `@produces` 声明成功媒体类型;特殊错误协议仍需项目维护。`@response raw` 仅表示原始 JSON。前面示例的 `c.Text` 控制这次正常返回;需要连鉴权失败、业务错误和 panic 都按回调协议响应时使用 `HandleWithResponse`。完整配置和回调示例见 [响应参考](RESPONSE.md)。
|
||
|
||
## 业务开发与依赖装配
|
||
|
||
### 日志
|
||
|
||
业务包直接使用 `logx`,不需要 HTTP 框架对象:
|
||
|
||
```go
|
||
// ctx 是 Service 接收的标准 context.Context。
|
||
logx.InfoContext(ctx, "order.created", "orderId", orderID)
|
||
logx.ErrorContext(ctx, "order.failed", "error", err)
|
||
```
|
||
|
||
入口用 `logx.Setup` 初始化,退出时关闭;生成的 API 项目已经完成日志初始化和退出清理。Service、Repository 和外部请求持续传递请求的 `context.Context`,业务日志可关联 `requestId`,同时保留取消信号。框架不规定或自动注入 `userId`、`tenantId`。
|
||
|
||
`WithLogger(true)` 开启 HTTP 访问日志。格式、级别、文件轮转、脱敏键和慢请求阈值均可配置,见 [日志](logx/README.md)。
|
||
|
||
### 鉴权与业务错误
|
||
|
||
`.api` 可声明认证方式和关联中间件,例如把下面的声明放入契约,并在相应 `@server` 块中加上 `auth: UserAuth`:
|
||
|
||
```text
|
||
auth UserAuth (
|
||
type: bearer
|
||
middleware: Auth
|
||
)
|
||
```
|
||
|
||
生成器会创建 `internal/middleware/auth.go`,由项目实现凭据校验和身份注入。声明会生成路由绑定与 OpenAPI 的认证描述,**不会自动实现登录或授权规则**;未实现的 hook 返回 501。手写路由可使用 `auth.RequireAuth(authenticator)` 或自己的中间件。JWT、Session 和 Policy 按需选择。
|
||
|
||
业务错误可以集中定义,业务只选择发生了哪个错误。**HTTP 状态由项目配置,业务失败可以返回 HTTP 200**;框架不根据 HTTP 状态单独判断成功与否。
|
||
|
||
错误目录支持两种维护方式,按项目选择一种:**Go 中定义并用 `omni.WithErrors` 注册一次**,或者在公共 `.api` 中声明并生成调用函数。前者不要求 `.api` 重复声明目录或逐路由列出错误;生成路由默认继承 App 目录。
|
||
|
||
采用 `.api` 管理时,其他契约可通过 `import` 引用:
|
||
|
||
```text
|
||
errorCatalog CommonErrors (
|
||
defaultStatus: 200
|
||
errors: {
|
||
"UsernameInvalid": {"key": "USERNAME_INVALID", "code": 400002, "message": "用户名格式不正确"},
|
||
"PasswordInvalid": {"key": "PASSWORD_INVALID", "code": 400002, "message": "密码格式不正确"},
|
||
"PermissionDenied": {"key": "PERMISSION_DENIED", "status": 403, "code": 403001, "message": "没有权限"}
|
||
}
|
||
)
|
||
```
|
||
|
||
`UsernameInvalid` 是生成函数名;`key` 是唯一逻辑标识;`code` 是项目对外的字符串或整数业务码,可以重复。条目没有填写 `status` 时使用目录的 `defaultStatus`,未配置目录默认值时为 400。这个默认值只作用于该目录的条目,不会把所有框架异常都改成 200。
|
||
|
||
在已有公共响应中保留 `codeType: int32` / `int64` / `string`、成功码、未定义错误的兜底码和返回体模板;不再重复填写这几条错误的 `codes` / `messages`。通过 `@server (errors: UsernameInvalid, PasswordInvalid)` 或路由 `@errors UsernameInvalid` 引用它们。
|
||
|
||
生成后业务调用:
|
||
|
||
```go
|
||
return TokenResponse{}, apierrors.UsernameInvalid()
|
||
// 需要保留内部原因时:
|
||
return TokenResponse{}, apierrors.UsernameInvalid().WithCause(err)
|
||
```
|
||
|
||
生成器同时提供 `internal/apierrors.Catalog()`、错误函数、路由错误目录及 OpenAPI。目录会在 App 中间件执行前绑定到生成路由;无需手动改已经生成的 main。HTTP 200 的错误使用失败响应模板;OpenAPI 用 `anyOf` 同时保留成功 DTO 和错误体,多条错误共用数字码时仍保留各自示例。完整契约见 [errors.api](examples/contracts/errors.api)。
|
||
|
||
手写项目可在启动时加载自己的目录,保留原来的响应配置:
|
||
|
||
```go
|
||
// omnierrors 是 git.yuntuo.hk/k2safe/go-omni/errors。
|
||
catalog := omnierrors.Catalog{
|
||
DefaultStatus: 200,
|
||
Definitions: map[string]omnierrors.Definition{
|
||
"USERNAME_INVALID": {Code: 400002, Message: "用户名格式不正确"},
|
||
"PASSWORD_INVALID": {Code: 400002, Message: "密码格式不正确"},
|
||
},
|
||
}
|
||
app := omni.New(
|
||
omni.WithErrors(catalog),
|
||
omni.WithResponse(response.Config{
|
||
CodeType: "int32", SuccessCode: 0, ErrorCode: 9999,
|
||
MessageField: "msg", DataMode: "always",
|
||
}),
|
||
)
|
||
// Service 只返回标准 error,不依赖 App 或 HTTP Context:
|
||
// return omnierrors.ByCode("USERNAME_INVALID").WithCause(err)
|
||
```
|
||
|
||
目录也可放入 `config.Config.Errors`,由 JSON/YAML 加载;生成项目的路径为 `server.errors`,生成路由只有显式引用错误定义时才使用 `.api` 独立目录,未声明则继承 App。不同 App 独立持有配置,单路由也可通过 `response.WithErrors` 绑定专用目录与自定义 Encoder。直接输出文本、XML、下载或已提交的响应继续保留原协议。
|
||
|
||
访问日志保留实际 HTTP 状态,并增加失败标记、逻辑错误标识和目录业务码;指标、链路及框架标准中间件/幂等重放也保留失败语义。未知标识使用系统错误兜底,内部 cause 不写入默认公开响应。旧 `error (...)`、`New` / `BadRequest` / `Codes` / `Messages` 继续支持;新目录与旧映射冲突会报错,避免静默覆盖。
|
||
|
||
动态提示也可以集中配置,在目录中声明 `message: "{label}不能超过{max}个字符"` 和有序 `params`(label/string、max/int),生成带类型的调用:
|
||
|
||
```go
|
||
return apierrors.TextTooLong("企业名称", 100)
|
||
// 最终提示:企业名称不能超过100个字符
|
||
```
|
||
|
||
运行时支持 `omnierrors.ByCode("TEXT_TOO_LONG").WithParams(...)`,与生成函数共用解析器。固定错误继续无参数调用;缺参或类型错误使用系统兜底。模板、参数类型及具体响应示例同步写入 OpenAPI。完整示例见 [errorParams.api](examples/contracts/errorParams.api)。
|
||
|
||
更详细的配置、作用域和升级步骤见 [统一错误目录](errors/README.md)。
|
||
|
||
### 数据库、存储及其他模块
|
||
|
||
生成项目在 `internal/svc` 中创建依赖,提供给 Logic/Repository,并用 `c.OnClose(db.Close)` 登记自有资源。生成的 main 在 HTTP 排空后关闭 svc,再关闭日志。直接使用框架的手写项目则可以用 `app.OnShutdown(db.Close)`;不要对同一资源重复登记。
|
||
|
||
已有项目使用 `driver.Connector` 时,直接 `database.OpenConnector(ctx, connector, database.DefaultPoolConfig())`。MySQL 可直接用 [database/mysql](database/mysql/README.md) 配置独立 TLS、Logger、时区与拨号参数;任意驱动也可由项目构造 Connector。框架负责连接池、启动 Ping、健康检查及事务。无需转成 DSN,也无需注册全局 TLS/Logger。具体示例和资源归属见 [Connector 接入](database/README.md#connector-接入v091-preview)。
|
||
|
||
| 能力 | 包与接入说明 |
|
||
| --- | --- |
|
||
| 数据库连接池、事务、显式迁移 | `database`,见 [数据库](database/README.md);表结构与驱动由项目选择 |
|
||
| 本地文件、S3、R2、OSS、直传与临时下载 | `storage`,见 [文件存储](storage/README.md);业务依赖接口,装配时选择适配器 |
|
||
| 缓存、出站 HTTP、限流、幂等、后台任务 | `cache`、`httpc`、`middleware`、`task`,见 [基础模块](MODULES.md) |
|
||
| JWT/Session、策略、Redis 队列、审计、分页、观测 | 见 [扩展模块](ADVANCED.md);按需注入实现 |
|
||
| 存活与就绪检查 | `health`,见 [健康检查](health/README.md);数据库等依赖作为 readiness 探针 |
|
||
|
||
API 项目的数据库迁移需要单独维护并显式执行;启动不会自动改表。完整示例 [userService](examples/userService/README.md) 展示 MySQL、事务、鉴权、日志和文件导出,可参考依赖装配方式后接入自己的业务规则。
|
||
|
||
## 配置与运行
|
||
|
||
生成项目从 `etc/config.yaml` 读取配置,优先级为 **默认值 → 配置文件 → 环境变量**。密钥通过环境变量提供。手写入口需显式调用配置加载函数并传给 `omni.New(omni.WithConfig(settings))`;单独调用 `omni.New()` 不会自动读取环境变量。
|
||
|
||
| 环境变量 | 用途 |
|
||
| --- | --- |
|
||
| `GO_OMNI_HTTP_ADDR` | 监听地址;生成项目默认 `127.0.0.1:8080` |
|
||
| `GO_OMNI_TRANSPORT` | `nethttp`(默认)、`native`、`http2` |
|
||
| `GO_OMNI_TLS_CERT_FILE` / `GO_OMNI_TLS_KEY_FILE` | 成对设置 TLS 证书和私钥路径 |
|
||
| `GO_OMNI_HTTP_READ_HEADER_TIMEOUT` / `GO_OMNI_HTTP_READ_TIMEOUT` | 请求头 / 请求读取超时,如 `5s` / `30s` |
|
||
| `GO_OMNI_HTTP_WRITE_TIMEOUT` / `GO_OMNI_HTTP_IDLE_TIMEOUT` | 响应写入 / Keep-Alive 空闲超时 |
|
||
| `GO_OMNI_HTTP_MAX_HEADER_BYTES` | 请求头大小限制,字节数 |
|
||
| `GO_OMNI_SHUTDOWN_TIMEOUT` | 优雅退出等待时间,默认 `10s` |
|
||
| `GO_OMNI_ENABLE_LOGGER` / `GO_OMNI_LOG_LEVEL` | 访问日志开关、日志级别 |
|
||
| `GO_OMNI_TRUSTED_PROXIES` | 可解析转发地址头的可信代理 IP/CIDR,逗号分隔 |
|
||
| `GO_OMNI_DB_HOST/USER/PASSWORD/DATABASE` | 官方 MySQL 适配器的结构化连接配置 |
|
||
|
||
例如在生成项目中切换 native:
|
||
|
||
```sh
|
||
GO_OMNI_TRANSPORT=native go run .
|
||
```
|
||
|
||
手写入口可用 `omni.WithTransport(omni.TransportNative)`;TLS 配置为 `omni.WithTLS(certFile, keyFile)`。native 提供 HTTP/1.1、Keep-Alive、TLS、超时、连接/请求并发上限和对象复用;HTTP/2 是单独的实验适配器,必须配置 TLS。默认 nethttp 的 TLS 支持 HTTP/1.1 与 HTTP/2。
|
||
|
||
读写超时也可以在生成项目的 `etc/config.yaml` 中声明(放在 `server.http` 下):
|
||
|
||
```yaml
|
||
server:
|
||
http:
|
||
readHeaderTimeout: 5s
|
||
readTimeout: 30s
|
||
writeTimeout: 30s
|
||
idleTimeout: 60s
|
||
maxHeaderBytes: 1048576
|
||
```
|
||
|
||
代码配置使用 `omni.WithHTTPConfig(omni.HTTPConfig{ReadTimeout: 30*time.Second, WriteTimeout: 30*time.Second})`。`Run`、`RunContext`、`ServeContext` 会应用它;`NewServer` 会配置返回的标准 `http.Server`,包含可选的 Go 字段 `ErrorLog`。**正值覆盖、零值保留所选网络层的原有默认行为、负值报错**;不配置时升级行为不变。超时含义与支持范围见 [HTTP 配置](transport/README.md#http-配置v091-preview),不要把网络 I/O 超时当成业务执行时限。
|
||
|
||
自研网络层通过 `RunWith(ctx, runner)` 接入监听与连接生命周期,通过 `NewWithRouter(router)` 替换路由;业务 Handler/Service 不需要导入具体适配器。细节与验收契约见 [网络适配层](transport/README.md)。
|
||
|
||
生成项目的依赖可调用 `svcCtx.RequestShutdown(err)` 主动申请退出,框架排空请求、清理依赖后保留故障原因并以非零状态退出。健康检查与 OpenAPI 的开关、路径通过 `server.endpoints` 配置,健康响应默认沿用项目公共体;未就绪保持 HTTP 503。完整配置和 v0.12 项目的升级步骤见 [项目指南](PROJECT.md#依赖主动申请退出)。
|
||
|
||
`Run` 处理 SIGINT/SIGTERM;`RunContext` 接收调用方控制的退出信号。服务先停止接收新请求,等待活跃请求完成,再清理资源;超时会取消请求并关闭连接,业务代码仍需响应 context 取消。
|
||
|
||
## 日常开发与升级
|
||
|
||
在已生成的 API 项目目录执行:
|
||
|
||
```sh
|
||
go-omni api validate
|
||
go-omni api inspect
|
||
go-omni api generate --dry-run
|
||
go-omni api generate
|
||
go-omni api generate --check
|
||
go test ./...
|
||
```
|
||
|
||
`main.go`、`Gen.go`、`docs/openapi.json` 和 manifest 由生成器管理,手动修改会报告冲突。Logic、Repository、中间件实现、配置和依赖装配属于项目,重复生成保留手写内容。契约变化会提示受影响的 Logic 文件和 DTO 字段,明确的签名错误会给出原签名与期望签名;生成后 `--check` 仍能检出未修复的问题。将 `.api`、生成文件和 `.goOmni/apiManifest.json` 一起提交,CI 同时运行 `generate --check` 与 `go test ./...`。详见 [契约变更提示与检查边界](PROJECT.md#契约变更提示)。
|
||
|
||
CLI 与业务 module 固定使用 `v0.15.0-preview`。v0.14.x 项目升级依赖与 CLI 后重新生成,框架维护的 main/runtimeGen 自动更新;v0.13 及更早项目按 [升级预览](DEVELOPMENT.md#v013-项目升级) 处理,不直接覆盖手写入口或业务文件。升级后执行生成检查与业务回归。版本变化、迁移边界和验收记录见 [发布说明](RELEASE.md)。
|
||
|
||
## 按需深入
|
||
|
||
| 需要了解 | 文档 |
|
||
| --- | --- |
|
||
| `.api` 类型、参数绑定、错误、鉴权和生成规则 | [API 开发指南](API.md) |
|
||
| 公共响应体、提示优先级、独立回调协议 | [响应参考](RESPONSE.md) |
|
||
| CLI 完整参数、本地框架开发与迁移 | [CLI](cmd/go-omni/README.md) |
|
||
| 当前生成项目:Go 错误目录、上传、回调和下载 | [managedService](examples/managedService/README.md) |
|
||
| 数据库/事务业务参考(旧目录示例) | [userService](examples/userService/README.md) |
|
||
| 手写接口分层示例 | [reference](examples/reference/README.md) |
|
||
| 框架成熟度、历史变更 | [路线图](ROADMAP.md)、[更新记录](CHANGELOG.md) |
|
||
| 网络性能、测试条件与适用范围 | [性能说明](PERFORMANCE.md)、[HTTP/2 验收](transport/native/HTTP2.md) |
|
||
|
||
当前版本可用于固定版本接入与业务验证,仍标记为 preview。`.api` 借鉴 go-zero 的接口开发方式,支持范围以本仓库文档为准,不是完整 goctl 语法兼容层;HTTP/2 保持实验状态,OSS/R2 云账号实网验收不属于本次发布范围。
|