From 38d81959466e431210a8c9b41d22e5104395a5a1 Mon Sep 17 00:00:00 2001 From: benjamin Date: Tue, 19 May 2026 22:04:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(channel-monitor):=20=E7=BA=A6=E6=9D=9F?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=20API=20=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../channel_monitor_template_service.go | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/backend/internal/service/channel_monitor_template_service.go b/backend/internal/service/channel_monitor_template_service.go index 8d2e8173..4d50952d 100644 --- a/backend/internal/service/channel_monitor_template_service.go +++ b/backend/internal/service/channel_monitor_template_service.go @@ -14,14 +14,14 @@ type ChannelMonitorRequestTemplateRepository interface { Update(ctx context.Context, t *ChannelMonitorRequestTemplate) error Delete(ctx context.Context, id int64) error List(ctx context.Context, params ChannelMonitorRequestTemplateListParams) ([]*ChannelMonitorRequestTemplate, error) - // ApplyToMonitors 把模板当前的 extra_headers / body_override_mode / body_override + // ApplyToMonitors 把模板当前的 api_mode / extra_headers / body_override_mode / body_override // 批量覆盖到指定 monitorIDs 的监控上(同时还要求这些监控当前 template_id = id, // 防止误覆盖未关联的监控)。monitorIDs 必须非空;空列表直接返回 0 不写库。 // 返回被覆盖的监控数量。 ApplyToMonitors(ctx context.Context, id int64, monitorIDs []int64) (int64, error) // CountAssociatedMonitors 统计 template_id = id 的监控数(用于 UI 展示「应用到 N 个配置」)。 CountAssociatedMonitors(ctx context.Context, id int64) (int64, error) - // ListAssociatedMonitors 列出所有 template_id = id 的监控简略信息(id/name/provider/enabled) + // ListAssociatedMonitors 列出所有 template_id = id 的监控简略信息(id/name/provider/api_mode/enabled) // 给 apply picker UI 用,避免前端再做一次 list+filter。 ListAssociatedMonitors(ctx context.Context, id int64) ([]*AssociatedMonitorBrief, error) } @@ -31,6 +31,7 @@ type AssociatedMonitorBrief struct { ID int64 Name string Provider string + APIMode string Enabled bool } @@ -53,6 +54,15 @@ func (s *ChannelMonitorRequestTemplateService) List(ctx context.Context, params return nil, err } } + if params.APIMode != "" { + if params.Provider == "" { + if err := validateAPIMode(MonitorProviderOpenAI, params.APIMode); err != nil { + return nil, err + } + } else if err := validateAPIMode(params.Provider, params.APIMode); err != nil { + return nil, err + } + } return s.repo.List(ctx, params) } @@ -69,6 +79,7 @@ func (s *ChannelMonitorRequestTemplateService) Create(ctx context.Context, p Cha t := &ChannelMonitorRequestTemplate{ Name: strings.TrimSpace(p.Name), Provider: p.Provider, + APIMode: defaultAPIMode(p.APIMode), Description: strings.TrimSpace(p.Description), ExtraHeaders: emptyHeadersIfNil(p.ExtraHeaders), BodyOverrideMode: defaultBodyMode(p.BodyOverrideMode), @@ -144,7 +155,10 @@ func validateTemplateCreateParams(p ChannelMonitorRequestTemplateCreateParams) e if err := validateProvider(p.Provider); err != nil { return ErrChannelMonitorTemplateInvalidProvider } - if err := validateBodyModeParams(p.BodyOverrideMode, p.BodyOverride); err != nil { + if err := validateAPIMode(p.Provider, p.APIMode); err != nil { + return ErrChannelMonitorTemplateInvalidAPIMode + } + if err := validateBodyModeForProtocol(p.Provider, p.APIMode, p.BodyOverrideMode, p.BodyOverride); err != nil { return err } if err := validateExtraHeaders(p.ExtraHeaders); err != nil { @@ -165,6 +179,13 @@ func applyTemplateUpdate(existing *ChannelMonitorRequestTemplate, p ChannelMonit if p.Description != nil { existing.Description = strings.TrimSpace(*p.Description) } + newAPIMode := defaultAPIMode(existing.APIMode) + if p.APIMode != nil { + newAPIMode = defaultAPIMode(*p.APIMode) + } + if err := validateAPIMode(existing.Provider, newAPIMode); err != nil { + return ErrChannelMonitorTemplateInvalidAPIMode + } if p.ExtraHeaders != nil { if err := validateExtraHeaders(*p.ExtraHeaders); err != nil { return err @@ -180,14 +201,29 @@ func applyTemplateUpdate(existing *ChannelMonitorRequestTemplate, p ChannelMonit if p.BodyOverride != nil { newBody = *p.BodyOverride } - if err := validateBodyModeParams(newMode, newBody); err != nil { + if err := validateBodyModeForProtocol(existing.Provider, newAPIMode, newMode, newBody); err != nil { return err } + existing.APIMode = newAPIMode existing.BodyOverrideMode = defaultBodyMode(newMode) existing.BodyOverride = newBody return nil } +// validateBodyModeForProtocol 校验 body_override_mode 与 provider/api_mode 的协议特定要求。 +func validateBodyModeForProtocol(provider, apiMode, mode string, body map[string]any) error { + if err := validateBodyModeParams(mode, body); err != nil { + return err + } + if defaultBodyMode(mode) != MonitorBodyOverrideModeReplace { + return nil + } + if err := validateReplaceRequestBody(provider, defaultAPIMode(apiMode), body); err != nil { + return ErrChannelMonitorInvalidRequestBody + } + return nil +} + // validateBodyModeParams 校验 body_override_mode 合法,且 merge/replace 模式下 body_override 非空。 func validateBodyModeParams(mode string, body map[string]any) error { switch mode {