diff --git a/internal/dao/internal/aud_request_logs.go b/internal/dao/internal/aud_request_logs.go index 8cb94c2..891cb4f 100644 --- a/internal/dao/internal/aud_request_logs.go +++ b/internal/dao/internal/aud_request_logs.go @@ -52,6 +52,7 @@ type AudRequestLogsColumns struct { TaskUpstreamHeaders string // 异步任务完成时上游返回的响应头(仅审计级别为 full 时记录) TaskCompletedAt string // 异步任务达到终态的时间 AttachmentIds string // 提取的媒体附件 fil_files ID 数组(JSONB: [1,2,3]) + Model string // 请求使用的模型名称(从请求体或 URL 中提取) } // audRequestLogsColumns holds the columns for the table aud_request_logs. @@ -87,6 +88,7 @@ var audRequestLogsColumns = AudRequestLogsColumns{ TaskUpstreamHeaders: "task_upstream_headers", TaskCompletedAt: "task_completed_at", AttachmentIds: "attachment_ids", + Model: "model", } // NewAudRequestLogsDao creates and returns a new DAO object for table data access. diff --git a/internal/handler/relay/relay.go b/internal/handler/relay/relay.go index c3730f1..13378d2 100644 --- a/internal/handler/relay/relay.go +++ b/internal/handler/relay/relay.go @@ -453,11 +453,15 @@ func captureRequestHeaders(r *ghttp.Request) map[string]string { func recordAudit(r *ghttp.Request, rc *handler.RelayContext, capture *ResponseCaptureWriter, body []byte, path string, latencyMs int, ttft int) { // 解析 stream 标志 var isStream bool + var modelName string var rawRequest map[string]json.RawMessage if err := json.Unmarshal(body, &rawRequest); err == nil { if streamVal, ok := rawRequest["stream"]; ok { _ = json.Unmarshal(streamVal, &isStream) } + if modelVal, ok := rawRequest["model"]; ok { + _ = json.Unmarshal(modelVal, &modelName) + } } // 捕获响应体(流式请求记录 SSE 原始数据,非流式记录完整响应) @@ -476,6 +480,7 @@ func recordAudit(r *ghttp.Request, rc *handler.RelayContext, capture *ResponseCa StatusCode: capture.StatusCode(), ClientIP: rc.ClientIP, UserAgent: r.Header.Get("User-Agent"), + Model: modelName, RequestBody: string(body), ResponseBody: responseBody, LatencyMs: latencyMs, diff --git a/internal/logic/relay/provider.go b/internal/logic/relay/provider.go index 769dd6d..34aa549 100644 --- a/internal/logic/relay/provider.go +++ b/internal/logic/relay/provider.go @@ -540,6 +540,7 @@ func (p *DataProviderImpl) RecordAudit(ctx context.Context, record *common.Audit StatusCode: record.StatusCode, ClientIp: record.ClientIP, UserAgent: record.UserAgent, + Model: record.Model, RequestBody: sysReq, ResponseBody: sysResp, TenantRequestBody: tntReq, diff --git a/internal/model/do/aud_request_logs.go b/internal/model/do/aud_request_logs.go index 6d6dbbd..60b7f18 100644 --- a/internal/model/do/aud_request_logs.go +++ b/internal/model/do/aud_request_logs.go @@ -43,4 +43,5 @@ type AudRequestLogs struct { TaskUpstreamHeaders any // 异步任务完成时上游返回的响应头(仅审计级别为 full 时记录) TaskCompletedAt *gtime.Time // 异步任务达到终态的时间 AttachmentIds any // 提取的媒体附件 fil_files ID 数组(JSONB: [1,2,3]) + Model any // 请求使用的模型名称(从请求体或 URL 中提取) } diff --git a/internal/model/entity/aud_request_logs.go b/internal/model/entity/aud_request_logs.go index bf20994..6c30d51 100644 --- a/internal/model/entity/aud_request_logs.go +++ b/internal/model/entity/aud_request_logs.go @@ -41,4 +41,5 @@ type AudRequestLogs struct { TaskUpstreamHeaders string `json:"task_upstream_headers" orm:"task_upstream_headers" description:"异步任务完成时上游返回的响应头(仅审计级别为 full 时记录)"` // 异步任务完成时上游返回的响应头(仅审计级别为 full 时记录) TaskCompletedAt *gtime.Time `json:"task_completed_at" orm:"task_completed_at" description:"异步任务达到终态的时间"` // 异步任务达到终态的时间 AttachmentIds string `json:"attachment_ids" orm:"attachment_ids" description:"提取的媒体附件 fil_files ID 数组(JSONB: [1,2,3])"` // 提取的媒体附件 fil_files ID 数组(JSONB: [1,2,3]) + Model string `json:"model" orm:"model" description:"请求使用的模型名称(从请求体或 URL 中提取)"` // 请求使用的模型名称(从请求体或 URL 中提取) } diff --git a/relay/channel/ali/adaptor.go b/relay/channel/ali/adaptor.go index c95c943..dec6f38 100644 --- a/relay/channel/ali/adaptor.go +++ b/relay/channel/ali/adaptor.go @@ -119,10 +119,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/aws/adaptor.go b/relay/channel/aws/adaptor.go index af01819..5ffd592 100644 --- a/relay/channel/aws/adaptor.go +++ b/relay/channel/aws/adaptor.go @@ -108,10 +108,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/baidu_v2/adaptor.go b/relay/channel/baidu_v2/adaptor.go index f61f961..4339881 100644 --- a/relay/channel/baidu_v2/adaptor.go +++ b/relay/channel/baidu_v2/adaptor.go @@ -133,10 +133,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/claude/adaptor.go b/relay/channel/claude/adaptor.go index f910bd4..aeea9c0 100644 --- a/relay/channel/claude/adaptor.go +++ b/relay/channel/claude/adaptor.go @@ -129,10 +129,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/cloudflare/adaptor.go b/relay/channel/cloudflare/adaptor.go index f60c9d5..20797e6 100644 --- a/relay/channel/cloudflare/adaptor.go +++ b/relay/channel/cloudflare/adaptor.go @@ -125,10 +125,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) return client.Do(httpReq) diff --git a/relay/channel/codex/adaptor.go b/relay/channel/codex/adaptor.go index 1fc0cf6..fb18e77 100644 --- a/relay/channel/codex/adaptor.go +++ b/relay/channel/codex/adaptor.go @@ -103,10 +103,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/coze/adaptor.go b/relay/channel/coze/adaptor.go index 1e2e811..721855b 100644 --- a/relay/channel/coze/adaptor.go +++ b/relay/channel/coze/adaptor.go @@ -96,6 +96,9 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request if timeout <= 0 { timeout = 120 // Coze 可能响应较慢,给更长超时 } + if constant.RelayMode(info.RelayMode) == constant.RelayModeImagesGenerations && timeout < 600 { + timeout = 600 + } client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/deepseek/adaptor.go b/relay/channel/deepseek/adaptor.go index cc45e31..24fd58f 100644 --- a/relay/channel/deepseek/adaptor.go +++ b/relay/channel/deepseek/adaptor.go @@ -208,6 +208,9 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request if timeout <= 0 { timeout = 300 } + if constant.RelayMode(info.RelayMode) == constant.RelayModeImagesGenerations && timeout < 600 { + timeout = 600 + } client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/dify/adaptor.go b/relay/channel/dify/adaptor.go index 038ab22..24e3a3a 100644 --- a/relay/channel/dify/adaptor.go +++ b/relay/channel/dify/adaptor.go @@ -93,6 +93,9 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request if timeout <= 0 { timeout = 120 // Dify 应用可能包含复杂工作流,给更长超时 } + if constant.RelayMode(info.RelayMode) == constant.RelayModeImagesGenerations && timeout < 600 { + timeout = 600 + } client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/gemini/adaptor.go b/relay/channel/gemini/adaptor.go index 8ddd21d..8b02f1f 100644 --- a/relay/channel/gemini/adaptor.go +++ b/relay/channel/gemini/adaptor.go @@ -297,10 +297,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/jimeng/adaptor.go b/relay/channel/jimeng/adaptor.go index 3ab426d..413166c 100644 --- a/relay/channel/jimeng/adaptor.go +++ b/relay/channel/jimeng/adaptor.go @@ -118,6 +118,9 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request if timeout <= 0 { timeout = 120 // 图像生成通常较慢 } + if constant.RelayMode(info.RelayMode) == constant.RelayModeImagesGenerations && timeout < 600 { + timeout = 600 + } client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/minimax/adaptor.go b/relay/channel/minimax/adaptor.go index 83a053d..3a54242 100644 --- a/relay/channel/minimax/adaptor.go +++ b/relay/channel/minimax/adaptor.go @@ -105,10 +105,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/mistral/adaptor.go b/relay/channel/mistral/adaptor.go index 2e769e4..49d6163 100644 --- a/relay/channel/mistral/adaptor.go +++ b/relay/channel/mistral/adaptor.go @@ -94,10 +94,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/moonshot/adaptor.go b/relay/channel/moonshot/adaptor.go index 3fb9f69..2d6fa22 100644 --- a/relay/channel/moonshot/adaptor.go +++ b/relay/channel/moonshot/adaptor.go @@ -102,10 +102,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/ollama/adaptor.go b/relay/channel/ollama/adaptor.go index 56ceddf..e5ba06d 100644 --- a/relay/channel/ollama/adaptor.go +++ b/relay/channel/ollama/adaptor.go @@ -109,11 +109,15 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } + // Ollama 默认 300s 超时(大模型推理可能很慢) // Ollama 默认 300s 超时(大模型推理可能很慢) timeout := info.ChannelMeta.Settings.TimeoutSeconds if timeout <= 0 { timeout = 300 } + if constant.RelayMode(info.RelayMode) == constant.RelayModeImagesGenerations && timeout < 600 { + timeout = 600 + } client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go index 2405e0b..22e201b 100644 --- a/relay/channel/openai/adaptor.go +++ b/relay/channel/openai/adaptor.go @@ -207,10 +207,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request } } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/openai/realtime.go b/relay/channel/openai/realtime.go index fa31b5d..d449509 100644 --- a/relay/channel/openai/realtime.go +++ b/relay/channel/openai/realtime.go @@ -66,10 +66,7 @@ func (p *RealtimeProxy) DialUpstream() error { targetURL := buildRealtimeURL(p.info.ChannelMeta.BaseURL) header := buildRealtimeHeaders(p.info) - timeout := p.info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := p.info.ChannelMeta.Settings.GetTimeoutSeconds(p.info.RelayMode) dialer := websocket.Dialer{ HandshakeTimeout: time.Duration(timeout) * time.Second, diff --git a/relay/channel/siliconflow/adaptor.go b/relay/channel/siliconflow/adaptor.go index 1f56d48..680085b 100644 --- a/relay/channel/siliconflow/adaptor.go +++ b/relay/channel/siliconflow/adaptor.go @@ -117,10 +117,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) return client.Do(httpReq) diff --git a/relay/channel/submodel/adaptor.go b/relay/channel/submodel/adaptor.go index f197541..ddcbf2c 100644 --- a/relay/channel/submodel/adaptor.go +++ b/relay/channel/submodel/adaptor.go @@ -88,10 +88,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/tencent/adaptor.go b/relay/channel/tencent/adaptor.go index 6bea976..636b1e2 100644 --- a/relay/channel/tencent/adaptor.go +++ b/relay/channel/tencent/adaptor.go @@ -159,10 +159,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request httpReq.Header.Set("Authorization", authorization) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/vertex/adaptor.go b/relay/channel/vertex/adaptor.go index 3a179e0..84757bf 100644 --- a/relay/channel/vertex/adaptor.go +++ b/relay/channel/vertex/adaptor.go @@ -154,10 +154,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/volcengine/adaptor.go b/relay/channel/volcengine/adaptor.go index 5165fcd..8892a40 100644 --- a/relay/channel/volcengine/adaptor.go +++ b/relay/channel/volcengine/adaptor.go @@ -112,10 +112,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/xai/adaptor.go b/relay/channel/xai/adaptor.go index 22bf64a..0f56faf 100644 --- a/relay/channel/xai/adaptor.go +++ b/relay/channel/xai/adaptor.go @@ -125,10 +125,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/xunfei/adaptor.go b/relay/channel/xunfei/adaptor.go index a31f46d..8bc699a 100644 --- a/relay/channel/xunfei/adaptor.go +++ b/relay/channel/xunfei/adaptor.go @@ -96,10 +96,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/channel/zhipu/adaptor.go b/relay/channel/zhipu/adaptor.go index d7722d4..8a3b5f9 100644 --- a/relay/channel/zhipu/adaptor.go +++ b/relay/channel/zhipu/adaptor.go @@ -192,13 +192,7 @@ func (a *Adaptor) DoRequest(ctx context.Context, info *common.RelayInfo, request override.MergeHeaderOverrides(httpReq.Header, hdrOverrides) } - timeout := info.ChannelMeta.Settings.TimeoutSeconds - if timeout <= 0 { - timeout = 60 - if constant.RelayMode(info.RelayMode) == constant.RelayModeImagesGenerations { - timeout = 300 - } - } + timeout := info.ChannelMeta.Settings.GetTimeoutSeconds(info.RelayMode) client := common.NewPooledClient(timeout, info.ChannelMeta.Settings.UseProxy, info.IsStream) diff --git a/relay/common/provider.go b/relay/common/provider.go index 0606938..d096a0c 100644 --- a/relay/common/provider.go +++ b/relay/common/provider.go @@ -217,6 +217,7 @@ type AuditRecord struct { StatusCode int ClientIP string UserAgent string + Model string // 请求使用的模型名称 RequestBody string ResponseBody string LatencyMs int diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 21aba2c..2a8a3b0 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -20,6 +20,23 @@ type ChannelMeta struct { Settings ChannelSettings } +const ( + DefaultTimeoutSeconds = 60 + ImagesGenerationTimeoutSecs = 600 +) + +// GetTimeoutSeconds 返回请求超时秒数。 +// 渠道自定义优先,未配置时根据请求模式返回默认值(图片生成 600s,其余 60s)。 +func (s ChannelSettings) GetTimeoutSeconds(relayMode int) int { + if s.TimeoutSeconds > 0 { + return s.TimeoutSeconds + } + if constant.RelayMode(relayMode) == constant.RelayModeImagesGenerations { + return ImagesGenerationTimeoutSecs + } + return DefaultTimeoutSeconds +} + // ChannelSettings 渠道配置(来自 chn_channels.settings JSONB) type ChannelSettings struct { TimeoutSeconds int `json:"timeout_seconds"` // 请求超时秒数,默认 60