tools/goctl/rpc/CHANGELOG-cn.md
--proto_path / -I)新增通过 -I / --proto_path 标志导入外部目录中的 proto 文件,支持完整的传递性依赖解析。
涉及文件:
generator/gen.go — ZRpcContext 新增 ProtoPaths 字段;新增 resolveImportedProtos() 在代码生成前填充 ImportedProtos。generator/genpb.go — 新增 buildProtocCmd() 自动发现并追加传递性导入的 proto 文件到 protoc 命令;新增 relativeToProtoPath() 计算正确的相对路径。parser/import.go — 新文件(主要新增)。实现 ResolveImports() 递归解析传递性导入,ParseImportedProtos() 提取导入 proto 的 go_package / package 元数据,BuildProtoPackageMap() 构建按 proto 包名的 O(1) 查找表。parser/proto.go — Proto 结构体新增 ImportedProtos []ImportedProto 字段。cli/cli.go — RPCNew 传递 ProtoPaths 到 ZRpcContext。cli/zrpc.go — 将 VarStringSliceProtoPath 传递到 ZRpcContext.ProtoPaths。前后对比:
| 变更前 | 变更后 | |
|---|---|---|
| 从外部目录导入 Proto | ❌ 不支持,所有类型必须在同一文件中定义 | ✅ 使用 -I ./ext_protos 添加搜索路径 |
| 传递性导入(A → B → C) | ❌ 仅识别直接导入 | ✅ 递归解析所有传递性依赖 |
导入 proto 的 .pb.go 生成 | ❌ 需手动为每个文件单独运行 protoc | ✅ 自动将导入的 proto 追加到 protoc 命令 |
| Proto 搜索路径 | ❌ 仅源文件所在目录 | ✅ 支持多个 -I 路径,与 protoc 一致 |
行为说明:
import 声明,跳过 google/* 知名类型。-I 目录中搜索被导入的文件,未找到的系统级 proto 静默跳过。protoc 命令,使其 .pb.go 文件与主 proto 一同生成。当导入的 proto 与主 proto 具有不同的 go_package 时,goctl 现在能够自动在 server、logic 和 client 代码中生成正确的 Go 导入路径和限定类型引用。
涉及文件:
generator/typeref.go — 新文件,核心类型解析引擎:
resolveRPCTypeRef() — 将 proto RPC 类型(简单类型、同包点号类型、跨包点号类型、Google WKT)解析为带正确导入路径的 Go 类型引用。resolveCallTypeRef() — 客户端代码生成变体,支持类型别名。googleWKTTable — 全部 16 种 Google 知名类型到 Go 等价类型的映射表。generator/genserver.go — genFunctions() 调用 resolveRPCTypeRef() 解析请求/响应类型并收集额外导入路径。generator/genlogic.go — genLogicFunction() 使用 resolveRPCTypeRef();新增 addLogicImports() 按需添加主 pb 导入和跨包导入。generator/gencall.go — genFunction() 和 getInterfaceFuncs() 使用 resolveCallTypeRef() 处理类型别名和额外导入;新增 buildExtraImportLines() 辅助函数。generator/call.tpl — 新增 {{.extraImports}} 占位符用于跨包导入行。前后对比:
| Proto 类型 | 变更前 | 变更后 |
|---|---|---|
GetReq(同文件) | pb.GetReq | pb.GetReq(无变化) |
ext.ExtReq(相同 go_package) | ❌ 报错:"request type must defined in" | ✅ pb.ExtReq — 合并到主包 |
common.TypesReq(不同 go_package) | ❌ 报错:"request type must defined in" | ✅ common.TypesReq + 自动生成 import "example.com/demo/pb/common" |
google.protobuf.Empty | ❌ 报错:"request type must defined in" | ✅ emptypb.Empty + 自动生成导入 |
行为说明:
GetReq)解析为 pb.GetReq,无额外导入。ext.ExtReq,其中 ext 与主 proto 有相同的 go_package)解析为 pb.ExtReq。common.TypesReq,其中 common 有不同的 go_package)解析为 common.TypesReq,并自动添加正确的 Go 导入路径。Google protobuf 知名类型现在可以直接用作 RPC 的请求/响应类型(而不仅仅是消息字段)。
涉及文件:
generator/typeref.go — resolveGoogleWKT() + googleWKTTable 处理所有标准类型。前后对比:
| Proto 类型 | 变更前(作为 RPC 参数) | 变更后(作为 RPC 参数) |
|---|---|---|
google.protobuf.Empty | ❌ 报错 | ✅ emptypb.Empty |
google.protobuf.Timestamp | ❌ 报错 | ✅ timestamppb.Timestamp |
google.protobuf.Duration | ❌ 报错 | ✅ durationpb.Duration |
google.protobuf.Any | ❌ 报错 | ✅ anypb.Any |
google.protobuf.Struct | ❌ 报错 | ✅ structpb.Struct |
google.protobuf.FieldMask | ❌ 报错 | ✅ fieldmaskpb.FieldMask |
google.protobuf.*Value | ❌ 报错 | ✅ wrapperspb.*Value |
注:这些类型此前已可用作消息字段。本次变更使其可直接用作 RPC 请求/响应类型。
完整类型映射表:
| Proto 类型 | Go 类型 |
|---|---|
google.protobuf.Empty | emptypb.Empty |
google.protobuf.Timestamp | timestamppb.Timestamp |
google.protobuf.Duration | durationpb.Duration |
google.protobuf.Any | anypb.Any |
google.protobuf.Struct | structpb.Struct |
google.protobuf.Value | structpb.Value |
google.protobuf.ListValue | structpb.ListValue |
google.protobuf.FieldMask | fieldmaskpb.FieldMask |
google.protobuf.*Value(包装类型) | wrapperspb.*Value |
此前 goctl 会拒绝 RPC 请求/响应类型中包含点号的情况(如 base.Req),要求所有类型必须定义在同一个 proto 文件中。此限制已移除。
前后对比:
| Proto 定义 | 变更前 | 变更后 |
|---|---|---|
rpc Fetch(base.Req) returns (base.Reply) | ❌ 解析错误:"request type must defined in xxx.proto" | ✅ 解析成功,base.Req 通过导入的 proto 解析 |
rpc Ping(google.protobuf.Empty) returns (Reply) | ❌ 解析错误:"request type must defined in xxx.proto" | ✅ 解析成功,解析为 emptypb.Empty |
涉及文件:
parser/service.go — 移除了拒绝点号类型名的验证循环(原错误信息为 "request type must defined in" / "returns type must defined in")。parser/parser_test.go — TestDefaultProtoParseCaseInvalidRequestType 和 TestDefaultProtoParseCaseInvalidResponseType 重命名并更新,验证点号类型现在可以正常解析。