Files
smartmate/backend/service/events/notification_feishu.go
Losita a6c1e5d077 Version: 0.9.64.dev.260503
后端:
1. 服务级 outbox 基础设施全量落地——新增 service route / service catalog / route registry,重构 outbox engine、repository、event bus 和 model,按 `event_type -> service -> table/topic/group` 统一写入与投递,保留 `agent` 兼容壳但不再依赖共享 outbox
2. Kafka 投递、消费与启动装配同步切换——更新 kafka config、consumer、envelope,接入服务级 topic 与 consumer group,并同步调整 mysql 初始化、start/main/router 装配,保证各服务 relay / consumer 独立装配
3. 业务事件处理器按服务归属重接新 bus——`active-scheduler` 触发链路,以及 `agent` / `memory` / `notification` / `task` 相关 outbox handler 统一切到新路由注册与服务目录,避免新流量回流共享表
4. 同步更新《微服务四步迁移与第二阶段并行开发计划》,把阶段 1 改成当前基线并补齐结构图、阶段快照、风险回退和多代理执行口径
2026-05-03 20:29:00 +08:00

110 lines
3.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 (
"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 OutboxBus,
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")
}
eventOutboxRepo, err := scopedOutboxRepoForEvent(outboxRepo, sharedevents.NotificationFeishuRequestedEventType)
if err != nil {
return err
}
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 {
_ = eventOutboxRepo.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 {
_ = eventOutboxRepo.MarkDead(ctx, envelope.OutboxID, "解析 notification.feishu.requested 载荷失败: "+unmarshalErr.Error())
return nil
}
if validateErr := payload.Validate(); validateErr != nil {
_ = eventOutboxRepo.MarkDead(ctx, envelope.OutboxID, "notification.feishu.requested 载荷非法: "+validateErr.Error())
return nil
}
result, handleErr := svc.HandleFeishuRequested(ctx, payload)
if handleErr != nil {
return handleErr
}
if consumeErr := eventOutboxRepo.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,
})
}