package douyin import ( "context" "encoding/json" "errors" "fmt" "io" "net/http" "strings" "time" "bindbox-game/internal/service/sysconfig" ) const ( // 抖音 code2session 接口地址 Code2SessionURL = "https://developer.toutiao.com/api/apps/v2/jscode2session" ) // Code2SessionRequest 抖音登录请求参数 type Code2SessionRequest struct { AppID string `json:"appid"` Secret string `json:"secret"` Code string `json:"code,omitempty"` AnonymousCode string `json:"anonymous_code,omitempty"` } // Code2SessionResponse 抖音登录响应 type Code2SessionResponse struct { ErrNo int `json:"err_no"` ErrTips string `json:"err_tips"` Data struct { SessionKey string `json:"session_key"` OpenID string `json:"openid"` AnonymousOpenID string `json:"anonymous_openid"` UnionID string `json:"unionid"` } `json:"data"` } // Code2Session 调用抖音 code2session 接口获取用户信息 func Code2Session(ctx context.Context, code, anonymousCode string) (*Code2SessionResponse, error) { // 从动态配置获取抖音配置 dynamicCfg := sysconfig.GetGlobalDynamicConfig() if dynamicCfg == nil { return nil, errors.New("动态配置服务未初始化") } douyinCfg := dynamicCfg.GetDouyin(ctx) if douyinCfg.AppID == "" || douyinCfg.AppSecret == "" { return nil, errors.New("抖音小程序配置不完整,请在系统配置中设置 AppID 和 AppSecret") } // 构建请求 reqBody := Code2SessionRequest{ AppID: douyinCfg.AppID, Secret: douyinCfg.AppSecret, Code: code, AnonymousCode: anonymousCode, } jsonBody, err := json.Marshal(reqBody) if err != nil { return nil, fmt.Errorf("序列化请求失败: %w", err) } // 发送请求 httpClient := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequestWithContext(ctx, "POST", Code2SessionURL, strings.NewReader(string(jsonBody))) if err != nil { return nil, fmt.Errorf("创建请求失败: %w", err) } req.Header.Set("Content-Type", "application/json") resp, err := httpClient.Do(req) if err != nil { return nil, fmt.Errorf("请求抖音接口失败: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("读取响应失败: %w", err) } // 解析响应 var result Code2SessionResponse if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("解析响应失败: %w, body: %s", err, string(body)) } if result.ErrNo != 0 { return nil, fmt.Errorf("抖音登录失败: %s (code: %d)", result.ErrTips, result.ErrNo) } return &result, nil }