--- name: crud-grpc description: PowerX CRUD gRPC 开发规范(proto、server、拦截器、错误映射)。 --- # PowerX CRUD gRPC ## 步骤 1) 打开 `本文件内嵌规则`。 2) 按规则执行实现/校对。 3) 完成后按核对清单验收。 ## 核对点 - 与 PowerX 当前代码结构、路径与命名一致。 - 仅在传输层/契约层做职责内改动,不跨层越界。 ## 规则(内嵌) ### dev_crud_grpc_guides.md ````markdown # PowerX CRUD – gRPC Guides > 本文与《dev_crud_http_guides.md》并列,复用同一套 **Model / Repository / Service** 主干,仅替换传输层为 gRPC。 > 入口仍由 `constitution.md` 控制,别名解析由同级 `manifest.yaml` 完成(`@dev-crud-grpc`)。 --- ## 1. 适用范围(Scope) * 与 HTTP 版 CRUD 规范**等价**:创建/查询/更新/删除、分页、错误、审计、多租户。 * 仅规定 **gRPC 传输层**:Server 行为、拦截器、错误映射、分页/上下文 DTO 对齐 **proto 契约**。 * 宪章入口仍为 `.specify/memory/constitution.md`(由其 `use: "` 决定是否引入本指南)。" --- ## 2. 目录与生成物(与你仓库对齐) ``` api/grpc/ contracts/ # *.proto 契约(Buf 维护) buf.yaml buf.gen.yaml common/v1/{context,pagination,resource,response}.proto powerx/{iam,agent,auth/sts,plugin/control,setting}/v1/*.proto gen/go/... # 代码生成产物(勿手改) sdk/{ts,rust,php}/... # 多语言 SDK(封装拨号、上下文、重试等) internal/transport/grpc/ iam/{member_handler.go,team_handler.go,...} agent/{stream_handler.go,setting_handler.go,...} auth/{sts_handler.go} auth/middleware/auth_interceptor.go ``` * 你的 `api/grpc` 与 `internal/transport/grpc` 已经是成熟形态;本指南只统一**约束与验收**。 --- ## 3. 设计原则(Transport-Agnostic) * **服务端不写业务**:gRPC Server 仅做 **参数绑定/校验 → 调 Service → 错误映射**。 示例:`MemberServer` 只解析上下文、分页、调用 `MemberService.ListMembers` 并映射返回。 * **多租户一致**:`tenant_id` 必须来自 **RequestContext 或 Metadata**;缺失返回 400 语义。 * **错误等价**:将应用错误(`ErrInvalidParam/ErrForbidden/ErrNotFound/ErrConflict`)映射为 gRPC `codes.*`;HTTP 与 gRPC 语义一致。 * **分页一致**:`common.v1.pagination.proto` 的 PageRequest/PageResponse 必须与 HTTP 页码/页大小语义一致。 * **审计与鉴权**:通过拦截器(Auth + Tenant)注入上下文,Service 侧照常记录审计;Server 不直接写审计逻辑。 --- ## 4. Proto 契约(最小统一约束) * **公共类型**:统一使用 `common/v1` 下的 `context.proto / pagination.proto / resource.proto / response.proto`。 * **包与版本**:采用 `powerx..v1`,破坏性变更才升级 `v2`。 * **分页**:推荐结构(与你现有 common 保持一致): * `PageRequest{ page, page_size, sort_by, sort_order, offset? }` * `PageResponse{ total, page, page_size, pages? }` * **Context**:`RequestContext{ tenant_id, request_id, actor_id, trace_id, ... }`,所有 RPC **强制**携带。 * 生成配置使用 Buf:`buf.yaml / buf.gen.yaml`(已存在)。 > 你现有 `powerx/iam/v1/member.proto`、`team.proto` 与 `common/v1/*.proto` 均满足上述形态,可直接沿用。 --- ## 5. Server 实现(约束与示例) ### 5.1 绑定/校验 * **tenant 提取**:优先读 `RequestContext.tenant_id`,其次仅从 Metadata `tenant-id` 兜底(不接受任何 `x-powerx-*` 遗留租户头)。 你的 `tenantIDFrom()` 已经实现这一落地逻辑。 * **分页映射**:`PageRequest(offset,page_size)` → `(page,size)` 的换算统一用工具函数(如 `pageFrom()`)。 * **错误回包**:Meta 中返回 `code/message/request_id`,与你的 `okMeta/badMeta` 一致(见 `member_handler.go`/`team_handler.go` 调用)。 ### 5.2 调用 Service * Server 仅调用同名用例,如 `MemberService.ListMembers/GetMember/...`; * 严禁在 Server 内写 DB 或外部 IO 调用(与 HTTP 版一致)。 ### 5.3 映射输出 * 领域对象 → PB:使用专用转换函数(如 `toPBMember()`),并保持 PRN/Ref 等字段预留。 * 分页总数写入 `PageResponse.total`,按需要计算 `pages`。 --- ## 6. 拦截器(Auth / Tenant / Trace / Recovery) * 统一在 `internal/transport/grpc/auth/middleware/auth_interceptor.go` 注册链: `Tracing → Auth/Tenant → Recovery → Logging/Audit(可选)`。 * Auth 成功后,将 `tenant_id`、`actor` 注入 `context.Context`,与 HTTP 中间件语义一致。 * Server 端实现保持**无状态**;租户与权限校验仍以 Service 为准。 (你已存在 `auth_interceptor.go` 路径,保持即可。) --- ## 7. 错误映射(与 HTTP 等价) | 应用错误 | gRPC Code | | ---------------------- | -------------------------------------------------- | | ErrInvalidParam / 参数缺失 | `codes.InvalidArgument` | | ErrForbidden / 越权 | `codes.PermissionDenied` | | ErrNotFound / 不存在 | `codes.NotFound` | | ErrConflict / 冲突 | `codes.AlreadyExists` 或 `codes.FailedPrecondition` | | 其他未分类 | `codes.Internal` | > 建议提供一个 `grpcerr.FromAppError(err)` 小工具集中处理映射;当前 `member_handler.go`/`team_handler.go` 以 “Meta + 200/400/500 语义”返回,你也可以逐步替换为标准 `status.Error` 形式以统一链路。 --- ## 8. 流式(Server-Streaming) * 与 SSE 事件顺序对齐:`start → data(token/partial) → final → end`;异常用 `error` 结束。 * 建议为长连接加入 heartbeat(超时时间与退避策略由客户端/SDK 控制)。 * 你的 `agent/stream_handler.go` 已是落地点,遵循以上语义即可。 --- ## 9. 代码生成(Buf) * 在 `api/grpc/contracts/` 下运行: ``` buf lint buf breaking --against 'https://github.com//.git#branch=main' buf generate ``` * 生成物落在 `api/grpc/gen/go/**`,不要手改;SDK 封装在 `api/grpc/sdk/**`。 --- ## 10. 验收要点(Checklist) * [ ] **Server 零业务**:仅绑定/校验/调用 Service/错误映射(对齐 HTTP 版职能)。 * [ ] **多租户**:`tenant_id` 必带;缺失返回 400 语义或 `codes.InvalidArgument`。 * [ ] **分页一致**:PageRequest/PageResponse 与 HTTP 语义一致(`page/page_size/pages`)。 * [ ] **错误等价**:应用错误 → `codes.*`;或临时以 Meta 语义保持一致,逐步收敛至 `status.Error`。 * [ ] **拦截器链**:Tracing → Auth/Tenant → Recovery → Logging/Audit;服务端不重复做鉴权解析细节。 * [ ] **输出映射**:使用 `toPB*`,保留 `ResourceRef/PRN` 等字段空位。 * [ ] **生成流程**:`buf lint / breaking / generate` 通过;生成物与源码分离。 * [ ] **与 HTTP 等价**:同一用例在 HTTP 与 gRPC 的语义、错误、分页完全可对照。 --- ````