代码生成
fun 的生成器把注册的服务变成带完整类型的客户端代码:TS(前端)与 Go(服务间调用)可选。v1.3.2 起提供 BindServiceForGen——生成不依赖任何基础设施。
生成命令
推荐独立 cmd/gen(或放测试里):
go
// cmd/gen/main.go
package main
import (
"fmt"
"github.com/cyi-cc/fun"
"hello-fun"
)
func main() {
f := fun.New()
// 只登记类型元信息,不装配任何基础设施——数据库/Redis 无需运行
f.BindServiceForGen(&OrderSvc{})
f.BindServiceForGen(&ChatSvc{})
fun.SetOutput("./frontend/src/api") // 输出目录(默认 ./gen)
fun.GenCode(fun.GenTs{}) // 加 fun.GenGo{} 同时生成 Go 客户端
fmt.Println("done")
}bash
go run ./cmd/genSetOutput 指向的目录会被清空
GenCode 执行时会 RemoveAll 整个输出目录再写入。务必指向专门的生成目录(如 frontend/src/api),不要指向源码根目录。
BindServiceForGen vs BindService
BindService | BindServiceForGen | |
|---|---|---|
| 反射登记方法 | ✅ | ✅ |
| 装配依赖(拉起 Postgres/Redis 连接) | ✅ 会 | ❌ 不会 |
| 用于 | 服务启动 | 代码生成 |
用 BindService 做生成会把基础设施真正拉起来(New() 建连接)——生成命令请始终用 BindServiceForGen。
产物结构
<out>/
├── ts/ # GenTs 产物
│ ├── client.ts # Client 类 + result<T> + 拦截器类型(零依赖)
│ ├── fun.ts # api.create(url) 聚合入口
│ ├── orderSvc.ts # 每个有方法的服务一个文件(无方法的服务不生成)
│ ├── chatSvc.ts
│ ├── createOrderDto.ts # DTO / 返回类型 interface
│ ├── orderDto.ts
│ └── orderStatus.ts # 枚举(含 DisplayNames)
└── go/ # GenGo 产物(可选)
├── fun.go
├── orderSvc.go
└── ...- 产物固定落在
<out>/ts/(Go 在<out>/go/)子目录;要拍平到目录根,生成后自行os.Rename上移一层; - 生成确定性:service / method / imports 全部按源端排序,重复生成字节一致,可安全进 CI / code review;
client.ts自包含、零运行时依赖;JS 项目也可直接import(Vite / esbuild 原生转译.ts)。
聚合入口 fun.ts
ts
import { api } from './api/ts/fun'
const c = api.create('/api') // url 会拼成 `${url}/cell`
c.orderSvc.create({ sku: 'A', count: 1 })服务属性名首字母小写(orderSvc),方法名保持原样。用法详见下一章 TS 客户端使用。
何时重新生成
服务定义(方法、DTO、枚举)一变就重新跑 go run ./cmd/gen。生成产物是纯派生代码——不要手改,改了也会被下次生成覆盖(输出目录整个清空)。
下一步
- TS 客户端使用:拦截器、AbortSignal、流式重连
- example/gen:真实产物参考