154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
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)
|
||
// 直接调用 TopAPI(oapi.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)
|
||
// 直接调用 TopAPI(oapi.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
|
||
}
|