# GoOmni GoOmni 是面向多个业务项目复用的 Go 后端框架:用 `.api` 定义接口、请求和返回类型,生成项目目录、HTTP 接入代码及 OpenAPI,业务代码负责 Logic 和 Repository。也可以在已有项目中直接使用 `omni.New()`,或单独引入日志、存储等模块。 **从这份 README 开始接入。** 当前版本 **`v0.18.0-preview`**,新生成项目采用根目录入口和 `config / svc / logic / handler`。可以固定版本开展业务接入;文件维护边界和已有服务迁移步骤见 [项目指南](PROJECT.md),版本变化与验收见 [发布说明](RELEASE.md)。 **v0.17 提供**:公共字段组合、可选结构映射、带依赖的中间件工厂、按需 Repository 骨架,使用与迁移见 [生成能力指南](GENERATION.md)。 **本版新增**:可空嵌套映射、映射配置按文件拆分、显式 nil 策略和可选指针返回辅助,见 [生成能力指南](GENERATION.md)。 Go **1.22+**。框架不依赖 Gin,默认使用标准库网络层,提供可切换的 native HTTP/1.1 和实验性 HTTP/2 适配器。 响应字段、业务码、登录方式、权限规则和数据库表结构由接入项目决定。文中的 `code/msg/data`、`200/9999` 都是示例配置,可以替换。 本版补齐 `returns([]string/[]int64/[]bool/[]any)`、OpenAPI 公共引用、workspace 一次导出共享 Go 错误目录,以及统一环境变量别名覆盖。详细配置与升级步骤见 [业务开发指南](DEVELOPMENT.md)。 已有通用开发能力见 [业务开发指南](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.18.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 # 项目配置 ├── 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`,再执行后面的生成命令。这里的响应格式、错误码和提示都由这个业务项目定义。 ```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.18.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/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` 引用: ```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`、`omni.gen.json`、生成文件和 `.goOmni/apiManifest.json` 一起提交,CI 同时运行 `generate --check` 与 `go test ./...`。详见 [契约变更提示与检查边界](PROJECT.md#契约变更提示)。 CLI 与业务 module 固定使用 `v0.18.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 云账号实网验收不属于本次发布范围。