Files
smartmate/backend/service/events/notification_feishu.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

106 lines
3.7 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 (
"context"
"encoding/json"
"errors"
"log"
"strings"
kafkabus "github.com/LoveLosita/smartflow/backend/infra/kafka"
outboxinfra "github.com/LoveLosita/smartflow/backend/infra/outbox"
"github.com/LoveLosita/smartflow/backend/notification"
sharedevents "github.com/LoveLosita/smartflow/backend/shared/events"
)
// RegisterFeishuNotificationHandler 注册 `notification.feishu.requested` 消费 handler。
//
// 职责边界:
// 1. 只负责事件解析、协议校验、调用 NotificationService 和推进 outbox consumed
// 2. 不承担 notification_records 状态机细节,状态流转全部下沉到 notification 模块;
// 3. 不在 handler 内部创建 provider/service避免事件消费与 retry loop 使用两套不同配置。
func RegisterFeishuNotificationHandler(
bus *outboxinfra.EventBus,
outboxRepo *outboxinfra.Repository,
svc *notification.NotificationService,
) error {
if bus == nil {
return errors.New("event bus is nil")
}
if outboxRepo == nil {
return errors.New("outbox repository is nil")
}
if svc == nil {
return errors.New("notification service is nil")
}
handler := func(ctx context.Context, envelope kafkabus.Envelope) error {
// 1. 先校验 event_version避免未来协议破坏性升级后旧 handler 误吃新消息。
// 2. 当前阶段只接受 v1版本不匹配属于不可恢复协议错误直接标记 dead。
eventVersion := strings.TrimSpace(envelope.EventVersion)
if eventVersion != "" && eventVersion != sharedevents.NotificationFeishuRequestedEventVersion {
_ = outboxRepo.MarkDead(ctx, envelope.OutboxID, "notification.feishu.requested event_version 不匹配: "+eventVersion)
return nil
}
var payload sharedevents.FeishuNotificationRequestedPayload
if unmarshalErr := json.Unmarshal(envelope.Payload, &payload); unmarshalErr != nil {
_ = outboxRepo.MarkDead(ctx, envelope.OutboxID, "解析 notification.feishu.requested 载荷失败: "+unmarshalErr.Error())
return nil
}
if validateErr := payload.Validate(); validateErr != nil {
_ = outboxRepo.MarkDead(ctx, envelope.OutboxID, "notification.feishu.requested 载荷非法: "+validateErr.Error())
return nil
}
result, handleErr := svc.HandleFeishuRequested(ctx, payload)
if handleErr != nil {
return handleErr
}
if consumeErr := outboxRepo.ConsumeAndMarkConsumed(ctx, envelope.OutboxID, nil); consumeErr != nil {
return consumeErr
}
log.Printf(
"notification.feishu.requested 消费完成: outbox_id=%d notification_id=%d status=%s delivered=%t reused=%t attempt_count=%d",
envelope.OutboxID,
result.RecordID,
result.Status,
result.Delivered,
result.Reused,
result.AttemptCount,
)
return nil
}
return bus.RegisterEventHandler(sharedevents.NotificationFeishuRequestedEventType, handler)
}
// PublishFeishuNotificationRequested 发布 `notification.feishu.requested` 事件。
//
// 职责边界:
// 1. 只负责把 shared/events payload 投递到 outbox
// 2. 不等待 provider 结果,也不提前创建 notification_records
// 3. 供主动调度 preview 阶段后续切入通知时直接复用。
func PublishFeishuNotificationRequested(
ctx context.Context,
publisher outboxinfra.EventPublisher,
payload sharedevents.FeishuNotificationRequestedPayload,
) error {
if publisher == nil {
return errors.New("event publisher is nil")
}
if err := payload.Validate(); err != nil {
return err
}
return publisher.Publish(ctx, outboxinfra.PublishRequest{
EventType: sharedevents.NotificationFeishuRequestedEventType,
EventVersion: sharedevents.NotificationFeishuRequestedEventVersion,
MessageKey: payload.MessageKey(),
AggregateID: payload.AggregateID(),
Payload: payload,
})
}