Files
smartmate/backend/notification/mock_provider.go
Losita 0a014f7472 Version: 0.9.60.dev.260430
后端:
1.接入主动调度 worker 与飞书通知链路
- 新增 due job scanner 与 active_schedule.triggered workflow
- 接入 notification.feishu.requested handler、飞书 webhook provider 和用户通知配置接口
- 支持 notification_records 去重、重试、skipped/dead 状态流转
- 完成 api / worker / all 启动模式装配与主动调度验收记录
2.后续要做的就是补全从异常发生到给用户推送消息之间的逻辑缺口
2026-04-30 23:45:27 +08:00

143 lines
4.1 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 notification
import (
"context"
"fmt"
"sync"
"time"
)
// MockFeishuMode 描述 mock provider 下一次返回哪类结果。
type MockFeishuMode string
const (
MockFeishuModeSuccess MockFeishuMode = "success"
MockFeishuModeTemporaryFail MockFeishuMode = "temporary_fail"
MockFeishuModePermanentFail MockFeishuMode = "permanent_fail"
)
// MockFeishuProvider 是进程内 mock provider。
//
// 职责边界:
// 1. 只用于本地联调、单元测试和阶段性验收;
// 2. 不做真实 HTTP 调用,直接根据预设 mode 返回 success / temporary_fail / permanent_fail
// 3. 保留调用历史,方便测试断言“有没有重复发飞书”。
type MockFeishuProvider struct {
mu sync.Mutex
defaultMode MockFeishuMode
queuedModes []MockFeishuMode
calls []FeishuSendRequest
}
// NewMockFeishuProvider 创建一个进程内 mock provider。
func NewMockFeishuProvider(defaultMode MockFeishuMode) *MockFeishuProvider {
if defaultMode == "" {
defaultMode = MockFeishuModeSuccess
}
return &MockFeishuProvider{defaultMode: defaultMode}
}
// SetDefaultMode 设置默认返回模式。
func (p *MockFeishuProvider) SetDefaultMode(mode MockFeishuMode) {
p.mu.Lock()
defer p.mu.Unlock()
if mode == "" {
mode = MockFeishuModeSuccess
}
p.defaultMode = mode
}
// PushModes 追加一组“一次性模式”。
//
// 说明:
// 1. 先进先出消费,便于测试“先失败再成功”的重试路径;
// 2. 队列用尽后回退到 defaultMode
// 3. 空模式会被自动忽略,避免测试代码误塞脏数据。
func (p *MockFeishuProvider) PushModes(modes ...MockFeishuMode) {
p.mu.Lock()
defer p.mu.Unlock()
for _, mode := range modes {
if mode == "" {
continue
}
p.queuedModes = append(p.queuedModes, mode)
}
}
// Calls 返回当前 provider 已记录的调用快照。
func (p *MockFeishuProvider) Calls() []FeishuSendRequest {
p.mu.Lock()
defer p.mu.Unlock()
copied := make([]FeishuSendRequest, len(p.calls))
copy(copied, p.calls)
return copied
}
// Send 按预设模式返回模拟结果。
//
// 步骤说明:
// 1. 先记录本次请求,方便测试校验是否发生重复投递;
// 2. 再按 queuedModes -> defaultMode 的顺序决定 outcome
// 3. 最后返回可落库审计的 request/response 摘要。
func (p *MockFeishuProvider) Send(_ context.Context, req FeishuSendRequest) (FeishuSendResult, error) {
p.mu.Lock()
p.calls = append(p.calls, req)
mode := p.defaultMode
if len(p.queuedModes) > 0 {
mode = p.queuedModes[0]
p.queuedModes = p.queuedModes[1:]
}
p.mu.Unlock()
switch mode {
case MockFeishuModeTemporaryFail:
return FeishuSendResult{
Outcome: FeishuSendOutcomeTemporaryFail,
ErrorCode: FeishuErrorCodeProviderTimeout,
ErrorMessage: "mock feishu provider temporary failure",
RequestPayload: map[string]any{
"notification_id": req.NotificationID,
"user_id": req.UserID,
"preview_id": req.PreviewID,
"target_url": req.TargetURL,
},
ResponsePayload: map[string]any{
"mode": string(mode),
"reason": "mock temporary failure",
},
}, nil
case MockFeishuModePermanentFail:
return FeishuSendResult{
Outcome: FeishuSendOutcomePermanentFail,
ErrorCode: FeishuErrorCodePayloadInvalid,
ErrorMessage: "mock feishu provider permanent failure",
RequestPayload: map[string]any{
"notification_id": req.NotificationID,
"user_id": req.UserID,
"preview_id": req.PreviewID,
"target_url": req.TargetURL,
},
ResponsePayload: map[string]any{
"mode": string(mode),
"reason": "mock permanent failure",
},
}, nil
default:
return FeishuSendResult{
Outcome: FeishuSendOutcomeSuccess,
ProviderMessageID: fmt.Sprintf("mock_feishu_%d", time.Now().UnixNano()),
RequestPayload: map[string]any{
"notification_id": req.NotificationID,
"user_id": req.UserID,
"preview_id": req.PreviewID,
"target_url": req.TargetURL,
},
ResponsePayload: map[string]any{
"mode": string(MockFeishuModeSuccess),
"status": "ok",
},
}, nil
}
}