Version: 0.6.1.dev.260316

♻️ refactor(outbox): 抽离通用事件总线,并完成 event_type-only 收口

-  新增 `infra` 层通用 `EventBus` / `EventContract`,统一事件发布与消费协议
- 🔄 将聊天持久化链路调整为通过 `service/events` 注册 handler 并发布事件,进一步解耦业务逻辑与异步处理流程
- 🧹 移除 `chat_history_async` 旧适配实现,以及基于 `biz_type` 的兼容分发逻辑
- 📝 更新 Outbox 异步持久化决策记录,明确保留方案 A,并正式启用方案 B
- 📚 同步更新 README 中关于 Outbox + Kafka 可靠异步链路的说明
- 🚚 当前 `outbox + kafka` 已与项目业务链路完全解耦,沉淀为通用、可靠性更强的消息队列能力;后续将参考消息队列的典型使用方式,逐步扩展到更多业务场景
-  补充跨不同分类事务管理器中的 `agent dao` 注册与接入支持
This commit is contained in:
Losita
2026-03-16 13:00:26 +08:00
parent 712bcd3605
commit 626fc700d2
17 changed files with 782 additions and 422 deletions

View File

@@ -0,0 +1,87 @@
package outbox
import (
"context"
"errors"
kafkabus "github.com/LoveLosita/smartflow/backend/infra/kafka"
)
// EventPublisher 是通用事件发布能力接口。
//
// 职责边界:
// 1. 只暴露“发布事件”这一件事,隐藏底层 outbox/kafka 实现细节;
// 2. 业务层只依赖该接口,避免直接耦合具体引擎结构体;
// 3. 该接口不承诺“立即消费成功”,只承诺“事件已入队或返回错误”。
type EventPublisher interface {
Publish(ctx context.Context, req PublishRequest) error
}
// EventBus 是 outbox 异步总线的门面对象。
//
// 设计目的:
// 1. 对外提供“发布 + 注册处理器 + 启停”三类最小能力;
// 2. 对内复用 Engine不重复实现状态机和调度逻辑
// 3. 为后续引入更多事件类型提供统一扩展点。
type EventBus struct {
engine *Engine
}
// NewEventBus 创建通用事件总线。
//
// 说明:
// 1. 当 kafka.enabled=false 时返回 nil调用方可直接降级为同步模式
// 2. 该方法只创建基础设施对象,不自动注册任何业务事件处理器;
// 3. 业务事件处理器注册应由上层在启动阶段显式完成,避免隐式副作用。
func NewEventBus(repo *Repository, cfg kafkabus.Config) (*EventBus, error) {
engine, err := NewEngine(repo, cfg)
if err != nil {
return nil, err
}
if engine == nil {
return nil, nil
}
return &EventBus{engine: engine}, nil
}
// RegisterEventHandler 注册事件处理器。
//
// 失败语义:
// 1. bus 未初始化时直接返回错误;
// 2. event_type 为空或 handler 为空时返回错误;
// 3. 重复注册时采用“后者覆盖前者”并打日志(由 Engine 负责)。
func (b *EventBus) RegisterEventHandler(eventType string, handler MessageHandler) error {
if b == nil || b.engine == nil {
return errors.New("event bus is not initialized")
}
return b.engine.RegisterEventHandler(eventType, handler)
}
// Publish 发布事件到 outbox 队列。
//
// 关键语义:
// 1. 返回 nil 仅表示“已写入 outbox 成功”;
// 2. 真正 Kafka 投递与业务消费由后台异步循环完成;
// 3. 若返回 error表示本次入队失败调用方应按业务策略决定是否重试/降级。
func (b *EventBus) Publish(ctx context.Context, req PublishRequest) error {
if b == nil || b.engine == nil {
return errors.New("event bus is not initialized")
}
return b.engine.Publish(ctx, req)
}
// Start 启动事件总线后台循环dispatch + consume
func (b *EventBus) Start(ctx context.Context) {
if b == nil || b.engine == nil {
return
}
b.engine.Start(ctx)
}
// Close 关闭事件总线资源producer/consumer
func (b *EventBus) Close() {
if b == nil || b.engine == nil {
return
}
b.engine.Close()
}