package dingtalk import ( "context" "fmt" "time" openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" dingtalkcard_1_0 "github.com/alibabacloud-go/dingtalk/card_1_0" dingtalkim_1_0 "github.com/alibabacloud-go/dingtalk/im_1_0" dingtalkoauth2_1_0 "github.com/alibabacloud-go/dingtalk/oauth2_1_0" "github.com/alibabacloud-go/tea/tea" ) // Client 钉钉官方 SDK 客户端(按机器人凭证一份实例) type Client struct { ClientID string ClientSecret string cardClient *dingtalkcard_1_0.Client imClient *dingtalkim_1_0.Client oauthClient *dingtalkoauth2_1_0.Client } // NewDingTalkClient 创建钉钉官方客户端 func NewDingTalkClient(clientID, clientSecret string) *Client { config := &openapi.Config{ Protocol: tea.String("https"), RegionId: tea.String("central"), } cardClient, _ := dingtalkcard_1_0.NewClient(config) imClient, _ := dingtalkim_1_0.NewClient(config) oauthClient, _ := dingtalkoauth2_1_0.NewClient(config) return &Client{ ClientID: clientID, ClientSecret: clientSecret, imClient: imClient, oauthClient: oauthClient, cardClient: cardClient, } } // GetAccessToken 获取 access_token(带 L1 内存 + L2 可插拔缓存 + singleflight) func (c *Client) GetAccessToken() (string, error) { return c.GetAccessTokenCtx(context.Background()) } // GetAccessTokenCtx 带 ctx 的 token 获取 func (c *Client) GetAccessTokenCtx(ctx context.Context) (string, error) { return globalTokenManager.Fetch(ctx, c.ClientID, c.refreshAccessToken) } // InvalidateAccessToken 主动失效 token 缓存(收到授权类错误时调用) func (c *Client) InvalidateAccessToken(ctx context.Context) { globalTokenManager.Invalidate(ctx, c.ClientID) } // refreshAccessToken 真正调用钉钉接口获取 token,内部使用 func (c *Client) refreshAccessToken(_ context.Context) (string, time.Duration, error) { req := &dingtalkoauth2_1_0.GetAccessTokenRequest{ AppKey: tea.String(c.ClientID), AppSecret: tea.String(c.ClientSecret), } resp, err := c.oauthClient.GetAccessToken(req) if err != nil { return "", 0, err } if resp == nil || resp.Body == nil { return "", 0, fmt.Errorf("GetAccessToken 响应为空") } token := tea.StringValue(resp.Body.AccessToken) if token == "" { return "", 0, fmt.Errorf("GetAccessToken 返回空 token") } ttl := time.Duration(tea.Int64Value(resp.Body.ExpireIn)) * time.Second if ttl <= 0 { ttl = tokenMaxTTL } return token, ttl, nil }