# CLAUDE.md 本文件为 Claude Code (claude.ai/code) 在此仓库中工作时提供指引。 ## 常用命令 ```bash # 运行某个 service 的全部测试 go test ./services/apk/... # 运行单个测试函数 go test ./services/apk/... -run TestClient_GetUserInfo -v # 编译检查 go build ./... ``` ## 整体架构 分两层: - `sdk/`:核心框架,处理签名、HTTP 请求、响应解析,不含业务逻辑 - `services//`:各业务服务封装,每个包是一个独立 SDK client ### sdk/ 核心流程 ``` 调用方 → Client.DoAction(req, resp) → auth.Sign(req, signer) # 根据 credential 类型签名 → req.BuildUrl() # RpcRequest 拼接域名+路径+query → HTTP 请求 → responses.Unmarshal(resp) # JSON 反序列化 ``` - `sdk/auth/credentials/`:三种凭证类型(AccessKey、StsToken、AliAppcode) - `sdk/auth/signers/`:对应签名器实现 - `sdk/requests/`:`RpcRequest`(主流)和 `RoaRequest` 两种请求风格;字段通过 struct tag `position:"Body|Query|Header"` 和 `field:"xxx"` 声明参数位置 - `sdk/config.go`:`Config` 结构体,字段用 `default:"..."` tag 自动初始化 ### services/ 统一模式 每个服务包含: | 文件 | 职责 | |------|------| | `client.go` | 定义 `Client`(内嵌 `sdk.Client`)、`HOST`、`VERSION` 常量,以及所有对外方法 | | `xxx_action.go` | 每个接口一个文件,包含 Request/Response 结构体和两个工厂函数 | ## 添加新接口的标准步骤 **1. 新建 `services//new_action.go`:** ```go package import ( "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk/requests" "golib.gaore.com/GaoreGo/gaore-common-sdk-go/sdk/responses" ) type NewActionRequest struct { *requests.RpcRequest Field1 string `position:"Body" field:"field1" default:""` Field2 int64 `position:"Body" field:"field2"` } type NewActionResponse struct { *responses.BaseResponse Code int `json:"code"` Msg string `json:"msg"` Data struct { // ... } `json:"data"` } func CreateNewActionRequest() *NewActionRequest { req := &NewActionRequest{RpcRequest: &requests.RpcRequest{}} req.InitWithApiInfo(HOST, VERSION, "/api/path/here") req.Method = requests.POST return req } func CreateNewActionResponse() *NewActionResponse { return &NewActionResponse{BaseResponse: &responses.BaseResponse{}} } ``` **2. 在 `client.go` 添加方法:** ```go func (c *Client) NewAction(req *NewActionRequest) (response *NewActionResponse, err error) { response = CreateNewActionResponse() err = c.DoAction(req, response) return } ``` ## HOST 两种写法 ```go // 简单写法(内部服务,hostname 由调用方注入) var HOST = requests.Host{Default: "service-name"} // 带环境路由写法(外部服务,各环境域名不同) var HOST = requests.Host{ Default: "api.example.com", Func: func(env string) string { return map[string]string{ requests.RELEASE: "api.example.com", requests.TEST: "test.api.example.com", }[env] }, } ``` 环境常量:`requests.RELEASE`、`requests.PRE`、`requests.TEST` ## Client 初始化两种模式 ```go // 模式一:无需认证(apk、callback 等内部服务) func NewClient() *Client { client := &Client{} client.InitWithAccessKey("", "", "") return client } // 模式二:需要认证(sso、pay 等) func NewClient() (*Client, error) { client := new(Client) err := client.Init() return client, err } ```