repair-agent 134ccb70f3
All checks were successful
Build and Deploy Backend / build-and-deploy (push) Successful in 5m41s
fix 音频并发优化
2026-03-03 17:21:46 +08:00

102 lines
2.8 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 rtcclient 封装对 RTC 后端 Django REST API 的 HTTP 调用。
package rtcclient
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
)
// StoryInfo 是 GET /api/v1/devices/stories/ 返回的故事信息。
type StoryInfo struct {
Title string `json:"title"`
AudioURL string `json:"audio_url"`
OpusURL string `json:"opus_url"` // 预转码 Opus JSON 地址,为空表示未转码
}
// Client 是 RTC 后端的 HTTP 客户端,复用连接池。
type Client struct {
baseURL string
httpClient *http.Client
}
// New 创建 ClientbaseURL 形如 "http://rtc-backend-svc:8000"。
func New(baseURL string) *Client {
return &Client{
baseURL: strings.TrimRight(baseURL, "/"),
httpClient: &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 50,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
// 限制重定向次数,防止无限跳转
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) >= 3 {
return errors.New("rtcclient: too many redirects")
}
return nil
},
},
}
}
// rtcResponse 是 RTC 后端的统一响应结构。
type rtcResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data"`
}
// FetchStoryByMAC 通过设备 MAC 地址获取随机故事。
// 返回 nil, nil 表示设备/用户/故事不存在(非错误,调用方直接跳过)。
func (c *Client) FetchStoryByMAC(ctx context.Context, mac string) (*StoryInfo, error) {
url := c.baseURL + "/api/v1/devices/stories/"
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("rtcclient: build request: %w", err)
}
q := req.URL.Query()
q.Set("mac_address", strings.ToUpper(mac))
req.URL.RawQuery = q.Encode()
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("rtcclient: request failed: %w", err)
}
defer resp.Body.Close()
// 404 表示设备/用户/故事不存在,不是服务器错误
if resp.StatusCode == http.StatusNotFound {
return nil, nil
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("rtcclient: unexpected status %d", resp.StatusCode)
}
var result rtcResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("rtcclient: decode response: %w", err)
}
if result.Code != 0 {
return nil, nil // 业务错误(如暂无故事),返回 nil 让调用方处理
}
var story StoryInfo
if err := json.Unmarshal(result.Data, &story); err != nil {
return nil, fmt.Errorf("rtcclient: decode story: %w", err)
}
if story.Title == "" || story.AudioURL == "" {
return nil, nil
}
return &story, nil
}