PowerX API 命名与访问规范(/api/v1、/admin、/internal 边界)。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
PowerX HTTP Handler 规则(绑定校验、统一回包、无 DB IO)。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
本文件内嵌规则。kind: ruleset
name: crud_handler_http
version: 1.0.0
owner: powerx
status: stable
meta:
intent: >
规范 HTTP Handler 的目录、职责与调用形态:参数绑定/校验 → 调用 Service → 统一回包/错误桥接,
禁止业务与 DB/外部 IO;分页、错误、SSE 事件与 REST 契约/DTO 对齐。
references:
- dev_crud_http_guides.md
- constitution.md
scope:
applies_to:
- "internal/transport/http/**/**_handler.go"
- "internal/transport/http/**/api.go"
principles:
- Handler 只做 绑定/校验 → 调 Service → 回包;禁止写业务、DB 或外部 IO。 # 指南·Handler职责
- DTO 独立于模型;参数绑定使用统一函数,错误使用 AppError 桥接返回。 # 指南·DTO/错误桥接
- 路由注册独立在 api.go;前缀使用 /api/v1/{admin|open|web}。 # 指南·路由与版本
- 多租户上下文来自中间件;缺失租户返回 400 语义,鉴权在 Service 落实。 # 宪章·多租户
checks:
# 目录 & 入口
- id: http.dir.shape
level: error
when:
glob: "internal/transport/http/**/api.go"
assert:
- must_define: "func Register*Routes(*gin.RouterGroup, *shared.Deps)"
- must_prefix_route_one_of: ["/api/v1/admin", "/api/v1/open", "/api/v1/web"]
# Handler 职责边界
- id: handler.no_db_or_io
level: error
when:
glob: "internal/transport/http/**/**_handler.go"
assert:
- must_not_import: ["database/sql","gorm.io/gorm","net/http"] # 允许 gin 但禁止直接 http client
- must_not_call: ["sql.DB","gorm.DB","http.DefaultClient.Do","os.Remove","ioutil.ReadAll"]
- must_define_like: "type *Handler struct { svc *service.* }" # 依赖仅指向 Service
- id: handler.ctor
level: error
when:
glob: "internal/transport/http/**/**_handler.go"
assert:
- must_define_like: "func New*Handler(*service.*) *"
- must_not_define: "func New*Handler(*gorm.DB)" # 禁止直注 DB
# 租户上下文与校验/绑定
- id: handler.binding_and_tenant
level: error
when:
glob: "internal/transport/http/**/**_handler.go"
assert:
- must_call_one_of: ["ValidateRequestWithContext(","ValidateAndBindWithContext("]
- must_call: ["reqctx.From(c)"] # 从中间件注入的上下文取 tenant/request_id
- on_missing_tenant_http: 400
# 统一回包与错误桥接
- id: handler.response_envelope
level: error
when:
glob: "internal/transport/http/**/**_handler.go"
assert:
- must_use_one_of: ["ResponseSuccess(","RespondOK(","RespondErrorFrom("]
- http_status_in: [200,201,204,400,401,403,404,409,429,500]
# 分页语义统一
- id: handler.pagination_contract
level: error
when:
glob: "internal/transport/http/**/**_handler.go"
assert:
- must_bind_dto: ["PaginationRequest"]
- must_return: ["ResponseList","PaginationResponse"]
# SSE/WS(若涉及)
- id: handler.sse_events
level: warn
when:
contains: "WriteToSSE("
assert:
- must_use_events:
["start","intent","plan","token","data","action","final","end","error","heartbeat"]
# 契约一致性(与 REST 资源路由)
- id: handler.crud_shape
level: error
when:
glob: "internal/transport/http/**/api.go"
assert:
- must_register_methods:
- 'POST ""'
- 'GET ""'
- 'GET "/:id"'
- 'PATCH "/:id"'
- 'DELETE "/:id"'
# 依赖注入(Deps)
- id: handler.deps_injection
level: error
when:
glob: "internal/transport/http/**/**_handler.go"
assert:
- must_reference: "*shared.Deps" # Handler 通过 Deps 链路拿到 svc(在 api.go 中)
- must_not_new_external_clients: true
acceptance:
checklist:
- "[ ] api.go 只做路由注册,使用 /api/v1/{admin|open|web} 前缀"
- "[ ] Handler 仅做绑定/校验/调用 Service/回包,不含 DB 与外部 IO"
- "[ ] 使用 ValidateRequestWithContext/RespondErrorFrom/ResponseSuccess 等统一工具"
- "[ ] 分页使用 PaginationRequest/PaginationResponse,列表用 ResponseList"
- "[ ] SSE 事件(若有)使用统一事件名"
- "[ ] 租户上下文来自中间件,缺失返回 400;鉴权/审计在 Service 层"
- "[ ] Handler 构造函数存在,依赖通过 *shared.Deps 提供 svc"
templates:
handler_go: |
package {{domain}}
import (
"github.com/gin-gonic/gin"
"github.com/ArtisanCloud/PowerX/internal/app/shared"
"github.com/ArtisanCloud/PowerX/internal/service/{{domain}}"
dto "{{module_path}}/internal/transport/http/{{layer}}/{{domain}}/dto"
"github.com/ArtisanCloud/PowerX/pkg/corex/iam/reqctx"
)
type {{Entity}}Handler struct {
svc *{{domain}}.{{Entity}}Service
}
func New{{Entity}}Handler(s *{{domain}}.{{Entity}}Service) *{{Entity}}Handler {
return &{{Entity}}Handler{svc: s}
}
// POST /{{domain}}/{{resource}}
func (h *{{Entity}}Handler) Create(c *gin.Context) {
ctx, rc, err := ValidateRequestWithContext(c, &dto.{{Entity}}CreateReq{})
if err != nil { RespondErrorFrom(c, err); return }
out, err := h.svc.Create(ctx, rc.TenantID, rc.ActorID, rc.RequestID, rc.TraceID, rc.Payload)
if err != nil { RespondErrorFrom(c, err); return }
ResponseSuccess(c, out)
}
// GET /{{domain}}/{{resource}}
func (h *{{Entity}}Handler) List(c *gin.Context) {
ctx, rc, err := ValidateRequestWithContext(c, &dto.{{Entity}}ListReq{})
if err != nil { RespondErrorFrom(c, err); return }
items, pg, err := h.svc.List(ctx, rc.TenantID, rc.Pagination, rc.Filters)
if err != nil { RespondErrorFrom(c, err); return }
ResponseSuccess(c, ResponseList{Items: items, Pagination: pg})
}
// GET /{{domain}}/{{resource}}/:id
func (h *{{Entity}}Handler) Get(c *gin.Context) {
ctx, rc, err := ValidateRequestWithContext(c, &dto.{{Entity}}GetReq{})
if err != nil { RespondErrorFrom(c, err); return }
out, err := h.svc.Get(ctx, rc.TenantID, rc.ID)
if err != nil { RespondErrorFrom(c, err); return }
ResponseSuccess(c, out)
}
// PATCH /{{domain}}/{{resource}}/:id
func (h *{{Entity}}Handler) Update(c *gin.Context) {
ctx, rc, err := ValidateRequestWithContext(c, &dto.{{Entity}}UpdateReq{})
if err != nil { RespondErrorFrom(c, err); return }
out, err := h.svc.Update(ctx, rc.TenantID, rc.ID, rc.Payload, rc.IfMatch) // 支持 If-Match/ETag(可选)
if err != nil { RespondErrorFrom(c, err); return }
ResponseSuccess(c, out)
}
// DELETE /{{domain}}/{{resource}}/:id
func (h *{{Entity}}Handler) Delete(c *gin.Context) {
ctx, rc, err := ValidateRequestWithContext(c, &dto.{{Entity}}DeleteReq{})
if err != nil { RespondErrorFrom(c, err); return }
err = h.svc.Delete(ctx, rc.TenantID, rc.ID, rc.Force) // 默认为软删;Force 需权限
if err != nil { RespondErrorFrom(c, err); return }
ResponseSuccess(c, ResponseSuccess{OK: true})
}
api_go: |
package {{domain}}
import (
"github.com/gin-gonic/gin"
"github.com/ArtisanCloud/PowerX/internal/app/shared"
)
func Register{{Domain}}Routes(rg *gin.RouterGroup, deps *shared.Deps) {
h := New{{Entity}}Handler(deps.{{Entity}}Service)
g := rg.Group("/{{domain}}/{{resource}}")
{
g.POST("", h.Create)
g.GET("", h.List)
g.GET("/:id", h.Get)
g.PATCH("/:id", h.Update)
g.DELETE("/:id", h.Delete)
}
}
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
PowerX API 命名与访问规范(/api/v1、/admin、/internal 边界)。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
PowerX 底座 Capability 治理与发布准入规则。用于审计 REST/OpenAPI/gRPC/Gin 生成的能力候选、正式 platform_capabilities 目录、Capability Registry 登记、agent_usable/permission_code/risk_level 元数据、ignore 清单和缺失能力;当用户要求“重新识别能力”“能力是否能发布”“补齐能力登记”“检查 capability-gen/audit/check”“底座接口暴露给插件/agent”时使用。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
PowerX REST 契约规则(资源命名、分页、错误、版本化)。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
PowerX CRUD 依赖注入规则(Deps 单入口、构造注入、跨传输复用)。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
PowerX CRUD DTO 规则(输入输出分离、分页、校验)。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
PowerX CRUD gRPC 开发规范(proto、server、拦截器、错误映射)。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.