ai_scheduler/internal/pkg/dingtalk/client.go

154 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package dingtalk
import (
"ai_scheduler/internal/config"
"bytes"
"context"
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
)
// Client 使用官方接口获取 AccessToken并通过 HTTP 直接调用 TopAPI
type Client struct {
cfg *config.Config
}
// NewDingTalkClient 基于配置创建钉钉客户端(无 fastwego 依赖)
func NewDingTalkClient(cfg *config.Config) *Client { return &Client{cfg: cfg} }
type UserDetail struct {
UserID string `json:"userid"`
Name string `json:"name"`
UnionID string `json:"unionid"`
}
// getAccessToken 通过官方接口获取应用 AccessToken简单实现每次请求刷新不做缓存
func (c *Client) getAccessToken(ctx context.Context) (string, error) {
// 调用旧版 OAPI 获取 access_token供 /topapi 使用
appKey := c.cfg.Tools.DingTalkBot.APIKey
appSecret := c.cfg.Tools.DingTalkBot.APISecret
params := url.Values{}
params.Add("appkey", appKey)
params.Add("appsecret", appSecret)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://oapi.dingtalk.com/gettoken?"+params.Encode(), nil)
if err != nil {
return "", err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
var r struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}
if err := json.Unmarshal(b, &r); err != nil {
return "", err
}
if r.ErrCode != 0 || r.AccessToken == "" {
if r.ErrMsg == "" {
r.ErrMsg = "gettoken failed"
}
return "", errors.New(r.ErrMsg)
}
return r.AccessToken, nil
}
// QueryUserDetails 按 userId 查询用户详情
func (c *Client) QueryUserDetails(ctx context.Context, userId string) (*UserDetail, error) {
accessToken, err := c.getAccessToken(ctx)
if err != nil {
return nil, err
}
body := struct {
UserId string `json:"userid"`
Language string `json:"language,omitempty"`
}{UserId: userId, Language: "zh_CN"}
b, _ := json.Marshal(body)
// 直接调用 TopAPIoapi.dingtalk.com通过 query 参数携带 access_token
// 官方 SDK 无 topapi 封装,此处保留原接口以保证行为一致
params := url.Values{}
params.Add("access_token", accessToken)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://oapi.dingtalk.com/topapi/v2/user/get?"+params.Encode(), bytes.NewReader(b))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer httpResp.Body.Close()
res, err := io.ReadAll(httpResp.Body)
if err != nil {
return nil, err
}
var resp struct {
Code int `json:"errcode"`
Msg string `json:"errmsg"`
Result UserDetail `json:"result"`
}
if err := json.Unmarshal(res, &resp); err != nil {
return nil, err
}
if resp.Code != 0 {
return nil, errors.New(resp.Msg)
}
return &resp.Result, nil
}
// QueryUserDetailsByMobile 按手机号查询用户详情
func (c *Client) QueryUserDetailsByMobile(ctx context.Context, mobile string) (*UserDetail, error) {
accessToken, err := c.getAccessToken(ctx)
if err != nil {
return nil, err
}
body := struct {
Mobile string `json:"mobile"`
}{Mobile: mobile}
b, _ := json.Marshal(body)
// 直接调用 TopAPIoapi.dingtalk.com通过 query 参数携带 access_token
params := url.Values{}
params.Add("access_token", accessToken)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://oapi.dingtalk.com/topapi/v2/user/getbymobile?"+params.Encode(), bytes.NewReader(b))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer httpResp.Body.Close()
res, err := io.ReadAll(httpResp.Body)
if err != nil {
return nil, err
}
var resp struct {
Code int `json:"errcode"`
Msg string `json:"errmsg"`
Result UserDetail `json:"result"`
}
if err := json.Unmarshal(res, &resp); err != nil {
return nil, err
}
if resp.Code != 0 {
return nil, errors.New(resp.Msg)
}
return &resp.Result, nil
}