Files
smartmate/backend/shared/events/notification.go
Losita a3eaa9b2c2 Version: 0.9.61.dev.260501
后端:
1. 主动调度 graph + session bridge 收口——把 dry-run / select / preview / confirm / rerun 串成受限 graph,新增 active_schedule_sessions 缓存与聊天拦截,ready_preview 后释放回自由聊天
2. 会话与通知链路对齐——notification 统一绑定 conversation_id,action_url 指向 /assistant/{conversation_id},会话不存在改回 404 语义,避免 wrong param type 误导排障
3. estimated_sections 写入与主动调度消费链路补齐——任务创建、quick task 与随口记入口都透传估计节数,主动调度只消费落库值

前端:
4. AssistantPanel 最小适配主动调度预览与失败态——复用主动调度卡片/微调弹窗,补历史加载失败可见提示与跨账号会话拦截

文档:
5. 更新主动调度缺口分阶段实施计划和实现方案,标记阶段 0-2 收口并同步接力状态
2026-05-01 20:48:32 +08:00

83 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 events
import (
"errors"
"strconv"
"strings"
"time"
)
const (
NotificationFeishuRequestedEventType = "notification.feishu.requested"
NotificationFeishuRequestedEventVersion = "1"
)
// FeishuNotificationRequestedPayload 是飞书通知请求事件载荷。
//
// 职责边界:
// 1. 只描述“需要尝试发送一条飞书提醒”的跨模块协议;
// 2. 不包含 provider SDK 参数,也不复用 notification_records 的 GORM model
// 3. 不决定是否真正投递,去重、配置关闭和重试由 notification 模块处理。
type FeishuNotificationRequestedPayload struct {
NotificationID int64 `json:"notification_id,omitempty"`
UserID int `json:"user_id"`
TriggerID string `json:"trigger_id"`
PreviewID string `json:"preview_id"`
TriggerType string `json:"trigger_type"`
TargetType string `json:"target_type"`
TargetID int `json:"target_id"`
DedupeKey string `json:"dedupe_key"`
TargetURL string `json:"target_url"`
SummaryText string `json:"summary_text,omitempty"`
FallbackText string `json:"fallback_text,omitempty"`
TraceID string `json:"trace_id,omitempty"`
RequestedAt time.Time `json:"requested_at"`
}
// Validate 校验飞书通知事件的协议级必填字段。
//
// 职责边界:
// 1. 只保证通知 handler 能定位用户、预览和去重键;
// 2. 不校验用户是否绑定飞书,也不调用 provider
// 3. target_url 必须是站内相对路径,避免事件载荷携带任意外部跳转。
func (p FeishuNotificationRequestedPayload) Validate() error {
if p.UserID <= 0 {
return errors.New("user_id 必须大于 0")
}
if strings.TrimSpace(p.PreviewID) == "" {
return errors.New("preview_id 不能为空")
}
if strings.TrimSpace(p.DedupeKey) == "" {
return errors.New("dedupe_key 不能为空")
}
targetURL := strings.TrimSpace(p.TargetURL)
if targetURL == "" {
return errors.New("target_url 不能为空")
}
if !strings.HasPrefix(targetURL, "/assistant/") {
return errors.New("target_url 必须是 /assistant/{conversation_id} 站内相对路径")
}
if strings.Contains(targetURL, "://") || strings.HasPrefix(targetURL, "//") {
return errors.New("target_url 不允许携带外部链接")
}
if p.RequestedAt.IsZero() {
return errors.New("requested_at 不能为空")
}
return nil
}
// MessageKey 返回 outbox/Kafka 消息键。
func (p FeishuNotificationRequestedPayload) MessageKey() string {
if p.UserID <= 0 {
return ""
}
return strconv.Itoa(p.UserID)
}
// AggregateID 返回事件聚合 ID。
//
// 说明notification.feishu.requested 使用 preview_id 串联通知与预览。
func (p FeishuNotificationRequestedPayload) AggregateID() string {
return strings.TrimSpace(p.PreviewID)
}