38 KiB
GoOmni
GoOmni 是面向多个业务项目复用的 Go 后端框架:用 .api 定义接口、请求和返回类型,生成项目目录、HTTP 接入代码及 OpenAPI,业务代码负责 Logic 和 Repository。也可以在已有项目中直接使用 omni.New(),或单独引入日志、存储等模块。
从这份 README 开始接入。 当前版本 v0.20.0-preview,新生成项目采用根目录入口和 config / svc / logic / handler。可以固定版本开展业务接入;文件维护边界和已有服务迁移步骤见 项目指南,版本变化与验收见 发布说明。
v0.19 改动:新增可选 copyx 工具,用普通 Go 调用完成浅拷贝、深拷贝和显式类型转换;深拷贝会拒绝自定义转换中残留的源对象可变引用。移除了 mappings / mappingFiles 和映射代码生成,已有使用者须按 迁移步骤 调整,不能直接删除仍在调用的旧文件。
普通接口的流程是:修改 .api → 生成 Request/Response → 写 Logic → 测试。有独立职责的业务类型才需要转换;可直接写 Go,也可以按需使用 copyx。公共字段组合、中间件工厂、Repository 开关继续支持,见 生成能力指南。
Go 1.22+。框架不依赖 Gin,默认使用标准库网络层,提供可切换的 native HTTP/1.1 和实验性 HTTP/2 适配器。
响应字段、业务码、登录方式、权限规则和数据库表结构由接入项目决定。文中的 code/msg/data、200/9999 都是示例配置,可以替换。
契约支持 returns([]string/[]int64/[]bool/[]any)、OpenAPI 公共引用、workspace 一次导出共享 Go 错误目录,以及统一环境变量别名覆盖。详细配置与升级步骤见 业务开发指南。
已有通用开发能力见 业务开发指南:数组/null、严格请求策略、声明式上传、完整配置环境覆盖、资源生命周期、共享契约和升级工具。MySQL 直接使用 官方适配器。
后台组件可在 svc 登记 分阶段关闭,先停止接收工作并等待收尾,再关闭共享资源;无需绑定队列或 ORM。
使用 GORM 的项目可以选择 gormx 适配器,复用数据库初始化和连接池,并将 SQL 日志接入 logx。
当前功能与使用入口
下表是当前公开能力的索引。每项给出调用入口和完整说明;模块按需使用,omni.New() 不会自动初始化数据库、Redis、ORM、队列或云存储。示例中的 import 统一以 git.yuntuo.hk/k2safe/go-omni/ 开头。
| 功能与用途 | 怎么用 | 解释与示例 |
|---|---|---|
| 项目、接口与文档生成 | go-omni new;改 .api 后 api generate;--check 检查漂移 |
下方快速开始、CLI、完整生成项目 |
| 公共字段与跨服务共享契约 | .api 组合对象字段;omni.imports.json 声明共享根;workspace 批量生成 |
公共字段示例、共享契约与 workspace |
| 中间件依赖工厂、可选 Repository | omni.gen.json 选择 factories / none;在 New<Name>(deps) 写一次实现 |
配置与完整工厂示例 |
| 路由、分组、请求上下文 | omni.New()、app.GET / Group / Use;业务使用 context.Context |
基本服务、已有项目接入 |
| HTTP 网络与扩展适配 | 配置 transport 为 nethttp / native / http2;自定义 Router/Runner | 网络层配置与契约、性能与限制 |
| HTTP 老接口桥接 | 通过官方 Handler/Middleware 适配器接入标准 net/http,跟踪实际响应状态 |
接入说明、桥接/上传/自定义 ID 示例 |
| 请求校验、动态 JSON、自定义标量 | .api 声明来源/校验/scalar;手写契约用 contract.New[T];简单绑定可用 validate.JSON/Query/URI |
语法与例子、请求策略 |
| 数组、null、原始请求、上传下载 | .api 声明数组/可空返回、body/multipart 与大小限制;必要时使用 Bind/Manual 扩展 |
契约示例、含上传/回调/下载的项目 |
| 公共响应与特殊协议 | .api 定义 response 并 @app(defaultResponse: ...);Go 用 WithResponse / Encoder |
响应配置、响应模板契约 |
| 错误目录与动态提示 | WithErrors(catalog) 注册一次;ByCode(...).WithParams(...).WithCause(err) |
错误目录、模板参数示例 |
| OpenAPI 与错误公共引用 | .api 自动生成;手写接口可使用 openapi 注册器;Go 错误目录显式导出 |
契约工具、注册示例 |
| 结构化业务日志 | logx.Info/Error、logx.FromContext(ctx)、logx.New;支持轮转、脱敏、慢请求和级别更新 |
下方日志示例、完整配置 |
| 文件配置与环境覆盖 | 默认值→JSON/YAML/XML→环境→校验;完整业务结构用 config.LoadProject[T] / 环境别名 |
统一配置指南、完整可运行示例、动态级别 |
| 生命周期与健康检查 | svc 登记 OnClose/OnStop/OnDrain,RequestShutdown 通知故障;健康路径/开关可配 | 生命周期用法、健康检查、生成项目扩展 |
| 数据库、连接池与事务 | database.Open / OpenConnector;事务与检查由项目显式调用 |
通用数据库、MySQL TLS/Logger |
| 可选 GORM 与迁移 | gormx 复用已有连接池;go-omni migrate 预览,显式 apply |
GORM 示例、迁移用法 |
| 认证与权限接口 | 注入 Authenticator / Policy;JWT 或 Session 只是可选实现 | 认证说明、鉴权完整例子 |
| 缓存 | cache.NewMemory 或 Redis 实现;业务面向 Store 的 Get/Set/Delete |
缓存配置/代码、可运行模块服务 |
| 出站 HTTP、熔断 | httpc.New 复用连接池与截止时间;resilience.New 隔离故障依赖 |
HTTP 用法、熔断/观测 |
| 请求追踪、访问日志、恢复、限流、幂等、超时 | app.Use(...) 按需组合 middleware;跨副本选择 Redis 实现 |
模块说明与限制、分布式实现 |
| 本地后台任务与调度 | task.New、Submit、Every;有界队列、排空关闭 |
task 示例与生命周期 |
| 持久队列 | 业务使用 Publisher / Consumer;Redis Streams 提供至少一次交付 | 队列初始化/消费示例 |
| 文件存储、直传、云端分片 | Store 的 Put/Get/Delete;Local、S3/R2、OSS 按配置装配;扩展接口提供分片续传 | 存储接口与完整例子、本地存储服务 |
| 日志关联、指标与链路 | metrics.New().Middleware();telemetry 注入 OTel exporter/reader |
基础指标、OTel 用法、完整接入 |
| 审计与分页 | audit.New 注入 Sink/白名单;pagination.Parse / NewResult |
解释与调用例子 |
| 可选结构转换与深拷贝 | copyx.Copy / Clone / New;Go converter 处理差异 |
下方示例、完整 copyx 指南、可运行代码 |
| 自定义适配器验收 | transporttest.Run、storagetest.Run / RunUploads 验证扩展契约 |
网络契约测试入口、存储契约测试入口 |
统一配置加载
新生成项目的加载流程由框架管理。业务在 internal/config/config.go 增加字段、默认值和 Validate,无需改 main 或复制环境变量解析。JSON、YAML、XML 共用字段和校验;扩展格式在 loader.go 注册。
// 项目 Config 中增加字段,自动支持 GO_OMNI_BATCH_SIZE:
BatchSize int `json:"batchSize" envDefault:"100"`
// 项目 Validate 中增加业务范围检查:
if c.BatchSize < 1 {
return omniConfig.Invalid("batchSize", "must be positive")
}
错误包含文件、字段、可用的行列或环境变量名;输入原值不自动写入错误。
go run ./examples/configuration -format xml
DEMO_CLIENT_TIMEOUT=500ms go run ./examples/configuration
详见 统一加载、解析器、迁移 和 完整示例。旧项目须先迁移手写 Load,再重新生成;生成器会保留业务配置并拒绝冲突,不静默覆盖。
选择接入方式
| 你的项目 | 从哪里开始 |
|---|---|
| 新建服务,先定义接口再开发 | 新项目:生成并运行 |
| 已有服务,逐步接入 HTTP 或基础模块 | 已有项目:按模块接入 |
| 已有标准 Handler、动态 JSON、上传或自定义 ID 类型 | 迁移扩展指南 |
| 想统一返回体、业务错误或三方回调 | 配置公共响应与独立协议 |
| 要接日志、数据库、鉴权和存储 | 业务开发与依赖装配 |
| 要切换网络层、配置 TLS 和退出清理 | 配置与运行 |
| 已接入,需要升级和检查生成代码 | 日常开发与升级 |
准备环境与安装
module 地址是 git.yuntuo.hk/k2safe/go-omni。先确保本机 Go 和 Git 可用,并具有仓库读取权限;使用自己的 Git 凭据管理器,不把账号或 Token 写进 module 地址。
安装固定版本的 CLI,后面的命令继续使用同一终端。私有 module 加入 GOPRIVATE,保留已有配置:
export GOPRIVATE="$(go env GOPRIVATE),git.yuntuo.hk"
go install git.yuntuo.hk/k2safe/go-omni/cmd/go-omni@v0.20.0-preview
export PATH="$(go env GOPATH)/bin:$PATH"
go-omni --help
如果设置过 GOBIN,将该目录加入 PATH。远程 CLI 默认引用自身版本的框架,生成的 go.mod 不需要本地 replace。开发者与 CI 使用同版 CLI;框架源码开发方式见 CLI 文档。
新项目:生成并运行
在准备存放业务项目的目录执行,./hello 必须是新目录或空目录;把 example.com/hello 换成自己的 Go module 名称。
go-omni api new --module example.com/hello ./hello
cd hello
go mod tidy
go run .
另开终端验证:
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"}。初始模板未配置响应格式,使用默认公共体:
{"code":"OK","message":"success","data":{"message":"Hello Alice"},"requestId":"本次请求编号"}
按 Ctrl+C 停止服务。进入项目目录即可 go build .;该示例不需要数据库或其他外部服务。
生成的项目放什么
hello/
├── api/service.api # 维护接口定义,也可 import 其他 .api
├── main.go # 生成器维护:启动、路由注册、优雅退出
├── etc/config.yaml # 项目配置
├── omni.gen.json # 项目维护的生成选项与可选映射
├── internal/
│ ├── config/ # 配置读取
│ ├── svc/ # 数据库、日志、外部客户端等依赖
│ ├── types/*Gen.go # 按 .api 文件生成 DTO
│ ├── handler/ # 生成的绑定、响应策略、路由及文档
│ ├── logic/greet/ # 在这里实现业务逻辑
│ ├── repository/ # 按需创建,默认不生成空骨架
│ └── middleware/ # 声明鉴权等中间件后生成工厂
├── 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 返回 501。新项目的具名中间件工厂未实现时直接阻止启动;v0.16 的原有 handler 模式仍返回 501。生成器不会根据 HTTP 字段猜数据库表或业务规则。
从自己的接口定义生成
保存下面的完整示例为 catalog.api,再执行后面的生成命令。这里的响应格式、错误码和提示都由这个业务项目定义。
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)
}
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 返回业务数据或错误,不再手工包装公共体:
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 名称时同步替换。启动后,在另一个终端验证:
# 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、文件存储或文件日志时,首次生成可以使用:
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。
go-omni new 与 go-omni api new 统一生成契约项目。需要手写项目时,可直接引用框架包;生成器不再提供另一套 CRUD 启动目录。
已有项目:按模块接入
在业务项目的 go.mod 所在目录执行:
go get git.yuntuo.hk/k2safe/go-omni@v0.20.0-preview
已有项目可直接复用现有目录、Service、Repository 和数据库连接,先选择一个接口接入。也可以只用 logx、storage 等包,暂时保留原来的 HTTP 框架。希望入口和 HTTP 接入都交给生成器的项目,按 已有服务接入步骤 在新目录生成后迁入业务代码。
下面是可独立运行的 main.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、手写绑定和自定义类型的完整用法见 迁移扩展指南。
接入现有业务时,逐项确认:
- 协议:保留既有 URL、HTTP 状态、字段、分页和空值语义。已有业务错误类型需要在项目内转换为 Omni 错误,或使用
WithResponseEncoder适配原错误;设置字段模板不会自动识别其他库的错误。 - 鉴权:把现有登录态和权限校验接入中间件,继续使用原来的授权范围和业务规则。原来依赖
gin.Context等类型的 HTTP 代码需要适配;标准context.Context的业务服务可以继续复用。 - 数据类型:ID、金额、日期等按项目协议转换。用
.api的scalar显式关联项目自己的类型与 JSON/Text 转换方法,分别声明输入和输出协议;普通字段保持严格绑定,见 自定义标量。 - 生成器:它不会自动接管任意已有 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。完整配置和回调示例见 响应参考。
业务开发与依赖装配
日志
业务包直接使用 logx,不需要 HTTP 框架对象:
// 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 访问日志。格式、级别、文件轮转、脱敏键和慢请求阈值均可配置,见 日志。
鉴权与业务错误
.api 可声明认证方式和关联中间件,例如把下面的声明放入契约,并在相应 @server 块中加上 auth: UserAuth:
auth UserAuth (
type: bearer
middleware: Auth
)
新项目生成 internal/middleware/authFactory.go,由项目实现 NewAuth(deps);生成器负责初始化和注册。v0.16 或 handler 模式继续使用 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 引用:
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 引用它们。
生成后业务调用:
return TokenResponse{}, apierrors.UsernameInvalid()
// 需要保留内部原因时:
return TokenResponse{}, apierrors.UsernameInvalid().WithCause(err)
生成器同时提供 internal/apierrors.Catalog()、错误函数、路由错误目录及 OpenAPI。目录会在 App 中间件执行前绑定到生成路由;无需手动改已经生成的 main。HTTP 200 的错误使用失败响应模板;OpenAPI 用 anyOf 同时保留成功 DTO 和错误体,多条错误共用数字码时仍保留各自示例。完整契约见 errors.api。
手写项目可在启动时加载自己的目录,保留原来的响应配置:
// 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),生成带类型的调用:
return apierrors.TextTooLong("企业名称", 100)
// 最终提示:企业名称不能超过100个字符
运行时支持 omnierrors.ByCode("TEXT_TOO_LONG").WithParams(...),与生成函数共用解析器。固定错误继续无参数调用;缺参或类型错误使用系统兜底。模板、参数类型及具体响应示例同步写入 OpenAPI。完整示例见 errorParams.api。
更详细的配置、作用域和升级步骤见 统一错误目录。
可选结构转换与深拷贝
普通接口不需要任何拷贝配置,直接使用生成的 Request/Response。独立业务结构之间有转换需求时:
// import "git.yuntuo.hk/k2safe/go-omni/copyx"
var input DomainInput
if err := copyx.Copy(&input, req); err != nil { return err }
// 源与目标之间的指针、map、slice 需要隔离时:
snapshot, err := copyx.Clone(source)
if err != nil { return err }
默认浅拷贝,同名同类型字段自动赋值,保留零值;默认不会把 nil 填成非可空目标的零值。不要为使用工具新增一套重复模型。
需要重复使用或自定义规则时创建 Copier:
copier, err := copyx.New(
copyx.WithDeepCopy(),
copyx.WithFieldNames[Input, View](map[string]string{"Name": "DisplayName"}),
copyx.WithConverter(func(v time.Time) (string, error) {
return v.UTC().Format(time.RFC3339), nil
}),
)
if err != nil { return err }
if err := copier.Copy(&view, input); err != nil { return err }
自定义函数的源/目标类型由 Go 泛型确定。深拷贝时返回原指针、原 map、子切片或嵌套旧引用都会报错;字符串/整数原值可以返回。差异规则、nil 策略、字段覆盖、资源限制和原 mapping 移除步骤见 copyx 指南,完整类型及可运行代码见 examples/copying。工具使用运行时检查,类型对需测试;深拷贝不适用于连接、锁等资源。
数据库、存储及其他模块
生成项目在 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 配置独立 TLS、Logger、时区与拨号参数;任意驱动也可由项目构造 Connector。框架负责连接池、启动 Ping、健康检查及事务。无需转成 DSN,也无需注册全局 TLS/Logger。具体示例和资源归属见 Connector 接入。
| 能力 | 包与接入说明 |
|---|---|
| 数据库连接池、事务、显式迁移 | database,见 数据库;表结构与驱动由项目选择 |
| 本地文件、S3、R2、OSS、直传与临时下载 | storage,见 文件存储;业务依赖接口,装配时选择适配器 |
| 缓存、出站 HTTP、限流、幂等、后台任务 | cache、httpc、middleware、task,见 基础模块 |
| JWT/Session、策略、Redis 队列、审计、分页、观测 | 见 扩展模块;按需注入实现 |
| 存活与就绪检查 | health,见 健康检查;数据库等依赖作为 readiness 探针 |
API 项目的数据库迁移需要单独维护并显式执行;启动不会自动改表。完整示例 userService 展示 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:
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 下):
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 配置,不要把网络 I/O 超时当成业务执行时限。
自研网络层通过 RunWith(ctx, runner) 接入监听与连接生命周期,通过 NewWithRouter(router) 替换路由;业务 Handler/Service 不需要导入具体适配器。细节与验收契约见 网络适配层。
生成项目的依赖可调用 svcCtx.RequestShutdown(err) 主动申请退出,框架排空请求、清理依赖后保留故障原因并以非零状态退出。健康检查与 OpenAPI 的开关、路径通过 server.endpoints 配置,健康响应默认沿用项目公共体;未就绪保持 HTTP 503。完整配置和 v0.12 项目的升级步骤见 项目指南。
Run 处理 SIGINT/SIGTERM;RunContext 接收调用方控制的退出信号。服务先停止接收新请求,等待活跃请求完成,再清理资源;超时会取消请求并关闭连接,业务代码仍需响应 context 取消。
日常开发与升级
在已生成的 API 项目目录执行:
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、omni.gen.json、生成文件和 .goOmni/apiManifest.json 一起提交,CI 同时运行 generate --check 与 go test ./...。详见 契约变更提示与检查边界。
CLI 与业务 module 固定使用 v0.20.0-preview。v0.14–v0.19 项目先按 配置入口迁移 整理旧 Load,再升级依赖与 CLI 并重新生成,框架维护的 main/runtimeGen 自动更新;v0.13 及更早项目按 升级预览 处理,不直接覆盖手写入口或业务文件。使用过旧 mapping 的项目必须先完成 移除迁移,旧配置或旧生成文件会阻止生成。升级后执行生成检查与业务回归。版本变化、迁移边界和验收记录见 发布说明。
按需深入
| 需要了解 | 文档 |
|---|---|
.api 类型、参数绑定、错误、鉴权和生成规则 |
API 开发指南 |
| 公共响应体、提示优先级、独立回调协议 | 响应参考 |
| CLI 完整参数、本地框架开发与迁移 | CLI |
| 当前生成项目:Go 错误目录、上传、回调和下载 | managedService |
| 数据库/事务业务参考(旧目录示例) | userService |
| 手写接口分层示例 | reference |
| 框架成熟度、历史变更 | 路线图、更新记录 |
| 网络性能、测试条件与适用范围 | 性能说明、HTTP/2 验收 |
当前版本可用于固定版本接入与业务验证,仍标记为 preview。.api 借鉴 go-zero 的接口开发方式,支持范围以本仓库文档为准,不是完整 goctl 语法兼容层;HTTP/2 保持实验状态,OSS/R2 云账号实网验收不属于本次发布范围。
后续开发约定
每次功能开发完成,同步更新这份功能索引、用法说明、examples/ 和发布记录;删除或改变功能必须注明影响与迁移。代码、示例和文档一起回归后再发布。约定已记录在 AGENTS.md,后续开发继续遵循。